diff --git a/.editorconfig b/.editorconfig index 0a49eadc0bd..1966f917635 100644 --- a/.editorconfig +++ b/.editorconfig @@ -9,25 +9,6 @@ insert_final_newline=true # it's automatically set to 100 on `ktlint --android ...` (per Android Kotlin Style Guide) max_line_length=off -# Comma-separated list of rules to disable (Since 0.34.0) -# Note that rules in any ruleset other than the standard ruleset will need to be prefixed -# by the ruleset identifier. -disabled_rules=no-multi-spaces,colon-spacing,chain-wrapping,import-ordering,experimental:annotation - -# The following (so far identified) rules are kept: -# no-blank-line-before-rbrace -# final-newline -# no-consecutive-blank-lines -# comment-spacing -# filename -# comma-spacing -# paren-spacing -# op-spacing -# string-template -# no-unused-imports -# curly-spacing -# no-semi -# no-empty-class-body -# experimental:multiline-if-else -# experimental:no-empty-first-line-in-method-block -# no-wildcard-imports +# From https://github.com/pinterest/ktlint#custom-ktlint-specific-editorconfig-properties +# default IntelliJ IDEA style, same as alphabetical, but with "java", "javax", "kotlin" and alias imports in the end of the imports list +ij_kotlin_imports_layout=*,java.**,javax.**,kotlin.**,^ diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index dcb9f0a7662..38885e9cc71 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -23,12 +23,12 @@ body: - type: textarea id: result attributes: - label: What happened? + label: Intended result and actual result placeholder: Tell us what went wrong value: | - ### What did you expect? + #### What did you expect? - ### What happened? + #### What happened instead? validations: required: true - type: input diff --git a/.github/ISSUE_TEMPLATE/release.yml b/.github/ISSUE_TEMPLATE/release.yml index 51fe7a7bd14..903c05c5d36 100644 --- a/.github/ISSUE_TEMPLATE/release.yml +++ b/.github/ISSUE_TEMPLATE/release.yml @@ -1,7 +1,7 @@ name: Release checklist description: Checklist for each release. This template is only for the core team. title: "[Release] Element Android v" -labels: [\U0001F680 Release] +labels: [🚀 Release] assignees: - bmarty @@ -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. @@ -70,11 +71,27 @@ body: https://github.com/matrix-org/matrix-android-sdk2 - [ ] Create a release with GitFlow + - [ ] Update the value of VERSION_NAME in the file gradle.properties - [ ] 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 + - [ ] Check the diff in the file `./matrix-sdk-android/build.gradle` and restore what may have been erased (in particular the line `apply plugin: "com.vanniktech.maven.publish"` and the line about the version) + - [ ] Let the script finish to build the library - [ ] Update the file `CHANGES.md` - [ ] Finish the release using GitFlow + - [ ] Push the branch `main`, the new tag and the branch `develop` to origin + + ##### 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: diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 8fbc5602fee..2048b823f06 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,10 +1,16 @@ ### Pull Request Checklist - + - [ ] Changes has been tested on an Android device or Android emulator with API 21 - [ ] UI change has been tested on both light and dark themes +- [ ] Accessibility has been taken into account. See https://github.com/vector-im/element-android/blob/develop/CONTRIBUTING.md#accessibility - [ ] Pull request is based on the develop branch - [ ] Pull request includes a new file under ./changelog.d. See https://github.com/vector-im/element-android/blob/develop/CONTRIBUTING.md#changelog - [ ] Pull request includes screenshots or videos if containing UI changes - [ ] Pull request includes a [sign off](https://github.com/matrix-org/synapse/blob/master/CONTRIBUTING.md#sign-off) +- [ ] You've made a self review of your PR +- [ ] If you have modified the screen flow, or added new screens to the application, you have updated the test [UiAllScreensSanityTest.allScreensTest()](https://github.com/vector-im/element-android/blob/main/vector/src/androidTest/java/im/vector/app/ui/UiAllScreensSanityTest.kt#L73) \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 85148a2632a..91dc6d830b5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -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: diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 984ae0748e1..c18ca69fdee 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -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 diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 0f119152581..8c56edfa3a5 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -21,8 +21,12 @@ jobs: - uses: actions/checkout@v2 - name: Run klint run: | - curl -sSLO https://github.com/pinterest/ktlint/releases/download/0.36.0/ktlint && chmod a+x ktlint - ./ktlint --android --experimental -v + ./gradlew ktlintCheck --continue + - name: Upload reports + uses: actions/upload-artifact@v2 + with: + name: ktlinting-report + path: vector/build/reports/ktlint/*.* # Lint for main module and all the other modules android-lint: diff --git a/.github/workflows/sanity_test.yml b/.github/workflows/sanity_test.yml index 632dee8a582..3ab0017ce2e 100644 --- a/.github/workflows/sanity_test.yml +++ b/.github/workflows/sanity_test.yml @@ -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 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 6e51368ce5d..50195638de1 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -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 diff --git a/.gitignore b/.gitignore index 04d1b6fe06e..ff086d77234 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ .idea/*.xml .DS_Store /build +/benchmark-out /captures .externalNativeBuild @@ -15,4 +16,4 @@ /fastlane/private /fastlane/report.xml -ktlint +/library/build diff --git a/CHANGES.md b/CHANGES.md index 640d56a9fda..8d4899e6fb5 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,113 @@ +Changes in Element v1.3.1 (2021-09-29) +====================================== + +Bugfixes 🐛 +---------- + - Verifying exported E2E keys to provide user feedback when the output is malformed ([#4082](https://github.com/vector-im/element-android/issues/4082)) + - Fix settings crash when accelerometer not available ([#4103](https://github.com/vector-im/element-android/issues/4103)) + - Crash while rendering failed message warning ([#4110](https://github.com/vector-im/element-android/issues/4110)) + + +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) +====================================== + +Features ✨ +---------- + - Support Android 11 Conversation features ([#1809](https://github.com/vector-im/element-android/issues/1809)) + - Introduces AutoAcceptInvites which can be enabled at compile time. ([#3531](https://github.com/vector-im/element-android/issues/3531)) + - New call designs ([#3599](https://github.com/vector-im/element-android/issues/3599)) + - Restricted Join Rule | Inform admins of new option ([#3631](https://github.com/vector-im/element-android/issues/3631)) + - Mention and Keyword Notification Settings: Turn on/off keyword notifications and edit keywords. ([#3650](https://github.com/vector-im/element-android/issues/3650)) + - Support accept 3pid invite when email is not bound to account ([#3691](https://github.com/vector-im/element-android/issues/3691)) + - Space summary pagination ([#3693](https://github.com/vector-im/element-android/issues/3693)) + - Update Email invite to be aware of spaces ([#3695](https://github.com/vector-im/element-android/issues/3695)) + - M11.12 Spaces | Default to 'Home' in settings ([#3754](https://github.com/vector-im/element-android/issues/3754)) + - Call: show dialog for some ended reasons. ([#3853](https://github.com/vector-im/element-android/issues/3853)) + - Add expired account error code in the matrix SDK ([#3900](https://github.com/vector-im/element-android/issues/3900)) + - Add password errors in the matrix SDK ([#3927](https://github.com/vector-im/element-android/issues/3927)) + - Upgrade AGP to 7.0.2. + When compiling using command line, make sure to use the JDK 11 by adding for instance `-Dorg.gradle.java.home=/Applications/Android\ Studio\ Preview.app/Contents/jre/Contents/Home` or by setting JAVA_HOME. ([#3954](https://github.com/vector-im/element-android/issues/3954)) + - Check power level before displaying actions in the room details' timeline ([#3959](https://github.com/vector-im/element-android/issues/3959)) + +Bugfixes 🐛 +---------- + - Add mxid to autocomplete suggestion if more than one user in a room has the same displayname ([#1823](https://github.com/vector-im/element-android/issues/1823)) + - Use WebView cache for widgets to avoid excessive data use ([#2648](https://github.com/vector-im/element-android/issues/2648)) + - Jitsi-hosted jitsi conferences not loading ([#2846](https://github.com/vector-im/element-android/issues/2846)) + - Space Explore Rooms no feedback on failed to join ([#3207](https://github.com/vector-im/element-android/issues/3207)) + - Notifications - Fix missing sound on notifications. ([#3243](https://github.com/vector-im/element-android/issues/3243)) + - the element-based domain permalinks (e.g. https://app.element.io/#/user/@chagai95:matrix.org) don't have the mxid in the first param (like matrix.to does - https://matrix.to/#/@chagai95:matrix.org) but rather in the second after /user/ so /user/mxid ([#3735](https://github.com/vector-im/element-android/issues/3735)) + - Update the AccountData with the users' matrix Id instead of their email for those invited by email in a direct chat ([#3743](https://github.com/vector-im/element-android/issues/3743)) + - Send an empty body for POST rooms/{roomId}/receipt/{receiptType}/{eventId} ([#3789](https://github.com/vector-im/element-android/issues/3789)) + - Fix order in which the items of the attachment menu appear ([#3793](https://github.com/vector-im/element-android/issues/3793)) + - Authenticated Jitsi not working in release ([#3841](https://github.com/vector-im/element-android/issues/3841)) + - Home: Dial pad lost entry when config changes ([#3845](https://github.com/vector-im/element-android/issues/3845)) + - Message edition is not rendered in e2e rooms after pagination ([#3887](https://github.com/vector-im/element-android/issues/3887)) + - Crash on opening a room on Android 5.0 and 5.1 - Regression with Voice message ([#3897](https://github.com/vector-im/element-android/issues/3897)) + - Fix a crash at start-up if translated string is empty ([#3910](https://github.com/vector-im/element-android/issues/3910)) + - PushRule enabling request is not following the spec ([#3911](https://github.com/vector-im/element-android/issues/3911)) + - Enable image preview in Android's share sheet (Android 11+) ([#3965](https://github.com/vector-im/element-android/issues/3965)) + - Voice Message - Cannot render voice message if the waveform data is corrupted ([#3983](https://github.com/vector-im/element-android/issues/3983)) + - Fix memory leak on RoomDetailFragment (ValueAnimator) ([#3990](https://github.com/vector-im/element-android/issues/3990)) + +Other changes +------------- + - VoIP: Merge virtual room timeline in corresponding native room (call events only). ([#3520](https://github.com/vector-im/element-android/issues/3520)) + - Issue templates: modernise and sync with element-web ([#3883](https://github.com/vector-im/element-android/issues/3883)) + - Issue templates: modernise SDK and release checklists, and add homeserver question for bugs ([#3889](https://github.com/vector-im/element-android/issues/3889)) + - Issue templates: merge expected and actual results ([#3960](https://github.com/vector-im/element-android/issues/3960)) + + Changes in Element v1.2.0 (2021-08-12) ====================================== diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 610a6227b78..dbc0ce9b725 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,7 +9,7 @@ Android support can be found in this [![Element Android Matrix room #element-and ## Android Studio settings Please set the "hard wrap" setting of Android Studio to 160 chars, this is the setting we use internally to format the source code (Menu `Settings/Editor/Code Style` then `Hard wrap at`). -Please ensure that your using the project formatting rules (which are in the project at .idea/codeStyles/), and format the file before committing them. +Please ensure that you're using the project formatting rules (which are in the project at .idea/codeStyles/), and format the file before committing them. ### Template @@ -80,14 +80,13 @@ Make sure the following commands execute without any error: #### ktlint
-curl -sSLO https://github.com/pinterest/ktlint/releases/download/0.34.2/ktlint && chmod a+x ktlint
-./ktlint --android --experimental -v
+./gradlew ktlintCheck --continue
 
Note that you can run
-./ktlint --android --experimental -v -F
+./gradlew ktlintFormat
 
For ktlint to fix some detected errors for you (you still have to check and commit the fix of course) @@ -148,6 +147,8 @@ The string will be removed during the next sync with Weblate. Please consider accessibility as an important point. As a minimum requirement, in layout XML files please use attributes such as `android:contentDescription` and `android:importantForAccessibility`, and test with a screen reader if it's working well. You can add new string resources, dedicated to accessibility, in this case, please prefix theirs id with `a11y_`. +For instance, when updating the image `src` of an ImageView, please also consider updating its `contentDescription`. A good example is a play pause button. + ### Layout When adding or editing layouts, make sure the layout will render correctly if device uses a RTL (Right To Left) language. diff --git a/attachment-viewer/build.gradle b/attachment-viewer/build.gradle index d62062ae143..064f497dc75 100644 --- a/attachment-viewer/build.gradle +++ b/attachment-viewer/build.gradle @@ -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 } \ No newline at end of file diff --git a/attachment-viewer/src/main/java/im/vector/lib/attachmentviewer/AttachmentViewerActivity.kt b/attachment-viewer/src/main/java/im/vector/lib/attachmentviewer/AttachmentViewerActivity.kt index f909418d6fe..4ca6ced8fe5 100644 --- a/attachment-viewer/src/main/java/im/vector/lib/attachmentviewer/AttachmentViewerActivity.kt +++ b/attachment-viewer/src/main/java/im/vector/lib/attachmentviewer/AttachmentViewerActivity.kt @@ -39,7 +39,6 @@ import androidx.core.view.updatePadding import androidx.transition.TransitionManager import androidx.viewpager2.widget.ViewPager2 import im.vector.lib.attachmentviewer.databinding.ActivityAttachmentViewerBinding - import java.lang.ref.WeakReference import kotlin.math.abs @@ -291,8 +290,8 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi private fun calculateTranslationAlpha(translationY: Float, translationLimit: Int): Float = 1.0f - 1.0f / translationLimit.toFloat() / 4f * abs(translationY) - private fun createSwipeToDismissHandler() - : SwipeToDismissHandler = SwipeToDismissHandler( + private fun createSwipeToDismissHandler(): SwipeToDismissHandler = + SwipeToDismissHandler( swipeView = views.dismissContainer, shouldAnimateDismiss = { shouldAnimateDismiss() }, onDismiss = { animateClose() }, diff --git a/attachment-viewer/src/main/java/im/vector/lib/attachmentviewer/ImageLoaderTarget.kt b/attachment-viewer/src/main/java/im/vector/lib/attachmentviewer/ImageLoaderTarget.kt index 531e8171e15..99686eaabb2 100644 --- a/attachment-viewer/src/main/java/im/vector/lib/attachmentviewer/ImageLoaderTarget.kt +++ b/attachment-viewer/src/main/java/im/vector/lib/attachmentviewer/ImageLoaderTarget.kt @@ -36,8 +36,8 @@ interface ImageLoaderTarget { fun onResourceReady(uid: String, resource: Drawable) } -internal class DefaultImageLoaderTarget(val holder: AnimatedImageViewHolder, private val contextView: ImageView) - : ImageLoaderTarget { +internal class DefaultImageLoaderTarget(val holder: AnimatedImageViewHolder, private val contextView: ImageView) : + ImageLoaderTarget { override fun contextView(): ImageView { return contextView } diff --git a/build.gradle b/build.gradle index ef26530298f..93f3e17f34c 100644 --- a/build.gradle +++ b/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" @@ -26,7 +27,14 @@ buildscript { } } +// ktlint Plugin +plugins { + id "org.jlleitschuh.gradle.ktlint" version "10.2.0" +} + allprojects { + apply plugin: "org.jlleitschuh.gradle.ktlint" + repositories { // For olm library. This has to be declared first, to ensure that Olm library is not downloaded from another repo maven { @@ -74,6 +82,26 @@ allprojects { // You can override by passing `-PallWarningsAsErrors=false` in the command line kotlinOptions.allWarningsAsErrors = project.getProperties().getOrDefault("allWarningsAsErrors", "true").toBoolean() } + + // Fix "Java heap space" issue + tasks.withType(org.jlleitschuh.gradle.ktlint.tasks.BaseKtLintCheckTask).configureEach { + it.workerMaxHeapSize.set("2G") + } + + // See https://github.com/JLLeitschuh/ktlint-gradle#configuration + ktlint { + android = true + ignoreFailures = false + enableExperimentalRules = true + // display the corresponding rule + verbose = true + disabledRules = [ + "spacing-between-declarations-with-comments", + "no-multi-spaces", + "experimental:spacing-between-declarations-with-annotations", + "experimental:annotation" + ] + } } task clean(type: Delete) { diff --git a/changelog.d/1673.bugfix b/changelog.d/1673.bugfix new file mode 100644 index 00000000000..b0459f34b85 --- /dev/null +++ b/changelog.d/1673.bugfix @@ -0,0 +1 @@ +Avoid resending notifications that are already shown diff --git a/changelog.d/1809.feature b/changelog.d/1809.feature deleted file mode 100644 index b037ffa6d31..00000000000 --- a/changelog.d/1809.feature +++ /dev/null @@ -1 +0,0 @@ -Support Android 11 Conversation features \ No newline at end of file diff --git a/changelog.d/1823.bugfix b/changelog.d/1823.bugfix deleted file mode 100644 index 8252e1826f7..00000000000 --- a/changelog.d/1823.bugfix +++ /dev/null @@ -1 +0,0 @@ -- Add mxid to autocomplete suggestion if more than one user in a room has the same displayname diff --git a/changelog.d/240.feature b/changelog.d/240.feature new file mode 100644 index 00000000000..ee4d07a9759 --- /dev/null +++ b/changelog.d/240.feature @@ -0,0 +1 @@ +Android Auto notification support diff --git a/changelog.d/2648.bugfix b/changelog.d/2648.bugfix deleted file mode 100644 index 24853af1bc1..00000000000 --- a/changelog.d/2648.bugfix +++ /dev/null @@ -1 +0,0 @@ -Use WebView cache for widgets to avoid excessive data use \ No newline at end of file diff --git a/changelog.d/2846.bugfix b/changelog.d/2846.bugfix deleted file mode 100644 index 1669ea1cf9f..00000000000 --- a/changelog.d/2846.bugfix +++ /dev/null @@ -1 +0,0 @@ -Jitsi-hosted jitsi conferences not loading \ No newline at end of file diff --git a/changelog.d/3048.bugfix b/changelog.d/3048.bugfix new file mode 100644 index 00000000000..81fbe7b65eb --- /dev/null +++ b/changelog.d/3048.bugfix @@ -0,0 +1 @@ +Room filter no results bad CTA in space mode when a space selected \ No newline at end of file diff --git a/changelog.d/3243.bugfix b/changelog.d/3243.bugfix deleted file mode 100644 index 8c912a5c135..00000000000 --- a/changelog.d/3243.bugfix +++ /dev/null @@ -1 +0,0 @@ -Notifications - Fix missing sound on notifications. \ No newline at end of file diff --git a/changelog.d/3347.bugfix b/changelog.d/3347.bugfix new file mode 100644 index 00000000000..8ba1d7af882 --- /dev/null +++ b/changelog.d/3347.bugfix @@ -0,0 +1 @@ +Fixes notifications not dismissing when reading messages on other devices \ No newline at end of file diff --git a/changelog.d/3599.feature b/changelog.d/3599.feature deleted file mode 100644 index 7f09477f9c7..00000000000 --- a/changelog.d/3599.feature +++ /dev/null @@ -1 +0,0 @@ -New call designs \ No newline at end of file diff --git a/changelog.d/3631.feature b/changelog.d/3631.feature deleted file mode 100644 index 01e1ee28cc6..00000000000 --- a/changelog.d/3631.feature +++ /dev/null @@ -1 +0,0 @@ -Restricted Join Rule | Inform admins of new option \ No newline at end of file diff --git a/changelog.d/3650.feature b/changelog.d/3650.feature deleted file mode 100644 index 4ae065701fd..00000000000 --- a/changelog.d/3650.feature +++ /dev/null @@ -1 +0,0 @@ -Mention and Keyword Notification Settings: Turn on/off keyword notifications and edit keywords. \ No newline at end of file diff --git a/changelog.d/3691.feature b/changelog.d/3691.feature deleted file mode 100644 index ae139681b01..00000000000 --- a/changelog.d/3691.feature +++ /dev/null @@ -1 +0,0 @@ - Support accept 3pid invite when email is not bound to account \ No newline at end of file diff --git a/changelog.d/3693.feature b/changelog.d/3693.feature deleted file mode 100644 index 6351ded5223..00000000000 --- a/changelog.d/3693.feature +++ /dev/null @@ -1 +0,0 @@ -Space summary pagination \ No newline at end of file diff --git a/changelog.d/3695.feature b/changelog.d/3695.feature deleted file mode 100644 index bd5934529d5..00000000000 --- a/changelog.d/3695.feature +++ /dev/null @@ -1 +0,0 @@ -Update Email invite to be aware of spaces \ No newline at end of file diff --git a/changelog.d/3732.feature b/changelog.d/3732.feature new file mode 100644 index 00000000000..3352c30886d --- /dev/null +++ b/changelog.d/3732.feature @@ -0,0 +1 @@ +Add a fallback for user displayName when this one is null or empty \ No newline at end of file diff --git a/changelog.d/3735.bugfix b/changelog.d/3735.bugfix deleted file mode 100644 index 8262af627b5..00000000000 --- a/changelog.d/3735.bugfix +++ /dev/null @@ -1 +0,0 @@ -the element-based domain permalinks (e.g. https://app.element.io/#/user/@chagai95:matrix.org) don't have the mxid in the first param (like matrix.to does - https://matrix.to/#/@chagai95:matrix.org) but rather in the second after /user/ so /user/mxid \ No newline at end of file diff --git a/changelog.d/3743.bugfix b/changelog.d/3743.bugfix deleted file mode 100644 index 87baf628343..00000000000 --- a/changelog.d/3743.bugfix +++ /dev/null @@ -1 +0,0 @@ -Update the AccountData with the users' matrix Id instead of their email for those invited by email in a direct chat \ No newline at end of file diff --git a/changelog.d/3754.feature b/changelog.d/3754.feature deleted file mode 100644 index e997ac370dc..00000000000 --- a/changelog.d/3754.feature +++ /dev/null @@ -1 +0,0 @@ -M11.12 Spaces | Default to 'Home' in settings \ No newline at end of file diff --git a/changelog.d/3789.bugfix b/changelog.d/3789.bugfix deleted file mode 100644 index fc3402e029d..00000000000 --- a/changelog.d/3789.bugfix +++ /dev/null @@ -1 +0,0 @@ -Send an empty body for POST rooms/{roomId}/receipt/{receiptType}/{eventId} \ No newline at end of file diff --git a/changelog.d/3793.bugfix b/changelog.d/3793.bugfix deleted file mode 100644 index 8f812292624..00000000000 --- a/changelog.d/3793.bugfix +++ /dev/null @@ -1 +0,0 @@ -Fix order in which the items of the attachement menu appear diff --git a/changelog.d/3841.bugfix b/changelog.d/3841.bugfix deleted file mode 100644 index d0e84757dc7..00000000000 --- a/changelog.d/3841.bugfix +++ /dev/null @@ -1 +0,0 @@ -Authenticated Jitsi not working in release \ No newline at end of file diff --git a/changelog.d/3845.bugfix b/changelog.d/3845.bugfix deleted file mode 100644 index ce820738fe4..00000000000 --- a/changelog.d/3845.bugfix +++ /dev/null @@ -1 +0,0 @@ -Home: Dial pad lost entry when config changes \ No newline at end of file diff --git a/changelog.d/3853.feature b/changelog.d/3853.feature deleted file mode 100644 index 86c19d2c22b..00000000000 --- a/changelog.d/3853.feature +++ /dev/null @@ -1 +0,0 @@ -Call: show dialog for some ended reasons. \ No newline at end of file diff --git a/changelog.d/3883.misc b/changelog.d/3883.misc deleted file mode 100644 index 468e5e49278..00000000000 --- a/changelog.d/3883.misc +++ /dev/null @@ -1 +0,0 @@ -Issue templates: modernise and sync with element-web diff --git a/changelog.d/3887.bugfix b/changelog.d/3887.bugfix deleted file mode 100644 index eecd2cea19e..00000000000 --- a/changelog.d/3887.bugfix +++ /dev/null @@ -1 +0,0 @@ -Message edition is not rendered in e2e rooms after pagination \ No newline at end of file diff --git a/changelog.d/3897.bugfix b/changelog.d/3897.bugfix deleted file mode 100644 index cad8560ca41..00000000000 --- a/changelog.d/3897.bugfix +++ /dev/null @@ -1 +0,0 @@ -Crash on opening a room on Android 5.0 and 5.1 - Regression with Voice message \ No newline at end of file diff --git a/changelog.d/3898.bugfix b/changelog.d/3898.bugfix new file mode 100644 index 00000000000..49cab4adad3 --- /dev/null +++ b/changelog.d/3898.bugfix @@ -0,0 +1,2 @@ +Fixes the passphrase screen being incorrectly shown when pressing back on the key verification screen. +When the user doesn't have a passphrase set we don't show the passphrase screen. \ No newline at end of file diff --git a/changelog.d/3900.feature b/changelog.d/3900.feature deleted file mode 100644 index 765f51f054d..00000000000 --- a/changelog.d/3900.feature +++ /dev/null @@ -1 +0,0 @@ -Add expired account error code in the matrix SDK \ No newline at end of file diff --git a/changelog.d/3910.bugfix b/changelog.d/3910.bugfix deleted file mode 100644 index 3bca598e457..00000000000 --- a/changelog.d/3910.bugfix +++ /dev/null @@ -1 +0,0 @@ -Fix a crash at start-up if translated string is empty \ No newline at end of file diff --git a/changelog.d/3911.bugfix b/changelog.d/3911.bugfix deleted file mode 100644 index 4cd40bdd7d9..00000000000 --- a/changelog.d/3911.bugfix +++ /dev/null @@ -1 +0,0 @@ -PushRule enabling request is not following the spec \ No newline at end of file diff --git a/changelog.d/3927.feature b/changelog.d/3927.feature deleted file mode 100644 index 86091b6422b..00000000000 --- a/changelog.d/3927.feature +++ /dev/null @@ -1 +0,0 @@ -Add password errors in the matrix SDK \ No newline at end of file diff --git a/changelog.d/3933.bugfix b/changelog.d/3933.bugfix new file mode 100644 index 00000000000..3396bd75e7b --- /dev/null +++ b/changelog.d/3933.bugfix @@ -0,0 +1 @@ +App doesn't take you to a Space after choosing to Join it \ No newline at end of file diff --git a/changelog.d/3934.bugfix b/changelog.d/3934.bugfix new file mode 100644 index 00000000000..989f96d004e --- /dev/null +++ b/changelog.d/3934.bugfix @@ -0,0 +1 @@ +Validate public space addresses and room aliases length \ No newline at end of file diff --git a/changelog.d/3935.bugfix b/changelog.d/3935.bugfix new file mode 100644 index 00000000000..f4b1d309b43 --- /dev/null +++ b/changelog.d/3935.bugfix @@ -0,0 +1 @@ +Save button for adding rooms to a space is hidden when scrolling through list of rooms \ No newline at end of file diff --git a/changelog.d/3954.feature b/changelog.d/3954.feature deleted file mode 100644 index 132277032c2..00000000000 --- a/changelog.d/3954.feature +++ /dev/null @@ -1,2 +0,0 @@ -Upgrade AGP to 7.0.2. -When compiling using command line, make sure to use the JDK 11 by adding for instance `-Dorg.gradle.java.home=/Applications/Android\ Studio\ Preview.app/Contents/jre/Contents/Home` or by setting JAVA_HOME. \ No newline at end of file diff --git a/changelog.d/3957.misc b/changelog.d/3957.misc new file mode 100644 index 00000000000..bc6e417a49f --- /dev/null +++ b/changelog.d/3957.misc @@ -0,0 +1 @@ +Use ktlint plugin. See [the documentation](https://github.com/vector-im/element-android/blob/develop/CONTRIBUTING.md#ktlint) for more detail. \ No newline at end of file diff --git a/changelog.d/3960.misc b/changelog.d/3960.misc deleted file mode 100644 index d37fcc94595..00000000000 --- a/changelog.d/3960.misc +++ /dev/null @@ -1 +0,0 @@ -Issue templates: merge expected and actual results diff --git a/changelog.d/3965.bugfix b/changelog.d/3965.bugfix deleted file mode 100644 index 9d966c2ca34..00000000000 --- a/changelog.d/3965.bugfix +++ /dev/null @@ -1 +0,0 @@ -Enable image preview in Android's share sheet (Android 11+) diff --git a/changelog.d/4018.misc b/changelog.d/4018.misc new file mode 100644 index 00000000000..d0849d58420 --- /dev/null +++ b/changelog.d/4018.misc @@ -0,0 +1 @@ +Minimize the use of exported="true" in android Manifest (link: https://github.com/matrix-org/matrix-dinsic/issues/618) diff --git a/changelog.d/4027.feature b/changelog.d/4027.feature new file mode 100644 index 00000000000..fa45d07ef9e --- /dev/null +++ b/changelog.d/4027.feature @@ -0,0 +1 @@ +Add client base url config to customize permalinks \ No newline at end of file diff --git a/changelog.d/4045.bugfix b/changelog.d/4045.bugfix new file mode 100644 index 00000000000..c6798ae4928 --- /dev/null +++ b/changelog.d/4045.bugfix @@ -0,0 +1 @@ +Align new room encryption default to Web \ No newline at end of file diff --git a/changelog.d/4076.misc b/changelog.d/4076.misc new file mode 100644 index 00000000000..97b50d8c546 --- /dev/null +++ b/changelog.d/4076.misc @@ -0,0 +1 @@ +Fix redundancy in heading in the bug report issue form diff --git a/changelog.d/4077.bugfix b/changelog.d/4077.bugfix new file mode 100644 index 00000000000..a8ab6b3c54d --- /dev/null +++ b/changelog.d/4077.bugfix @@ -0,0 +1 @@ +Fix Reply/Edit mode animation is broken when sending \ No newline at end of file diff --git a/changelog.d/4092.bugfix b/changelog.d/4092.bugfix new file mode 100644 index 00000000000..68ce5180604 --- /dev/null +++ b/changelog.d/4092.bugfix @@ -0,0 +1,4 @@ +Added changes that will make SearchView in search bar focused by default on opening reaction picker. + +When tapping close icon of SearchView, the SearchView did not collapse therefore added the on close listener +which will collapse the SearchView on close. diff --git a/changelog.d/4109.bugfix b/changelog.d/4109.bugfix new file mode 100644 index 00000000000..4f35dd7f557 --- /dev/null +++ b/changelog.d/4109.bugfix @@ -0,0 +1 @@ +Troubleshoot notification: Fix button not clickable \ No newline at end of file diff --git a/changelog.d/4113.misc b/changelog.d/4113.misc new file mode 100644 index 00000000000..a5faa57b92e --- /dev/null +++ b/changelog.d/4113.misc @@ -0,0 +1 @@ +Fix release label in the release issue template diff --git a/changelog.d/4155.bugfix b/changelog.d/4155.bugfix new file mode 100644 index 00000000000..59f5c564aa5 --- /dev/null +++ b/changelog.d/4155.bugfix @@ -0,0 +1 @@ +Harmonize wording in the message bottom sheet and move up the View Reactions item \ No newline at end of file diff --git a/changelog.d/4156.bugfix b/changelog.d/4156.bugfix new file mode 100644 index 00000000000..a010398adc8 --- /dev/null +++ b/changelog.d/4156.bugfix @@ -0,0 +1 @@ +Remove unused SendRelationWorker and related API call (3588) \ No newline at end of file diff --git a/changelog.d/4157.feature b/changelog.d/4157.feature new file mode 100644 index 00000000000..71cbe60b1f9 --- /dev/null +++ b/changelog.d/4157.feature @@ -0,0 +1 @@ +Check if DM exists before creating a new one \ No newline at end of file diff --git a/changelog.d/4158.feature b/changelog.d/4158.feature new file mode 100644 index 00000000000..86d2c760c28 --- /dev/null +++ b/changelog.d/4158.feature @@ -0,0 +1 @@ +Handle 8 new slash commands: `/ignore`, `/unignore`, `/roomname`, `/myroomnick`, `/roomavatar`, `/myroomavatar`, `/lenny`, `/whois`. \ No newline at end of file diff --git a/changelog.d/4158.removal b/changelog.d/4158.removal new file mode 100644 index 00000000000..557a85262e9 --- /dev/null +++ b/changelog.d/4158.removal @@ -0,0 +1 @@ +Create extension `String.isMxcUrl()` \ No newline at end of file diff --git a/changelog.d/983.bugfix b/changelog.d/983.bugfix new file mode 100644 index 00000000000..7318f7f4cd2 --- /dev/null +++ b/changelog.d/983.bugfix @@ -0,0 +1 @@ +Ensure initial sync progress dialog is hidden when the initial sync is over \ No newline at end of file diff --git a/changelog.d/pr-3889.misc b/changelog.d/pr-3889.misc deleted file mode 100644 index 361bd6419bc..00000000000 --- a/changelog.d/pr-3889.misc +++ /dev/null @@ -1 +0,0 @@ -Issue templates: modernise SDK and release checklists, and add homeserver question for bugs diff --git a/dependencies.gradle b/dependencies.gradle new file mode 100644 index 00000000000..92358952db5 --- /dev/null +++ b/dependencies.gradle @@ -0,0 +1,135 @@ +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.31" +def kotlinCoroutines = "1.5.2" +def dagger = "2.39.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" +def vanniktechEmoji = "0.8.0" + +// 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", + '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.1", + 'work' : "androidx.work:work-runtime-ktx:2.6.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", + '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" + ], + vanniktech: [ + 'emojiMaterial' : "com.vanniktech:emoji-material:$vanniktechEmoji", + 'emojiGoogle' : "com.vanniktech:emoji-google:$vanniktechEmoji" + ], + tests : [ + 'kluent' : "org.amshove.kluent:kluent-android:1.68", + 'timberJunitRule' : "net.lachlanmckee:timber-junit-rule:1.0.1", + 'junit' : "junit:junit:4.13.2" + ] +] \ No newline at end of file diff --git a/fastlane/README.md b/fastlane/README.md index 54d3a005a6d..dc33f422d66 100644 --- a/fastlane/README.md +++ b/fastlane/README.md @@ -44,6 +44,6 @@ Get version code ---- -This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run. +This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run. More information about fastlane can be found on [fastlane.tools](https://fastlane.tools). The documentation of fastlane can be found on [docs.fastlane.tools](https://docs.fastlane.tools). diff --git a/fastlane/metadata/android/cs-CZ/changelogs/40102000.txt b/fastlane/metadata/android/cs-CZ/changelogs/40102000.txt new file mode 100644 index 00000000000..929281c3888 --- /dev/null +++ b/fastlane/metadata/android/cs-CZ/changelogs/40102000.txt @@ -0,0 +1,2 @@ +Hlavní změny v této verzi: Hlasové zprávy jsou povoleny ve výchozím nastavení. +Úplný seznam změn: https://github.com/vector-im/element-android/releases/tag/v1.2.0 diff --git a/fastlane/metadata/android/cs-CZ/changelogs/40102010.txt b/fastlane/metadata/android/cs-CZ/changelogs/40102010.txt new file mode 100644 index 00000000000..ca75c6b5d82 --- /dev/null +++ b/fastlane/metadata/android/cs-CZ/changelogs/40102010.txt @@ -0,0 +1,2 @@ +Hlavní změny v této verzi: Mnohá vylepšení VoIP a prostorů (stále v beta verzi). +Úplný seznam změn: https://github.com/vector-im/element-android/releases/tag/v1.2.1 diff --git a/fastlane/metadata/android/de-DE/changelogs/40102000.txt b/fastlane/metadata/android/de-DE/changelogs/40102000.txt new file mode 100644 index 00000000000..cfa9f725f2f --- /dev/null +++ b/fastlane/metadata/android/de-DE/changelogs/40102000.txt @@ -0,0 +1,2 @@ +Hauptänderungen: Sprachnachrichten standardmäßig aktiviert. +Ganze Änderungsliste: https://github.com/vector-im/element-android/releases/tag/v1.2.0 diff --git a/fastlane/metadata/android/de-DE/changelogs/40102010.txt b/fastlane/metadata/android/de-DE/changelogs/40102010.txt new file mode 100644 index 00000000000..2635704a81a --- /dev/null +++ b/fastlane/metadata/android/de-DE/changelogs/40102010.txt @@ -0,0 +1,2 @@ +VoIP und Spaces verbessert +Vollständige Änderungsliste: https://github.com/vector-im/element-android/releases/tag/v1.2.1 diff --git a/fastlane/metadata/android/en-US/changelogs/40102000.txt b/fastlane/metadata/android/en-US/changelogs/40102000.txt index 46d9b078099..4fc02966cde 100644 --- a/fastlane/metadata/android/en-US/changelogs/40102000.txt +++ b/fastlane/metadata/android/en-US/changelogs/40102000.txt @@ -1,2 +1,2 @@ Main changes in this version: Voice Message is enabled by default. -Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.1.16 \ No newline at end of file +Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.2.0 \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/40102010.txt b/fastlane/metadata/android/en-US/changelogs/40102010.txt new file mode 100644 index 00000000000..799b7dc8b61 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/40102010.txt @@ -0,0 +1,2 @@ +Main changes in this version: Many improvements on VoIP and Spaces (still in beta). +Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.2.1 \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/40103000.txt b/fastlane/metadata/android/en-US/changelogs/40103000.txt new file mode 100644 index 00000000000..d4ef2f75a0c --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/40103000.txt @@ -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 \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/40103010.txt b/fastlane/metadata/android/en-US/changelogs/40103010.txt new file mode 100644 index 00000000000..e3760f18822 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/40103010.txt @@ -0,0 +1,2 @@ +Main changes in this version: Organize your rooms using Spaces! v1.3.1 is fixing a crash which can occurs in v1.3.0. +Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.3.1 \ No newline at end of file diff --git a/fastlane/metadata/android/et/changelogs/40102000.txt b/fastlane/metadata/android/et/changelogs/40102000.txt new file mode 100644 index 00000000000..57f28039c58 --- /dev/null +++ b/fastlane/metadata/android/et/changelogs/40102000.txt @@ -0,0 +1,2 @@ +Põhilised muutused selles versioonis: häälsõnumid on nüüd vaikimisi kasutusel. +Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases/tag/v1.2.0 diff --git a/fastlane/metadata/android/et/changelogs/40102010.txt b/fastlane/metadata/android/et/changelogs/40102010.txt new file mode 100644 index 00000000000..0dc70c90afb --- /dev/null +++ b/fastlane/metadata/android/et/changelogs/40102010.txt @@ -0,0 +1,2 @@ +Põhilised muutused selles versioonis: palju täiendusi kõnede ja veel testjärgus olevas kogukonnakeskuste loogikas. +Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases/tag/v1.2.1 diff --git a/fastlane/metadata/android/fa/changelogs/40102000.txt b/fastlane/metadata/android/fa/changelogs/40102000.txt new file mode 100644 index 00000000000..9c9a7c51d07 --- /dev/null +++ b/fastlane/metadata/android/fa/changelogs/40102000.txt @@ -0,0 +1,2 @@ +تغییرهای اصلی در این نگارش: پیام صوتی به صورت پیش‌گزیده به کار افتاده. +گزارش دگرگونی کامل: https://github.com/vector-im/element-android/releases/tag/v1.2.0 diff --git a/fastlane/metadata/android/fa/changelogs/40102010.txt b/fastlane/metadata/android/fa/changelogs/40102010.txt new file mode 100644 index 00000000000..a2cc27d1b5b --- /dev/null +++ b/fastlane/metadata/android/fa/changelogs/40102010.txt @@ -0,0 +1,2 @@ +تغییرات اصلی در این نگارش: چندین بهبود در ویپ و فضاها (همچنان در حالت آزمایشی). +گزارش دگرگونی کامل: https://github.com/vector-im/element-android/releases/tag/v1.2.1 diff --git a/fastlane/metadata/android/fr-FR/changelogs/40102000.txt b/fastlane/metadata/android/fr-FR/changelogs/40102000.txt new file mode 100644 index 00000000000..504c3e24bec --- /dev/null +++ b/fastlane/metadata/android/fr-FR/changelogs/40102000.txt @@ -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 diff --git a/fastlane/metadata/android/fy/changelogs/40101060.txt b/fastlane/metadata/android/fy/changelogs/40101060.txt index 47ac5692d55..34b367f6a13 100644 --- a/fastlane/metadata/android/fy/changelogs/40101060.txt +++ b/fastlane/metadata/android/fy/changelogs/40101060.txt @@ -1,2 +1,2 @@ -Haadferoaring yn disse ferzje: feroaringen foar 1.1.5 -Folsleine feroaringslist: https://github.com/vector-im/element-android/releases/tag/v1.1.6 +Haadwiziging yn dizze ferzje: feroaringen foar 1.1.5 +Folsleine wizigingslist: https://github.com/vector-im/element-android/releases/tag/v1.1.6 diff --git a/fastlane/metadata/android/hu-HU/changelogs/40102000.txt b/fastlane/metadata/android/hu-HU/changelogs/40102000.txt new file mode 100644 index 00000000000..87824693f7a --- /dev/null +++ b/fastlane/metadata/android/hu-HU/changelogs/40102000.txt @@ -0,0 +1,2 @@ +Fő változás ebben a verzióban: Hangüzenetek alapértelmezetten engedélyezettek. +Teljes változásnapló: https://github.com/vector-im/element-android/releases/tag/v1.2.0 diff --git a/fastlane/metadata/android/hu-HU/changelogs/40102010.txt b/fastlane/metadata/android/hu-HU/changelogs/40102010.txt new file mode 100644 index 00000000000..1ccd51aa8af --- /dev/null +++ b/fastlane/metadata/android/hu-HU/changelogs/40102010.txt @@ -0,0 +1,2 @@ +Fő változás ebben a verzióban: Sok fejlesztés a VoIP és Terek kapcsán (még béta) +Teljes változásnapló: https://github.com/vector-im/element-android/releases/tag/v1.2.1 diff --git a/fastlane/metadata/android/id/changelogs/40101160.txt b/fastlane/metadata/android/id/changelogs/40101160.txt new file mode 100644 index 00000000000..19209bacf2d --- /dev/null +++ b/fastlane/metadata/android/id/changelogs/40101160.txt @@ -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 diff --git a/fastlane/metadata/android/id/changelogs/40102000.txt b/fastlane/metadata/android/id/changelogs/40102000.txt new file mode 100644 index 00000000000..2258b114e8d --- /dev/null +++ b/fastlane/metadata/android/id/changelogs/40102000.txt @@ -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 diff --git a/fastlane/metadata/android/id/full_description.txt b/fastlane/metadata/android/id/full_description.txt index 0a18b8d64aa..75249c6a204 100644 --- a/fastlane/metadata/android/id/full_description.txt +++ b/fastlane/metadata/android/id/full_description.txt @@ -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. Pesan privasi dan terenkripsi -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. Sangat aman -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. Komunikasi dan integrasi lengkap Perpesanan, panggilan suara dan video, berbagi file, berbagi layar dan banyak integrasi, bot dan widget. Buat ruangan, komunitas, tetap terhubung dan selesaikan hal-hal. diff --git a/fastlane/metadata/android/it-IT/changelogs/40102000.txt b/fastlane/metadata/android/it-IT/changelogs/40102000.txt new file mode 100644 index 00000000000..e10007a7b79 --- /dev/null +++ b/fastlane/metadata/android/it-IT/changelogs/40102000.txt @@ -0,0 +1,2 @@ +Modifiche principali in questa versione: i messaggi vocali sono attivi in modo predefinito. +Cronologia completa: https://github.com/vector-im/element-android/releases/tag/v1.2.0 diff --git a/fastlane/metadata/android/it-IT/changelogs/40102010.txt b/fastlane/metadata/android/it-IT/changelogs/40102010.txt new file mode 100644 index 00000000000..33c2d998a75 --- /dev/null +++ b/fastlane/metadata/android/it-IT/changelogs/40102010.txt @@ -0,0 +1,2 @@ +Modifiche principali in questa versione: molti miglioramenti nel VoIP e negli Spazi (ancora in beta). +Cronologia completa: https://github.com/vector-im/element-android/releases/tag/v1.2.1 diff --git a/fastlane/metadata/android/pt-BR/changelogs/40102000.txt b/fastlane/metadata/android/pt-BR/changelogs/40102000.txt new file mode 100644 index 00000000000..c6d01391da1 --- /dev/null +++ b/fastlane/metadata/android/pt-BR/changelogs/40102000.txt @@ -0,0 +1,2 @@ +Principais mudanças nesta versão: Mensagem de Voz está habilitada por default. +Changelog completo: https://github.com/vector-im/element-android/releases/tag/v1.2.0 diff --git a/fastlane/metadata/android/pt-BR/changelogs/40102010.txt b/fastlane/metadata/android/pt-BR/changelogs/40102010.txt new file mode 100644 index 00000000000..0894dd20220 --- /dev/null +++ b/fastlane/metadata/android/pt-BR/changelogs/40102010.txt @@ -0,0 +1,2 @@ +Principais mudanças nesta versão: Muitas melhorias em VoIP e Espaços (ainda em beta). +Changelog completo: https://github.com/vector-im/element-android/releases/tag/v1.2.1 diff --git a/fastlane/metadata/android/ru-RU/changelogs/40101150.txt b/fastlane/metadata/android/ru-RU/changelogs/40101150.txt new file mode 100644 index 00000000000..cbf64e470b9 --- /dev/null +++ b/fastlane/metadata/android/ru-RU/changelogs/40101150.txt @@ -0,0 +1,2 @@ +Основные изменения в этой версии: реализация голосовых сообщений в настройках лабораторий. +Полный список изменений: https://github.com/vector-im/element-android/releases/tag/v1.1.15 diff --git a/fastlane/metadata/android/ru-RU/changelogs/40101160.txt b/fastlane/metadata/android/ru-RU/changelogs/40101160.txt new file mode 100644 index 00000000000..5f0e555d946 --- /dev/null +++ b/fastlane/metadata/android/ru-RU/changelogs/40101160.txt @@ -0,0 +1,2 @@ +Основные изменения в этой версии: Исправление ошибки при отправке зашифрованного сообщения, если кто-то в комнате выходит. +Полный список изменений: https://github.com/vector-im/element-android/releases/tag/v1.1.16 diff --git a/fastlane/metadata/android/ru-RU/changelogs/40102000.txt b/fastlane/metadata/android/ru-RU/changelogs/40102000.txt new file mode 100644 index 00000000000..af0a444afa3 --- /dev/null +++ b/fastlane/metadata/android/ru-RU/changelogs/40102000.txt @@ -0,0 +1,2 @@ +Основные изменения в этой версии: Голосовое сообщение включено по умолчанию. +Полный список изменений: https://github.com/vector-im/element-android/releases/tag/v1.2.0 diff --git a/fastlane/metadata/android/ru-RU/changelogs/40102010.txt b/fastlane/metadata/android/ru-RU/changelogs/40102010.txt new file mode 100644 index 00000000000..167af260d54 --- /dev/null +++ b/fastlane/metadata/android/ru-RU/changelogs/40102010.txt @@ -0,0 +1,2 @@ +Основные изменения в этой версии: Множество улучшений в VoIP и пространствах (все еще в бета-версии). +Полный список изменений: https://github.com/vector-im/element-android/releases/tag/v1.2.1 diff --git a/fastlane/metadata/android/sq/changelogs/40100100.txt b/fastlane/metadata/android/sq/changelogs/40100100.txt new file mode 100644 index 00000000000..aba7bebd5a1 --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/40100100.txt @@ -0,0 +1,2 @@ +Ky version i ri përmban kryesisht ndreqje të metash dhe përmirësime. Dërgimi i një mesazhi tani është shumë i shpejtë. +Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.0.10 diff --git a/fastlane/metadata/android/sq/changelogs/40100110.txt b/fastlane/metadata/android/sq/changelogs/40100110.txt new file mode 100644 index 00000000000..d1b8e9f9d3a --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/40100110.txt @@ -0,0 +1,2 @@ +Ky version i ri përmban kryesisht përmirësime të ndërfaqes dhe punimit të përdoruesit. Tani mund të ftoni shokë, dhe të krijoni MD shumë shpejt, përmes skanimit të kodesh QR. +Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.0.11 diff --git a/fastlane/metadata/android/sq/changelogs/40100120.txt b/fastlane/metadata/android/sq/changelogs/40100120.txt new file mode 100644 index 00000000000..d7d9998e0b2 --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/40100120.txt @@ -0,0 +1,2 @@ +Ndryshimet kryesore në këtë version: Paraparje URL-sh, tastierë e re për emoji, aftësi të reja për rregullime dhome, dhe dëborë për Krishtlindje! +Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.0.12 diff --git a/fastlane/metadata/android/sq/changelogs/40100130.txt b/fastlane/metadata/android/sq/changelogs/40100130.txt new file mode 100644 index 00000000000..5d50ff531d9 --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/40100130.txt @@ -0,0 +1,2 @@ +Ndryshimet kryesore në këtë version: Paraparje URL-sh, tastierë e re për emoji, aftësi të reja për rregullime dhome, dhe dëborë për Krishtlindje! +Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.0.13 diff --git a/fastlane/metadata/android/sq/changelogs/40100140.txt b/fastlane/metadata/android/sq/changelogs/40100140.txt new file mode 100644 index 00000000000..bdab3841b0f --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/40100140.txt @@ -0,0 +1,2 @@ +Ndryshimet kryesore në këtë version: Përpunim lejesh dhome, temë e çelët/e errët e automatizuar, dhe një dorë ndreqjesh të metash. +Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.0.14 diff --git a/fastlane/metadata/android/sq/changelogs/40100150.txt b/fastlane/metadata/android/sq/changelogs/40100150.txt new file mode 100644 index 00000000000..045f2369b69 --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/40100150.txt @@ -0,0 +1,2 @@ +Ndryshimet kryesore në këtë version: Mbulim Hyrjesh nga rrjete shoqërorë. +Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.0.15 diff --git a/fastlane/metadata/android/sq/changelogs/40100160.txt b/fastlane/metadata/android/sq/changelogs/40100160.txt new file mode 100644 index 00000000000..ece7bbd2a64 --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/40100160.txt @@ -0,0 +1,2 @@ +Ndryshimet kryesore në këtë version: Mbulim Hyrjesh nga rrjete shoqërorë. +Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.0.15 dhe https://github.com/vector-im/element-android/releases/tag/v1.0.16 diff --git a/fastlane/metadata/android/sq/changelogs/40100170.txt b/fastlane/metadata/android/sq/changelogs/40100170.txt new file mode 100644 index 00000000000..76cf7a9ffab --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/40100170.txt @@ -0,0 +1,2 @@ +Ndryshimet kryesore në këtë version: Ndreqje të metash! +Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.0.17 diff --git a/fastlane/metadata/android/sq/changelogs/40101000.txt b/fastlane/metadata/android/sq/changelogs/40101000.txt new file mode 100644 index 00000000000..b4424f55bcd --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/40101000.txt @@ -0,0 +1,2 @@ +Ndryshimet kryesore në këtë version: Përmirësime për VoIP (thirrje audio dhe video në DM) dhe ndreqje të metash! +Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.1.0 diff --git a/fastlane/metadata/android/sq/changelogs/40101010.txt b/fastlane/metadata/android/sq/changelogs/40101010.txt new file mode 100644 index 00000000000..20b35d34394 --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/40101010.txt @@ -0,0 +1,2 @@ +Ndryshimet kryesore në këtë version: përmirësime funksionimi dhe ndreqje të metash! +Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.1.1 diff --git a/fastlane/metadata/android/sq/changelogs/40101020.txt b/fastlane/metadata/android/sq/changelogs/40101020.txt new file mode 100644 index 00000000000..6f53ec219e5 --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/40101020.txt @@ -0,0 +1,2 @@ +Ndryshimet kryesore në këtë version: përmirësime funksionimi dhe ndreqje të metash! +Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.1.2 diff --git a/fastlane/metadata/android/sq/changelogs/40101030.txt b/fastlane/metadata/android/sq/changelogs/40101030.txt new file mode 100644 index 00000000000..9dbc4f142ad --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/40101030.txt @@ -0,0 +1,2 @@ +Ndryshimet kryesore në këtë version: përmirësime funksionimi dhe ndreqje të metash! +Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.1.3 diff --git a/fastlane/metadata/android/sq/changelogs/40101040.txt b/fastlane/metadata/android/sq/changelogs/40101040.txt new file mode 100644 index 00000000000..949fa629b95 --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/40101040.txt @@ -0,0 +1,2 @@ +Ndryshimet kryesore në këtë version: përmirësime funksionimi dhe ndreqje të metash! +Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.1.4 diff --git a/fastlane/metadata/android/sq/changelogs/40101050.txt b/fastlane/metadata/android/sq/changelogs/40101050.txt new file mode 100644 index 00000000000..28e28523568 --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/40101050.txt @@ -0,0 +1,2 @@ +Ndryshimet kryesore në këtë version: ndreqje të metash për 1.1.4 +Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.1.5 diff --git a/fastlane/metadata/android/sq/changelogs/40101060.txt b/fastlane/metadata/android/sq/changelogs/40101060.txt new file mode 100644 index 00000000000..ab01eede45e --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/40101060.txt @@ -0,0 +1,2 @@ +Ndryshimet kryesore në këtë version: ndreqje të metash për 1.1.5 +Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.1.6 diff --git a/fastlane/metadata/android/sq/changelogs/40101070.txt b/fastlane/metadata/android/sq/changelogs/40101070.txt new file mode 100644 index 00000000000..8d23bb5b949 --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/40101070.txt @@ -0,0 +1,2 @@ +Ndryshimet kryesore në këtë version: mbulim për Hapësira, në fazë beta. Ngjeshje videosh, përpara dërgimi. +Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.1.7 diff --git a/fastlane/metadata/android/sq/changelogs/40101080.txt b/fastlane/metadata/android/sq/changelogs/40101080.txt new file mode 100644 index 00000000000..f9282142cbd --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/40101080.txt @@ -0,0 +1,2 @@ +Ndryshimet kryesore në këtë version: përmirësime për Hapësira. +Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.1.8 diff --git a/fastlane/metadata/android/sq/changelogs/40101090.txt b/fastlane/metadata/android/sq/changelogs/40101090.txt new file mode 100644 index 00000000000..069ab4954de --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/40101090.txt @@ -0,0 +1,2 @@ +Ndryshimet kryesore në këtë version: shtim mbulimi për rrjetin gitter.im. +Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.1.9 diff --git a/fastlane/metadata/android/sq/changelogs/40101100.txt b/fastlane/metadata/android/sq/changelogs/40101100.txt new file mode 100644 index 00000000000..bf5079bc9a6 --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/40101100.txt @@ -0,0 +1,2 @@ +Ndryshimet kryesore në këtë version: përditësime teme dhe stili dhe veçori të reja për hapësira. +Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.1.10 diff --git a/fastlane/metadata/android/sq/changelogs/40101110.txt b/fastlane/metadata/android/sq/changelogs/40101110.txt new file mode 100644 index 00000000000..44d03bb8cb5 --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/40101110.txt @@ -0,0 +1,2 @@ +Ndryshimet kryesore në këtë version: përditësime teme dhe stili dhe veçori të reja për hapësira (ndreqje të mete për 1.1.10) +Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.1.11 diff --git a/fastlane/metadata/android/sq/changelogs/40101120.txt b/fastlane/metadata/android/sq/changelogs/40101120.txt new file mode 100644 index 00000000000..aecede8d91b --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/40101120.txt @@ -0,0 +1,2 @@ +Ndryshimet kryesore në këtë version: përditësime teme dhe stili dhe ndreqje e një vithisjeje pas një thirrjeje video +Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.1.12 diff --git a/fastlane/metadata/android/sq/changelogs/40101130.txt b/fastlane/metadata/android/sq/changelogs/40101130.txt new file mode 100644 index 00000000000..535ccd7518a --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/40101130.txt @@ -0,0 +1,2 @@ +Ndryshimet kryesore në këtë version: përditësim kryesisht për qëndrueshmërinë dhe ndreqje të metash. +Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.1.13 diff --git a/fastlane/metadata/android/sq/changelogs/40101140.txt b/fastlane/metadata/android/sq/changelogs/40101140.txt new file mode 100644 index 00000000000..2dc279e1f74 --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/40101140.txt @@ -0,0 +1,2 @@ +Ndryshimet kryesore në këtë version: ndreqje e një problemi rreth mesazhesh të fshehtëzuar. +Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.1.14 diff --git a/fastlane/metadata/android/sq/changelogs/40101150.txt b/fastlane/metadata/android/sq/changelogs/40101150.txt new file mode 100644 index 00000000000..1fbf2bae7a7 --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/40101150.txt @@ -0,0 +1,2 @@ +Ndryshimet kryesore në këtë version: sendërtim mesazhesh zanore, nën mjedis laboratori. +Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.1.15 diff --git a/fastlane/metadata/android/sq/changelogs/40101160.txt b/fastlane/metadata/android/sq/changelogs/40101160.txt new file mode 100644 index 00000000000..ecb9a83918a --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/40101160.txt @@ -0,0 +1,2 @@ +Ndryshimet kryesore në këtë version: Ndreqje gabimi, kur dërgohet mesazh i fshehtëzuar, nëse dikush nga dhoma bën dalje prej saj. +Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.1.16 diff --git a/fastlane/metadata/android/sq/changelogs/40102000.txt b/fastlane/metadata/android/sq/changelogs/40102000.txt new file mode 100644 index 00000000000..c1f2333f1ca --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/40102000.txt @@ -0,0 +1,2 @@ +Ndryshimet kryesore në këtë version: Mesazh Zanor është i aktivizuar, si parazgjedhje. +Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.2.0 diff --git a/fastlane/metadata/android/sq/changelogs/40102010.txt b/fastlane/metadata/android/sq/changelogs/40102010.txt new file mode 100644 index 00000000000..6ffe456bd48 --- /dev/null +++ b/fastlane/metadata/android/sq/changelogs/40102010.txt @@ -0,0 +1,2 @@ +Ndryshimet kryesore në këtë version: Mjaft përmirësime në VoIP dhe Hapësira (ende në beta). +Regjistër i plotë ndryshimesh: https://github.com/vector-im/element-android/releases/tag/v1.2.1 diff --git a/fastlane/metadata/android/sq/full_description.txt b/fastlane/metadata/android/sq/full_description.txt new file mode 100644 index 00000000000..96cf102272e --- /dev/null +++ b/fastlane/metadata/android/sq/full_description.txt @@ -0,0 +1,39 @@ +Element-i është si aplikacion shkëmbyes i sigurt mesazhesh, ashtu edhe bashkëpunimi prodhimtar ekipi, i cili është ideal për fjalosje në grup, teksa punohet së largët. Ky aplikacion fjalosjeje përdor fshehtëzim skaj-më-skaj për të furnizuar konferencë video, shkëmbim kartelash dhe thirrje me zë të fuqishme. + +Në veçoritë e Element-it përfshihen: +- Mjete të thelluara komunikimi internetor +- Mesazhe plotësisht të fshehtëzuar, për të lejuar komunikim në nivel korporate, madje edhe për punonjës së largëti +- Fjalosje e decentralizuar, bazuar në platformën me burim të hapët Matrix +- Shkëmbim i sigurt kartelash, me të dhëna të fshehtëzuara, teksa administrohen projekte +- Fjalosje video të llojit VoIP dhe tregim ekrani +- Integrim i kollajtë me mjetet tuaja të parapëlqyera të bashkëpunimit internetor, mjete administrimi projektesh, shërbime VoIP dhe aplikacione të tjera shkëmbimi mesazhesh në ekip + +Element-i është plotësisht i ndryshëm nga aplikacione të tjera shkëmbimi mesazhesh dhe bashkëpunimi. Funksionimi i tij bazohet në Matrix, një rrjet i hapët për mesazhe të siguruar dhe komunikim të decentralizuar. Lejon vetëstrehim, për t’u lejuar përdoruesve pronësi dhe kontroll maksimal të të dhënave dhe mesazheve të tyre. + +Privatësi dhe shkëmbim mesazhesh të fshehtëzuar +Element-i ju mbron nga reklama të padëshiruara, shfrytëzim të dhënash dhe vatha dixhitale. Ai siguron gjithashtu krejt të dhënat tuaja, komunikime tek-për-tek me video dhe me zë, përmes fshehtëzimi skaj-më-skaj dhe verifikim “cross-signed” pajisjesh. + +Element-i ju jep kontrollin e privatësisë tuaj, teksa ju lejon të komunikoni në mënyrë të siguruar me këdo në rrjetin Matrix, ose me mjete të tjera bashkëpunimi në shkallë biznesi, duke u integruar me aplikacione të tillë si Slack. + +Element-i mund të vetëstrehohet +Për të lejuar më tepër kontroll mbi të dhënat dhe bisedat tuaja rezervat, Element-i mund të vetëstrehohet, ose mund të zgjidhni cilëndo strehë të bazuar në Matrix - standardi për komunikim me burim të hapët, të decentralizuar. Element-i ju jep privatësi, pajtueshmëri sigurie dhe zhdërvjelltësi integrimesh. + +Jini zot i të dhënave tuaja +Ju vendosni ku të mbahen të dhënat dhe mesazhet tuaja. Pa rrezikun e shfrytëzimit të të dhënave apo hyrjes në to nga palë të treta. + +Element-i ju vë ju në kontroll përmes rrugësh të ndryshme: +1. Merrni një llogari falas te shërbyesi publik matrix.org strehuar nga zhvillues të Matrix-it, ose zgjidhni prej mijëra shërbyesish publikë të strehuar nga vullnetarë +2. Vetëstrehoni llogarinë tuaj duke xhiruar një shërbyes në infrastrukturën tuaj TI +3. Regjistrohuni për një llogari në një shërbyes vetjak, thjesht duke u pajtuar te platforma Element Matrix Services e strehimeve + +Shkëmbim mesazhesh dhe bashkëpunim me burim të hapët +Mund të fjaloseni me këdo në rrjetin Matrix, qoftë kur përdorin Element, një tjetër aplikacion Matrix, apo edhe kur përdorin një tjetër aplikacion shkëmbimi mesazhesh. + +Super i sigurt +Fshehtëzim i njëmendtë skaj-më-skaj (vetëm ata te biseda mund të shfshehtëzojnë mesazhe), dhe verifikim “cross-signed” pajisjesh. + +Komunikim dhe integrim i plotë +Shkëmbim mesazhesh, thirrje me zë dhe me video, shkëmbim kartelash, tregim ekrani dhe një grup i tërë integrimesh, robotësh dhe widget-esh. Krijoni dhoma, bashkësi, mbani lidhjet dhe mbaroni punë. + +Rifillojani atje ku e latë +Jini në dijeni, kudo ku gjendeni, me historik plotësisht të njëkohësuar mesazhesh nëpër krejt pajisjet tuaja dhe në internet te https://app.element.io diff --git a/fastlane/metadata/android/sq/short_description.txt b/fastlane/metadata/android/sq/short_description.txt new file mode 100644 index 00000000000..21937ccce5c --- /dev/null +++ b/fastlane/metadata/android/sq/short_description.txt @@ -0,0 +1 @@ +Mesazhe grupi - mesazhe, fjalosje në grup dhe thirrje me video, të fshehtëzuara diff --git a/fastlane/metadata/android/sq/title.txt b/fastlane/metadata/android/sq/title.txt new file mode 100644 index 00000000000..097f9c48eaf --- /dev/null +++ b/fastlane/metadata/android/sq/title.txt @@ -0,0 +1 @@ +Element - Shkëmbyes i Sigurt Mesazhesh diff --git a/fastlane/metadata/android/sv-SE/changelogs/40102000.txt b/fastlane/metadata/android/sv-SE/changelogs/40102000.txt new file mode 100644 index 00000000000..c31355dc09c --- /dev/null +++ b/fastlane/metadata/android/sv-SE/changelogs/40102000.txt @@ -0,0 +1,2 @@ +Huvudsakliga ändringar i den här versionen: Röstmeddelanden är aktiverade som förval. +Full ändringslogg: https://github.com/vector-im/element-android/releases/tag/v1.2.0 diff --git a/fastlane/metadata/android/uk/changelogs/40102000.txt b/fastlane/metadata/android/uk/changelogs/40102000.txt new file mode 100644 index 00000000000..9abc8c0298f --- /dev/null +++ b/fastlane/metadata/android/uk/changelogs/40102000.txt @@ -0,0 +1,2 @@ +Основні зміни в цій версії: голосові повідомлення типово увімкнено. +Повний журнал змін: https://github.com/vector-im/element-android/releases/tag/v1.2.0 diff --git a/fastlane/metadata/android/uk/changelogs/40102010.txt b/fastlane/metadata/android/uk/changelogs/40102010.txt new file mode 100644 index 00000000000..39a8d839b66 --- /dev/null +++ b/fastlane/metadata/android/uk/changelogs/40102010.txt @@ -0,0 +1,2 @@ +Основні зміни в цій версії: багато вдосконалень VoIP і просторів (досі бета) +Повний журнал змін: https://github.com/vector-im/element-android/releases/tag/v1.2.1 diff --git a/fastlane/metadata/android/uk/full_description.txt b/fastlane/metadata/android/uk/full_description.txt index df06315754b..285f5774524 100644 --- a/fastlane/metadata/android/uk/full_description.txt +++ b/fastlane/metadata/android/uk/full_description.txt @@ -33,7 +33,7 @@ Element надає такі можливості на вибір: Справжнє наскрізне шифрування (лише учасники бесіди можуть розшифровувати повідомлення) та взаємне підписування пристроїв. Повноцінні спілкування та інтеграція -Обмін повідомленнями, голосові та відеовиклики, обмін файлами, спільний доступ до екрана та ціла купа інтеграцій, ботів та віджетів. Створюйте кімнати, спільноти, залишайтеся на зв’язку та виконуйте завдання. +Обмін повідомленнями, голосові та відеовиклики, обмін файлами, спільний доступ до екрана та ціла купа інтеграцій, ботів та розширень. Створюйте кімнати, спільноти, залишайтеся на зв’язку та виконуйте завдання. Продовжуйте, де зупинилися Залишайтеся на зв'язку, де б ви не знаходились, з повністю синхронізованою історією повідомлень на всіх своїх пристроях та в Інтернеті за адресою https://app.element.io diff --git a/fastlane/metadata/android/zh-CN/changelogs/40102000.txt b/fastlane/metadata/android/zh-CN/changelogs/40102000.txt new file mode 100644 index 00000000000..fa1db168050 --- /dev/null +++ b/fastlane/metadata/android/zh-CN/changelogs/40102000.txt @@ -0,0 +1,2 @@ +此版本中的主要更改:默认启用语音消息。 +完整更新日志:https://github.com/vector-im/element-android/releases/tag/v1.2.0 diff --git a/fastlane/metadata/android/zh-CN/changelogs/40102010.txt b/fastlane/metadata/android/zh-CN/changelogs/40102010.txt new file mode 100644 index 00000000000..2ec2ae22b3b --- /dev/null +++ b/fastlane/metadata/android/zh-CN/changelogs/40102010.txt @@ -0,0 +1,2 @@ +这个版本的主要变化:VoIP和空间的许多改进(仍在测试中)。 +完整更新日志:https://github.com/vector-im/element-android/releases/tag/v1.2.1 diff --git a/fastlane/metadata/android/zh-TW/changelogs/40102000.txt b/fastlane/metadata/android/zh-TW/changelogs/40102000.txt new file mode 100644 index 00000000000..993a59c825e --- /dev/null +++ b/fastlane/metadata/android/zh-TW/changelogs/40102000.txt @@ -0,0 +1,2 @@ +此版本中的主要變動:語音訊息預設啟用。 +完整的變更紀錄:https://github.com/vector-im/element-android/releases/tag/v1.2.0 diff --git a/fastlane/metadata/android/zh-TW/changelogs/40102010.txt b/fastlane/metadata/android/zh-TW/changelogs/40102010.txt new file mode 100644 index 00000000000..b520266a789 --- /dev/null +++ b/fastlane/metadata/android/zh-TW/changelogs/40102010.txt @@ -0,0 +1,2 @@ +此版本中的主要變動:對 VoIP 與空間功能的諸多改善(仍在測試中)。 +完整的變更紀錄:https://github.com/vector-im/element-android/releases/tag/v1.2.1 diff --git a/gradle.properties b/gradle.properties index 200866be253..98d561815be 100644 --- a/gradle.properties +++ b/gradle.properties @@ -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=-Xmx2048m -# 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 diff --git a/library/ui-styles/build.gradle b/library/ui-styles/build.gradle index 365e4b902c3..cee58414c78 100644 --- a/library/ui-styles/build.gradle +++ b/library/ui-styles/build.gradle @@ -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 diff --git a/library/ui-styles/src/main/res/values/stylable_bottom_sheet_action.xml b/library/ui-styles/src/main/res/values/stylable_bottom_sheet_action.xml index d6fa160d6b6..b0c45b1feac 100644 --- a/library/ui-styles/src/main/res/values/stylable_bottom_sheet_action.xml +++ b/library/ui-styles/src/main/res/values/stylable_bottom_sheet_action.xml @@ -8,6 +8,7 @@ + diff --git a/matrix-sdk-android-rx/build.gradle b/matrix-sdk-android-rx/build.gradle index d18e3b1d72f..dbd761cee36 100644 --- a/matrix-sdk-android-rx/build.gradle +++ b/matrix-sdk-android-rx/build.gradle @@ -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 } diff --git a/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/LiveDataObservable.kt b/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/LiveDataObservable.kt index 2174c6f1182..56b52facf93 100644 --- a/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/LiveDataObservable.kt +++ b/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/LiveDataObservable.kt @@ -34,8 +34,8 @@ private class LiveDataObservable( liveData.observeForever(relay) } - private inner class RemoveObserverInMainThread(private val observer: io.reactivex.Observer) - : MainThreadDisposable(), Observer { + private inner class RemoveObserverInMainThread(private val observer: io.reactivex.Observer) : + MainThreadDisposable(), Observer { override fun onChanged(t: T?) { if (!isDisposed) { diff --git a/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/OptionalRx.kt b/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/OptionalRx.kt index ff4b0d755ca..936bd824e74 100644 --- a/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/OptionalRx.kt +++ b/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/OptionalRx.kt @@ -16,8 +16,8 @@ package org.matrix.android.sdk.rx -import org.matrix.android.sdk.api.util.Optional import io.reactivex.Observable +import org.matrix.android.sdk.api.util.Optional fun Observable>.unwrap(): Observable { return filter { it.hasValue() }.map { it.get() } diff --git a/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/RxSession.kt b/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/RxSession.kt index 58fb760ff5e..47203816b43 100644 --- a/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/RxSession.kt +++ b/matrix-sdk-android-rx/src/main/java/org/matrix/android/sdk/rx/RxSession.kt @@ -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> = rxSingle { + session.identityService().lookUp(listOf(threePid)).firstOrNull().toOptional() + } } fun Session.rx(): RxSession { diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle index 24bed6f9957..b493db3157f 100644 --- a/matrix-sdk-android/build.gradle +++ b/matrix-sdk-android/build.gradle @@ -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.3.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 "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.jetbrains.kotlinStdlibJdk7 + implementation libs.jetbrains.coroutinesCore + implementation libs.jetbrains.coroutinesAndroid - implementation "androidx.appcompat:appcompat:1.3.1" - implementation "androidx.core:core-ktx:1.6.0" + implementation libs.androidx.appCompat + implementation libs.androidx.core - 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(platform("com.squareup.okhttp3:okhttp-bom:4.9.2")) 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.6' // 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 } diff --git a/matrix-sdk-android/src/androidTest/AndroidManifest.xml b/matrix-sdk-android/src/androidTest/AndroidManifest.xml index 274bd8c87b1..40360fcd197 100644 --- a/matrix-sdk-android/src/androidTest/AndroidManifest.xml +++ b/matrix-sdk-android/src/androidTest/AndroidManifest.xml @@ -8,10 +8,15 @@ This is mandatory to run integration tests --> + tools:node="merge"> + + diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/InstrumentedTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/InstrumentedTest.kt index 583406346eb..a7637668210 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/InstrumentedTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/InstrumentedTest.kt @@ -18,8 +18,8 @@ package org.matrix.android.sdk import android.content.Context import androidx.test.core.app.ApplicationProvider -import org.matrix.android.sdk.test.shared.createTimberTestRule import org.junit.Rule +import org.matrix.android.sdk.test.shared.createTimberTestRule interface InstrumentedTest { diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/SingleThreadCoroutineDispatcher.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/SingleThreadCoroutineDispatcher.kt index 9942ea9db33..192f6442b22 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/SingleThreadCoroutineDispatcher.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/SingleThreadCoroutineDispatcher.kt @@ -16,9 +16,9 @@ package org.matrix.android.sdk -import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.asCoroutineDispatcher +import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers import java.util.concurrent.Executors internal val testCoroutineDispatchers = MatrixCoroutineDispatchers(Main, Main, Main, Main, diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/account/AccountCreationTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/account/AccountCreationTest.kt index 5dede9dcfdc..e0451bea386 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/account/AccountCreationTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/account/AccountCreationTest.kt @@ -16,16 +16,16 @@ package org.matrix.android.sdk.account -import org.matrix.android.sdk.InstrumentedTest -import org.matrix.android.sdk.common.CommonTestHelper -import org.matrix.android.sdk.common.CryptoTestHelper -import org.matrix.android.sdk.common.SessionTestParams -import org.matrix.android.sdk.common.TestConstants import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 import org.junit.runners.MethodSorters +import org.matrix.android.sdk.InstrumentedTest +import org.matrix.android.sdk.common.CommonTestHelper +import org.matrix.android.sdk.common.CryptoTestHelper +import org.matrix.android.sdk.common.SessionTestParams +import org.matrix.android.sdk.common.TestConstants @RunWith(JUnit4::class) @FixMethodOrder(MethodSorters.JVM) diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/account/ChangePasswordTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/account/ChangePasswordTest.kt index 103b638c397..d32bcb3fe5b 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/account/ChangePasswordTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/account/ChangePasswordTest.kt @@ -16,17 +16,17 @@ package org.matrix.android.sdk.account -import org.matrix.android.sdk.InstrumentedTest -import org.matrix.android.sdk.api.failure.isInvalidPassword -import org.matrix.android.sdk.common.CommonTestHelper -import org.matrix.android.sdk.common.SessionTestParams -import org.matrix.android.sdk.common.TestConstants import org.amshove.kluent.shouldBeTrue import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 import org.junit.runners.MethodSorters +import org.matrix.android.sdk.InstrumentedTest +import org.matrix.android.sdk.api.failure.isInvalidPassword +import org.matrix.android.sdk.common.CommonTestHelper +import org.matrix.android.sdk.common.SessionTestParams +import org.matrix.android.sdk.common.TestConstants @RunWith(JUnit4::class) @FixMethodOrder(MethodSorters.JVM) diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/account/DeactivateAccountTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/account/DeactivateAccountTest.kt index 01c4f8ccb3b..f8d108fb739 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/account/DeactivateAccountTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/account/DeactivateAccountTest.kt @@ -67,9 +67,9 @@ class DeactivateAccountTest : InstrumentedTest { val throwable = commonTestHelper.logAccountWithError(session.myUserId, TestConstants.PASSWORD) // Test the error - assertTrue(throwable is Failure.ServerError - && throwable.error.code == MatrixError.M_USER_DEACTIVATED - && throwable.error.message == "This account has been deactivated") + assertTrue(throwable is Failure.ServerError && + throwable.error.code == MatrixError.M_USER_DEACTIVATED && + throwable.error.message == "This account has been deactivated") // Try to create an account with the deactivate account user id, it will fail (M_USER_IN_USE) val hs = commonTestHelper.createHomeServerConfig() @@ -95,8 +95,8 @@ class DeactivateAccountTest : InstrumentedTest { // Test the error accountCreationError.let { - assertTrue(it is Failure.ServerError - && it.error.code == MatrixError.M_USER_IN_USE) + assertTrue(it is Failure.ServerError && + it.error.code == MatrixError.M_USER_IN_USE) } // No need to close the session, it has been deactivated diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/api/Matrix.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/api/Matrix.kt index c439da8407b..8b9b6efa119 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/api/Matrix.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/api/Matrix.kt @@ -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 + ")" } } } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt index 7817351e534..cf9b8f87c1a 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CommonTestHelper.kt @@ -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) } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/MockOkHttpInterceptor.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/MockOkHttpInterceptor.kt index e7978a9cb29..b6d833a77cd 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/MockOkHttpInterceptor.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/MockOkHttpInterceptor.kt @@ -15,12 +15,12 @@ */ package org.matrix.android.sdk.common -import org.matrix.android.sdk.internal.session.TestInterceptor import okhttp3.Interceptor import okhttp3.Protocol import okhttp3.Request import okhttp3.Response import okhttp3.ResponseBody.Companion.toResponseBody +import org.matrix.android.sdk.internal.session.TestInterceptor import javax.net.ssl.HttpsURLConnection /** diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestMatrixCallback.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestMatrixCallback.kt index c2e1ec0f926..9f6d6eb1368 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestMatrixCallback.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/TestMatrixCallback.kt @@ -17,8 +17,8 @@ package org.matrix.android.sdk.common import androidx.annotation.CallSuper -import org.matrix.android.sdk.api.MatrixCallback import org.junit.Assert.fail +import org.matrix.android.sdk.api.MatrixCallback import timber.log.Timber import java.util.concurrent.CountDownLatch diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/AttachmentEncryptionTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/AttachmentEncryptionTest.kt index b6cb7f9e790..aaf779212b6 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/AttachmentEncryptionTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/AttachmentEncryptionTest.kt @@ -26,9 +26,9 @@ import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.matrix.android.sdk.internal.crypto.attachments.MXEncryptedAttachments +import org.matrix.android.sdk.internal.crypto.attachments.toElementToDecrypt import org.matrix.android.sdk.internal.crypto.model.rest.EncryptedFileInfo import org.matrix.android.sdk.internal.crypto.model.rest.EncryptedFileKey -import org.matrix.android.sdk.internal.crypto.attachments.toElementToDecrypt import java.io.ByteArrayOutputStream import java.io.InputStream diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/CryptoStoreHelper.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/CryptoStoreHelper.kt index 75ccce0db97..c717c8e33fe 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/CryptoStoreHelper.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/CryptoStoreHelper.kt @@ -16,12 +16,12 @@ package org.matrix.android.sdk.internal.crypto +import io.realm.RealmConfiguration import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStore import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStoreModule import org.matrix.android.sdk.internal.crypto.store.db.mapper.CrossSigningKeysMapper import org.matrix.android.sdk.internal.di.MoshiProvider -import io.realm.RealmConfiguration import kotlin.random.Random internal class CryptoStoreHelper { diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/CryptoStoreTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/CryptoStoreTest.kt index 1d838b5c849..f43c425cc97 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/CryptoStoreTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/CryptoStoreTest.kt @@ -17,9 +17,6 @@ package org.matrix.android.sdk.internal.crypto import androidx.test.ext.junit.runners.AndroidJUnit4 -import org.matrix.android.sdk.InstrumentedTest -import org.matrix.android.sdk.internal.crypto.model.OlmSessionWrapper -import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore import io.realm.Realm import org.junit.Assert.assertEquals import org.junit.Assert.assertNotEquals @@ -27,6 +24,9 @@ import org.junit.Assert.assertNull import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.matrix.android.sdk.InstrumentedTest +import org.matrix.android.sdk.internal.crypto.model.OlmSessionWrapper +import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore import org.matrix.olm.OlmAccount import org.matrix.olm.OlmManager import org.matrix.olm.OlmSession diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/PreShareKeysTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/PreShareKeysTest.kt index a2566c14149..825fba570a3 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/PreShareKeysTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/PreShareKeysTest.kt @@ -50,8 +50,8 @@ class PreShareKeysTest : InstrumentedTest { aliceSession.cryptoService().discardOutboundSession(e2eRoomID) val preShareCount = bobSession.cryptoService().getGossipingEvents().count { - it.senderId == aliceSession.myUserId - && it.getClearType() == EventType.ROOM_KEY + it.senderId == aliceSession.myUserId && + it.getClearType() == EventType.ROOM_KEY } assertEquals("Bob should not have receive any key from alice at this point", 0, preShareCount) @@ -65,16 +65,16 @@ class PreShareKeysTest : InstrumentedTest { mTestHelper.waitWithLatch { latch -> mTestHelper.retryPeriodicallyWithLatch(latch) { val newGossipCount = bobSession.cryptoService().getGossipingEvents().count { - it.senderId == aliceSession.myUserId - && it.getClearType() == EventType.ROOM_KEY + it.senderId == aliceSession.myUserId && + it.getClearType() == EventType.ROOM_KEY } newGossipCount > preShareCount } } val latest = bobSession.cryptoService().getGossipingEvents().lastOrNull { - it.senderId == aliceSession.myUserId - && it.getClearType() == EventType.ROOM_KEY + it.senderId == aliceSession.myUserId && + it.getClearType() == EventType.ROOM_KEY } val content = latest?.getClearContent().toModel() diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/WithHeldTests.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/WithHeldTests.kt index b2516ea2bee..c939952dc9f 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/WithHeldTests.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/gossiping/WithHeldTests.kt @@ -18,6 +18,11 @@ package org.matrix.android.sdk.internal.crypto.gossiping import android.util.Log import androidx.test.ext.junit.runners.AndroidJUnit4 +import org.junit.Assert +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters import org.matrix.android.sdk.InstrumentedTest import org.matrix.android.sdk.api.NoOpMatrixCallback import org.matrix.android.sdk.api.extensions.tryOrNull @@ -31,11 +36,6 @@ import org.matrix.android.sdk.common.SessionTestParams import org.matrix.android.sdk.common.TestConstants import org.matrix.android.sdk.internal.crypto.model.event.EncryptedEventContent import org.matrix.android.sdk.internal.crypto.model.event.WithHeldCode -import org.junit.Assert -import org.junit.FixMethodOrder -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters @RunWith(AndroidJUnit4::class) @FixMethodOrder(MethodSorters.JVM) diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupPasswordTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupPasswordTest.kt index cc71f88fc03..63e74603d04 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupPasswordTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupPasswordTest.kt @@ -17,9 +17,6 @@ package org.matrix.android.sdk.internal.crypto.keysbackup import androidx.test.ext.junit.runners.AndroidJUnit4 -import org.matrix.android.sdk.InstrumentedTest -import org.matrix.android.sdk.api.listeners.ProgressListener -import org.matrix.android.sdk.common.assertByteArrayNotEqual import org.junit.Assert.assertArrayEquals import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue @@ -28,6 +25,9 @@ import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters +import org.matrix.android.sdk.InstrumentedTest +import org.matrix.android.sdk.api.listeners.ProgressListener +import org.matrix.android.sdk.common.assertByteArrayNotEqual import org.matrix.olm.OlmManager import org.matrix.olm.OlmPkDecryption diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTest.kt index 89d297c5928..0785dba8b95 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTest.kt @@ -17,6 +17,16 @@ package org.matrix.android.sdk.internal.crypto.keysbackup import androidx.test.ext.junit.runners.AndroidJUnit4 +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue +import org.junit.Assert.fail +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters import org.matrix.android.sdk.InstrumentedTest import org.matrix.android.sdk.api.listeners.ProgressListener import org.matrix.android.sdk.api.listeners.StepProgressListener @@ -33,16 +43,6 @@ import org.matrix.android.sdk.internal.crypto.keysbackup.model.MegolmBackupCreat import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersion import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersionResult import org.matrix.android.sdk.internal.crypto.model.ImportRoomKeysResult -import org.junit.Assert.assertEquals -import org.junit.Assert.assertFalse -import org.junit.Assert.assertNotNull -import org.junit.Assert.assertNull -import org.junit.Assert.assertTrue -import org.junit.Assert.fail -import org.junit.FixMethodOrder -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters import java.util.ArrayList import java.util.Collections import java.util.concurrent.CountDownLatch diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTestHelper.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTestHelper.kt index b6e5ae73647..a625ffc0e97 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTestHelper.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTestHelper.kt @@ -16,6 +16,7 @@ package org.matrix.android.sdk.internal.crypto.keysbackup +import org.junit.Assert import org.matrix.android.sdk.api.listeners.ProgressListener import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupService @@ -28,7 +29,6 @@ import org.matrix.android.sdk.common.assertListEquals import org.matrix.android.sdk.internal.crypto.MegolmSessionData import org.matrix.android.sdk.internal.crypto.keysbackup.model.MegolmBackupCreationInfo import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersion -import org.junit.Assert import java.util.concurrent.CountDownLatch class KeysBackupTestHelper( diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/StateObserver.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/StateObserver.kt index ff8ce43b55f..80e54d82ec7 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/StateObserver.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/StateObserver.kt @@ -16,11 +16,11 @@ package org.matrix.android.sdk.internal.crypto.keysbackup +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupService import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupState import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupStateListener -import org.junit.Assert.assertEquals -import org.junit.Assert.assertNull import java.util.concurrent.CountDownLatch /** @@ -91,8 +91,8 @@ internal class StateObserver(private val keysBackup: KeysBackupService, stateList.add(newState) // Check that state transition is valid - if (stateList.size >= 2 - && !allowedStateTransitions.contains(stateList[stateList.size - 2] to newState)) { + if (stateList.size >= 2 && + !allowedStateTransitions.contains(stateList[stateList.size - 2] to newState)) { // Forbidden transition detected lastTransitionError = "Forbidden transition detected from " + stateList[stateList.size - 2] + " to " + newState } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/ssss/QuadSTests.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/ssss/QuadSTests.kt index 74855b86309..b343d7334ac 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/ssss/QuadSTests.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/ssss/QuadSTests.kt @@ -18,8 +18,20 @@ package org.matrix.android.sdk.internal.crypto.ssss import androidx.lifecycle.Observer import androidx.test.ext.junit.runners.AndroidJUnit4 +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertNull +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters import org.matrix.android.sdk.InstrumentedTest import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent import org.matrix.android.sdk.api.session.securestorage.EncryptedSecretContent import org.matrix.android.sdk.api.session.securestorage.KeySigner import org.matrix.android.sdk.api.session.securestorage.RawBytesKeySpec @@ -33,18 +45,6 @@ import org.matrix.android.sdk.common.TestConstants import org.matrix.android.sdk.internal.crypto.SSSS_ALGORITHM_AES_HMAC_SHA2 import org.matrix.android.sdk.internal.crypto.crosssigning.toBase64NoPadding import org.matrix.android.sdk.internal.crypto.secrets.DefaultSharedSecretStorageService -import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking -import org.junit.Assert.assertEquals -import org.junit.Assert.assertNotNull -import org.junit.Assert.assertNull -import org.junit.FixMethodOrder -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters import java.util.concurrent.CountDownLatch @RunWith(AndroidJUnit4::class) diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt index 4ea8cdc0744..e0d49b3f5e7 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt @@ -18,6 +18,16 @@ package org.matrix.android.sdk.internal.crypto.verification import android.util.Log import androidx.test.ext.junit.runners.AndroidJUnit4 +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue +import org.junit.Assert.fail +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters import org.matrix.android.sdk.InstrumentedTest import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.crypto.verification.CancelCode @@ -38,16 +48,6 @@ import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap import org.matrix.android.sdk.internal.crypto.model.rest.KeyVerificationCancel import org.matrix.android.sdk.internal.crypto.model.rest.KeyVerificationStart import org.matrix.android.sdk.internal.crypto.model.rest.toValue -import org.junit.Assert.assertEquals -import org.junit.Assert.assertFalse -import org.junit.Assert.assertNotNull -import org.junit.Assert.assertNull -import org.junit.Assert.assertTrue -import org.junit.Assert.fail -import org.junit.FixMethodOrder -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters import java.util.concurrent.CountDownLatch @RunWith(AndroidJUnit4::class) @@ -551,7 +551,7 @@ class SASTest : InstrumentedTest { cryptoTestData.roomId ) - var requestID : String? = null + var requestID: String? = null mTestHelper.waitWithLatch { mTestHelper.retryPeriodicallyWithLatch(it) { diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/SharedSecretTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/SharedSecretTest.kt index 97b93dcf5a2..9b10f9e9afa 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/SharedSecretTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/SharedSecretTest.kt @@ -17,13 +17,13 @@ package org.matrix.android.sdk.internal.crypto.verification.qrcode import androidx.test.ext.junit.runners.AndroidJUnit4 -import org.matrix.android.sdk.InstrumentedTest import org.amshove.kluent.shouldBe import org.amshove.kluent.shouldNotBeEqualTo import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters +import org.matrix.android.sdk.InstrumentedTest @RunWith(AndroidJUnit4::class) @FixMethodOrder(MethodSorters.JVM) diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/session/room/send/MarkdownParserTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/session/room/send/MarkdownParserTest.kt index 94303dda081..1ed2f899773 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/session/room/send/MarkdownParserTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/session/room/send/MarkdownParserTest.kt @@ -25,6 +25,9 @@ import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.matrix.android.sdk.InstrumentedTest +import org.matrix.android.sdk.api.MatrixConfiguration +import org.matrix.android.sdk.common.TestRoomDisplayNameFallbackProvider +import org.matrix.android.sdk.internal.session.displayname.DisplayNameResolver import org.matrix.android.sdk.internal.session.room.send.pills.MentionLinkSpecComparator import org.matrix.android.sdk.internal.session.room.send.pills.TextPillsUtils @@ -48,7 +51,14 @@ class MarkdownParserTest : InstrumentedTest { private val markdownParser = MarkdownParser( Parser.builder().build(), HtmlRenderer.builder().softbreak("
").build(), - TextPillsUtils(MentionLinkSpecComparator()) + TextPillsUtils( + MentionLinkSpecComparator(), + DisplayNameResolver( + MatrixConfiguration( + applicationFlavor = "TestFlavor", + roomDisplayNameFallbackProvider = TestRoomDisplayNameFallbackProvider() + ) + )) ) @Test diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/util/JsonCanonicalizerTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/util/JsonCanonicalizerTest.kt index b5ab6589ff5..d38afc6b62f 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/util/JsonCanonicalizerTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/util/JsonCanonicalizerTest.kt @@ -17,10 +17,10 @@ package org.matrix.android.sdk.internal.util import androidx.test.ext.junit.runners.AndroidJUnit4 -import org.matrix.android.sdk.InstrumentedTest import org.junit.Assert.assertEquals import org.junit.Test import org.junit.runner.RunWith +import org.matrix.android.sdk.InstrumentedTest @RunWith(AndroidJUnit4::class) internal class JsonCanonicalizerTest : InstrumentedTest { diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineBackToPreviousLastForwardTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineBackToPreviousLastForwardTest.kt index 3774e6f513f..7628f287c97 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineBackToPreviousLastForwardTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineBackToPreviousLastForwardTest.kt @@ -16,6 +16,14 @@ package org.matrix.android.sdk.session.room.timeline +import org.amshove.kluent.shouldBeFalse +import org.amshove.kluent.shouldBeTrue +import org.junit.Assert.assertTrue +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.junit.runners.MethodSorters import org.matrix.android.sdk.InstrumentedTest import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.events.model.EventType @@ -26,14 +34,6 @@ import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings import org.matrix.android.sdk.common.CommonTestHelper import org.matrix.android.sdk.common.CryptoTestHelper import org.matrix.android.sdk.common.checkSendOrder -import org.amshove.kluent.shouldBeFalse -import org.amshove.kluent.shouldBeTrue -import org.junit.Assert.assertTrue -import org.junit.FixMethodOrder -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 -import org.junit.runners.MethodSorters import timber.log.Timber import java.util.concurrent.CountDownLatch @@ -111,8 +111,8 @@ class TimelineBackToPreviousLastForwardTest : InstrumentedTest { } // Ok, we have the 10 last messages from Alice. - snapshot.size == 10 - && snapshot.all { it.root.content.toModel()?.body?.startsWith(messageRoot).orFalse() } + snapshot.size == 10 && + snapshot.all { it.root.content.toModel()?.body?.startsWith(messageRoot).orFalse() } } bobTimeline.addListener(eventsListener) @@ -160,10 +160,10 @@ class TimelineBackToPreviousLastForwardTest : InstrumentedTest { } // Bob can see the first event of the room (so Back pagination has worked) - snapshot.lastOrNull()?.root?.getClearType() == EventType.STATE_ROOM_CREATE + snapshot.lastOrNull()?.root?.getClearType() == EventType.STATE_ROOM_CREATE && // 8 for room creation item, and 30 for the forward pagination - && snapshot.size == 38 - && snapshot.checkSendOrder(messageRoot, 30, 0) + snapshot.size == 38 && + snapshot.checkSendOrder(messageRoot, 30, 0) } bobTimeline.addListener(eventsListener) diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineForwardPaginationTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineForwardPaginationTest.kt index 0fe341cad60..dfa6ec10ae0 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineForwardPaginationTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelineForwardPaginationTest.kt @@ -16,6 +16,13 @@ package org.matrix.android.sdk.session.room.timeline +import org.amshove.kluent.shouldBeFalse +import org.amshove.kluent.shouldBeTrue +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.junit.runners.MethodSorters import org.matrix.android.sdk.InstrumentedTest import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.events.model.EventType @@ -26,13 +33,6 @@ import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings import org.matrix.android.sdk.common.CommonTestHelper import org.matrix.android.sdk.common.CryptoTestHelper import org.matrix.android.sdk.common.checkSendOrder -import org.amshove.kluent.shouldBeFalse -import org.amshove.kluent.shouldBeTrue -import org.junit.FixMethodOrder -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 -import org.junit.runners.MethodSorters import timber.log.Timber import java.util.concurrent.CountDownLatch @@ -86,8 +86,8 @@ class TimelineForwardPaginationTest : InstrumentedTest { } // Ok, we have the 10 last messages of the initial sync - snapshot.size == 10 - && snapshot.all { it.root.content.toModel()?.body?.startsWith(message).orFalse() } + snapshot.size == 10 && + snapshot.all { it.root.content.toModel()?.body?.startsWith(message).orFalse() } } // Open the timeline at last sent message @@ -110,8 +110,8 @@ class TimelineForwardPaginationTest : InstrumentedTest { } // The event is not in db, so it is fetch alone - snapshot.size == 1 - && snapshot.all { it.root.content.toModel()?.body?.startsWith("Message from Alice").orFalse() } + snapshot.size == 1 && + snapshot.all { it.root.content.toModel()?.body?.startsWith("Message from Alice").orFalse() } } aliceTimeline.addListener(aliceEventsListener) @@ -137,9 +137,9 @@ class TimelineForwardPaginationTest : InstrumentedTest { } // Alice can see the first event of the room (so Back pagination has worked) - snapshot.lastOrNull()?.root?.getClearType() == EventType.STATE_ROOM_CREATE + snapshot.lastOrNull()?.root?.getClearType() == EventType.STATE_ROOM_CREATE && // 6 for room creation item (backward pagination), 1 for the context, and 50 for the forward pagination - && snapshot.size == 57 // 6 + 1 + 50 + snapshot.size == 57 // 6 + 1 + 50 } aliceTimeline.addListener(aliceEventsListener) @@ -166,8 +166,8 @@ class TimelineForwardPaginationTest : InstrumentedTest { Timber.w(" event ${it.root.content}") } // 6 for room creation item (backward pagination),and numberOfMessagesToSend (all the message of the room) - snapshot.size == 6 + numberOfMessagesToSend - && snapshot.checkSendOrder(message, numberOfMessagesToSend, 0) + snapshot.size == 6 + numberOfMessagesToSend && + snapshot.checkSendOrder(message, numberOfMessagesToSend, 0) } aliceTimeline.addListener(aliceEventsListener) diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelinePreviousLastForwardTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelinePreviousLastForwardTest.kt index 03a4d419881..e865fe17da8 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelinePreviousLastForwardTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/room/timeline/TimelinePreviousLastForwardTest.kt @@ -16,6 +16,13 @@ package org.matrix.android.sdk.session.room.timeline +import org.amshove.kluent.shouldBeFalse +import org.amshove.kluent.shouldBeTrue +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.junit.runners.MethodSorters import org.matrix.android.sdk.InstrumentedTest import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.events.model.EventType @@ -26,13 +33,6 @@ import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings import org.matrix.android.sdk.common.CommonTestHelper import org.matrix.android.sdk.common.CryptoTestHelper import org.matrix.android.sdk.common.checkSendOrder -import org.amshove.kluent.shouldBeFalse -import org.amshove.kluent.shouldBeTrue -import org.junit.FixMethodOrder -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 -import org.junit.runners.MethodSorters import timber.log.Timber import java.util.concurrent.CountDownLatch @@ -107,8 +107,8 @@ class TimelinePreviousLastForwardTest : InstrumentedTest { } // Ok, we have the 10 last messages from Alice. This will be our future previous lastForward chunk - snapshot.size == 10 - && snapshot.all { it.root.content.toModel()?.body?.startsWith(firstMessage).orFalse() } + snapshot.size == 10 && + snapshot.all { it.root.content.toModel()?.body?.startsWith(firstMessage).orFalse() } } bobTimeline.addListener(eventsListener) @@ -141,8 +141,8 @@ class TimelinePreviousLastForwardTest : InstrumentedTest { } // Ok, we have the 10 last messages from Alice. This will be our future previous lastForward chunk - snapshot.size == 10 - && snapshot.all { it.root.content.toModel()?.body?.startsWith(secondMessage).orFalse() } + snapshot.size == 10 && + snapshot.all { it.root.content.toModel()?.body?.startsWith(secondMessage).orFalse() } } bobTimeline.addListener(eventsListener) @@ -216,11 +216,11 @@ class TimelinePreviousLastForwardTest : InstrumentedTest { } // Bob can see the first event of the room (so Back pagination has worked) - snapshot.lastOrNull()?.root?.getClearType() == EventType.STATE_ROOM_CREATE + snapshot.lastOrNull()?.root?.getClearType() == EventType.STATE_ROOM_CREATE && // 8 for room creation item 60 message from Alice - && snapshot.size == 68 // 8 + 60 - && snapshot.checkSendOrder(secondMessage, 30, 0) - && snapshot.checkSendOrder(firstMessage, 30, 30) + snapshot.size == 68 && // 8 + 60 + snapshot.checkSendOrder(secondMessage, 30, 0) && + snapshot.checkSendOrder(firstMessage, 30, 30) } bobTimeline.addListener(eventsListener) diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceHierarchyTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceHierarchyTest.kt index 301cdea4610..436daf001be 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceHierarchyTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/session/space/SpaceHierarchyTest.kt @@ -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> + /** 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() } + + 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() + ?.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) } } diff --git a/matrix-sdk-android/src/debug/java/org/matrix/android/sdk/internal/network/interceptors/CurlLoggingInterceptor.kt b/matrix-sdk-android/src/debug/java/org/matrix/android/sdk/internal/network/interceptors/CurlLoggingInterceptor.kt index 2103dc954d5..3add757efad 100644 --- a/matrix-sdk-android/src/debug/java/org/matrix/android/sdk/internal/network/interceptors/CurlLoggingInterceptor.kt +++ b/matrix-sdk-android/src/debug/java/org/matrix/android/sdk/internal/network/interceptors/CurlLoggingInterceptor.kt @@ -17,10 +17,10 @@ package org.matrix.android.sdk.internal.network.interceptors -import org.matrix.android.sdk.internal.di.MatrixScope import okhttp3.Interceptor import okhttp3.Response import okio.Buffer +import org.matrix.android.sdk.internal.di.MatrixScope import timber.log.Timber import java.io.IOException import java.nio.charset.Charset @@ -36,8 +36,8 @@ import javax.inject.Inject * non-production environment. */ @MatrixScope -internal class CurlLoggingInterceptor @Inject constructor() - : Interceptor { +internal class CurlLoggingInterceptor @Inject constructor() : + Interceptor { /** * Set any additional curl command options (see 'curl --help'). @@ -90,8 +90,8 @@ internal class CurlLoggingInterceptor @Inject constructor() curlCmd += ((if (compressed) " --compressed " else " ") + "'" + request.url.toString() // Replace localhost for emulator by localhost for shell - .replace("://10.0.2.2:8080/".toRegex(), "://127.0.0.1:8080/") - + "'") + .replace("://10.0.2.2:8080/".toRegex(), "://127.0.0.1:8080/") + + "'") // Add Json formatting curlCmd += " | python -m json.tool" diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/Matrix.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/Matrix.kt index 99802592667..8a4526a5e12 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/Matrix.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/Matrix.kt @@ -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 + ")" } } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixConfiguration.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixConfiguration.kt index ed809cdb045..3359e253f64 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixConfiguration.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixConfiguration.kt @@ -32,14 +32,26 @@ data class MatrixConfiguration( "https://scalar-staging.riot.im/scalar/api" ), /** - * Optional proxy to connect to the matrix servers - * You can create one using for instance Proxy(proxyType, InetSocketAddress.createUnresolved(hostname, port) + * Optional base url to create client permalinks (eg. https://www.example.com/#/) instead of Matrix ones (matrix.to links). + * Do not forget to add the "#" which is required by the permalink parser. + * + * Note: this field is only used for permalinks creation, you will also have to edit the string-array `permalink_supported_hosts` in the config file + * and add it to your manifest to handle these links in the application. + */ + val clientPermalinkBaseUrl: String? = null, + /** + * Optional proxy to connect to the matrix servers. + * You can create one using for instance Proxy(proxyType, InetSocketAddress.createUnresolved(hostname, port). */ val proxy: Proxy? = null, /** * True to advertise support for call transfers to other parties on Matrix calls. */ val supportsCallTransfer: Boolean = false, + /** + * MatrixItemDisplayNameFallbackProvider to provide default display name for MatrixItem. By default, the id will be used + */ + val matrixItemDisplayNameFallbackProvider: MatrixItemDisplayNameFallbackProvider? = null, /** * RoomDisplayNameFallbackProvider to provide default room display name. */ @@ -47,7 +59,7 @@ data class MatrixConfiguration( ) { /** - * Can be implemented by your Application class + * Can be implemented by your Application class. */ interface Provider { fun providesMatrixConfiguration(): MatrixConfiguration diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixConstants.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixConstants.kt new file mode 100644 index 00000000000..49520f36785 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixConstants.kt @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * 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 + +/** + * This object define some global constants regarding the Matrix specification + */ +object MatrixConstants { + /** + * Max length for an alias. Room aliases MUST NOT exceed 255 bytes (including the # sigil and the domain). + * See [maxAliasLocalPartLength] + * Ref. https://matrix.org/docs/spec/appendices#room-aliases + */ + const val ALIAS_MAX_LENGTH = 255 + + fun maxAliasLocalPartLength(domain: String): Int { + return (ALIAS_MAX_LENGTH - 1 /* # sigil */ - 1 /* ':' */ - domain.length) + .coerceAtLeast(0) + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixItemDisplayNameFallbackProvider.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixItemDisplayNameFallbackProvider.kt new file mode 100644 index 00000000000..82008cda8c6 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixItemDisplayNameFallbackProvider.kt @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2021 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * 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 + +import org.matrix.android.sdk.api.util.MatrixItem + +interface MatrixItemDisplayNameFallbackProvider { + fun getDefaultName(matrixItem: MatrixItem): String +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixPatterns.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixPatterns.kt index 9a5e40ffeba..2a26b612fb0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixPatterns.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixPatterns.kt @@ -17,6 +17,8 @@ package org.matrix.android.sdk.api import org.matrix.android.sdk.BuildConfig +import org.matrix.android.sdk.internal.util.removeInvalidRoomNameChars +import org.matrix.android.sdk.internal.util.replaceSpaceChars import timber.log.Timber /** @@ -128,10 +130,10 @@ object MatrixPatterns { * @return true if the string is a valid event id. */ fun isEventId(str: String?): Boolean { - return str != null - && (str matches PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER - || str matches PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER_V3 - || str matches PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER_V4) + return str != null && + (str matches PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER || + str matches PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER_V3 || + str matches PATTERN_CONTAIN_MATRIX_EVENT_IDENTIFIER_V4) } /** @@ -162,10 +164,11 @@ object MatrixPatterns { return order != null && order.length < 50 && order matches ORDER_STRING_REGEX } - fun candidateAliasFromRoomName(name: String): String { - return Regex("\\s").replace(name.lowercase(), "_").let { - "[^a-z0-9._%#@=+-]".toRegex().replace(it, "") - } + fun candidateAliasFromRoomName(roomName: String, domain: String): String { + return roomName.lowercase() + .replaceSpaceChars(replacement = "_") + .removeInvalidRoomNameChars() + .take(MatrixConstants.maxAliasLocalPartLength(domain)) } /** diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixUrls.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixUrls.kt new file mode 100644 index 00000000000..dc4e0f152df --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixUrls.kt @@ -0,0 +1,26 @@ +/* + * 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 + +/** + * This class contains pattern to match Matrix Url, aka mxc urls + */ +object MatrixUrls { + const val MATRIX_CONTENT_URI_SCHEME = "mxc://" + + fun String.isMxcUrl() = startsWith(MATRIX_CONTENT_URI_SCHEME) +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/UIABaseAuth.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/UIABaseAuth.kt index d5e323e457f..3a8e6b10846 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/UIABaseAuth.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/UIABaseAuth.kt @@ -27,5 +27,5 @@ interface UIABaseAuth { fun copyWithSession(session: String): UIABaseAuth - fun asMap() : Map + fun asMap(): Map } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/failure/Extensions.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/failure/Extensions.kt index 3149a0218bc..b2035bb2eb5 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/failure/Extensions.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/failure/Extensions.kt @@ -23,20 +23,20 @@ import java.io.IOException import javax.net.ssl.HttpsURLConnection fun Throwable.is401() = - this is Failure.ServerError - && httpCode == HttpsURLConnection.HTTP_UNAUTHORIZED /* 401 */ - && error.code == MatrixError.M_UNAUTHORIZED + this is Failure.ServerError && + httpCode == HttpsURLConnection.HTTP_UNAUTHORIZED && /* 401 */ + error.code == MatrixError.M_UNAUTHORIZED fun Throwable.isTokenError() = - this is Failure.ServerError - && (error.code == MatrixError.M_UNKNOWN_TOKEN - || error.code == MatrixError.M_MISSING_TOKEN - || error.code == MatrixError.ORG_MATRIX_EXPIRED_ACCOUNT) + this is Failure.ServerError && + (error.code == MatrixError.M_UNKNOWN_TOKEN || + error.code == MatrixError.M_MISSING_TOKEN || + error.code == MatrixError.ORG_MATRIX_EXPIRED_ACCOUNT) fun Throwable.shouldBeRetried(): Boolean { - return this is Failure.NetworkConnection - || this is IOException - || (this is Failure.ServerError && error.code == MatrixError.M_LIMIT_EXCEEDED) + return this is Failure.NetworkConnection || + this is IOException || + (this is Failure.ServerError && error.code == MatrixError.M_LIMIT_EXCEEDED) } /** @@ -52,31 +52,31 @@ fun Throwable.getRetryDelay(defaultValue: Long): Long { } fun Throwable.isInvalidPassword(): Boolean { - return this is Failure.ServerError - && error.code == MatrixError.M_FORBIDDEN - && error.message == "Invalid password" + return this is Failure.ServerError && + error.code == MatrixError.M_FORBIDDEN && + error.message == "Invalid password" } fun Throwable.isInvalidUIAAuth(): Boolean { - return this is Failure.ServerError - && error.code == MatrixError.M_FORBIDDEN - && error.flows != null + return this is Failure.ServerError && + error.code == MatrixError.M_FORBIDDEN && + error.flows != null } /** * Try to convert to a RegistrationFlowResponse. Return null in the cases it's not possible */ fun Throwable.toRegistrationFlowResponse(): RegistrationFlowResponse? { - return if (this is Failure.OtherServerError - && httpCode == HttpsURLConnection.HTTP_UNAUTHORIZED /* 401 */) { + return if (this is Failure.OtherServerError && + httpCode == HttpsURLConnection.HTTP_UNAUTHORIZED /* 401 */) { tryOrNull { MoshiProvider.providesMoshi() .adapter(RegistrationFlowResponse::class.java) .fromJson(errorBody) } - } else if (this is Failure.ServerError - && httpCode == HttpsURLConnection.HTTP_UNAUTHORIZED /* 401 */ - && error.code == MatrixError.M_FORBIDDEN) { + } else if (this is Failure.ServerError && + httpCode == HttpsURLConnection.HTTP_UNAUTHORIZED && /* 401 */ + error.code == MatrixError.M_FORBIDDEN) { // This happens when the submission for this stage was bad (like bad password) if (error.session != null && error.flows != null) { RegistrationFlowResponse( @@ -94,9 +94,9 @@ fun Throwable.toRegistrationFlowResponse(): RegistrationFlowResponse? { } fun Throwable.isRegistrationAvailabilityError(): Boolean { - return this is Failure.ServerError - && httpCode == HttpsURLConnection.HTTP_BAD_REQUEST /* 400 */ - && (error.code == MatrixError.M_USER_IN_USE - || error.code == MatrixError.M_INVALID_USERNAME - || error.code == MatrixError.M_EXCLUSIVE) + return this is Failure.ServerError && + httpCode == HttpsURLConnection.HTTP_BAD_REQUEST && /* 400 */ + (error.code == MatrixError.M_USER_IN_USE || + error.code == MatrixError.M_INVALID_USERNAME || + error.code == MatrixError.M_EXCLUSIVE) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/logger/LoggerTag.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/logger/LoggerTag.kt index 51f9b50699b..0d204edceed 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/logger/LoggerTag.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/logger/LoggerTag.kt @@ -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) { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/network/ApiPath.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/network/ApiPath.kt index db112a30b26..baf33a59c57 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/network/ApiPath.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/network/ApiPath.kt @@ -154,7 +154,6 @@ enum class ApiPath(val path: String, val method: String) { SEND_STATE_EVENT(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/state/{state_event_type}", "PUT"), SEND_STATE_EVENT_WITH_STATE_KEY(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/state/{state_event_type}/{state_key}", "PUT"), GET_ROOM_STATE(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/state", "GET"), - SEND_RELATION(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/send_relation/{parent_id}/{relation_type}/{event_type}", "POST"), GET_RELATIONS(NetworkConstants.URI_API_PREFIX_PATH_UNSTABLE + "rooms/{roomId}/relations/{eventId}/{relationType}/{eventType}", "GET"), JOIN_ROOM(NetworkConstants.URI_API_PREFIX_PATH_R0 + "join/{roomIdOrAlias}", "POST"), LEAVE_ROOM(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/leave", "POST"), diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/Session.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/Session.kt index 2f981ffbbed..bde68da9d7e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/Session.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/Session.kt @@ -18,6 +18,7 @@ package org.matrix.android.sdk.api.session import androidx.annotation.MainThread import androidx.lifecycle.LiveData +import kotlinx.coroutines.flow.SharedFlow import okhttp3.OkHttpClient import org.matrix.android.sdk.api.auth.data.SessionParams import org.matrix.android.sdk.api.failure.GlobalError @@ -36,7 +37,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 @@ -52,6 +53,7 @@ import org.matrix.android.sdk.api.session.signout.SignOutService import org.matrix.android.sdk.api.session.space.SpaceService import org.matrix.android.sdk.api.session.sync.FilterService import org.matrix.android.sdk.api.session.sync.SyncState +import org.matrix.android.sdk.api.session.sync.model.SyncResponse import org.matrix.android.sdk.api.session.terms.TermsService import org.matrix.android.sdk.api.session.thirdparty.ThirdPartyService import org.matrix.android.sdk.api.session.typing.TypingUsersTracker @@ -75,7 +77,7 @@ interface Session : ProfileService, PushRuleService, PushersService, - InitialSyncProgressService, + SyncStatusService, HomeServerCapabilitiesService, SecureStorageService, AccountService { @@ -143,6 +145,11 @@ interface Session : */ fun getSyncState(): SyncState + /** + * This method returns a flow of SyncResponse. New value will be pushed through the sync thread. + */ + fun syncFlow(): SharedFlow + /** * This methods return true if an initial sync has been processed */ diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/CallState.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/CallState.kt index 47a63b4a251..ff1df63300c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/CallState.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/call/CallState.kt @@ -26,7 +26,7 @@ sealed class CallState { /** * CreateOffer. Intermediate state between Idle and Dialing. */ - object CreateOffer: CallState() + object CreateOffer : CallState() /** Dialing. Outgoing call is signaling the remote peer */ object Dialing : CallState() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/crosssigning/MXCrossSigningInfo.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/crosssigning/MXCrossSigningInfo.kt index 6327dd92fd3..20ee68d228d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/crosssigning/MXCrossSigningInfo.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/crosssigning/MXCrossSigningInfo.kt @@ -24,8 +24,8 @@ data class MXCrossSigningInfo( val crossSigningKeys: List ) { - fun isTrusted(): Boolean = masterKey()?.trustLevel?.isVerified() == true - && selfSigningKey()?.trustLevel?.isVerified() == true + fun isTrusted(): Boolean = masterKey()?.trustLevel?.isVerified() == true && + selfSigningKey()?.trustLevel?.isVerified() == true fun masterKey(): CryptoCrossSigningKey? = crossSigningKeys .firstOrNull { it.usages?.contains(KeyUsage.MASTER.value) == true } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/keyshare/GossipingRequestListener.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/keyshare/GossipingRequestListener.kt index 810d28dd839..ba2d4ba3f69 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/keyshare/GossipingRequestListener.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/keyshare/GossipingRequestListener.kt @@ -16,8 +16,8 @@ package org.matrix.android.sdk.api.session.crypto.keyshare -import org.matrix.android.sdk.internal.crypto.IncomingRoomKeyRequest import org.matrix.android.sdk.internal.crypto.IncomingRequestCancellation +import org.matrix.android.sdk.internal.crypto.IncomingRoomKeyRequest import org.matrix.android.sdk.internal.crypto.IncomingSecretShareRequest /** diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/verification/PendingVerificationRequest.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/verification/PendingVerificationRequest.kt index 1ee161724a2..be450b9d03c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/verification/PendingVerificationRequest.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/verification/PendingVerificationRequest.kt @@ -48,8 +48,8 @@ data class PendingVerificationRequest( * SAS is supported if I support it and the other party support it */ fun isSasSupported(): Boolean { - return requestInfo?.methods?.contains(VERIFICATION_METHOD_SAS).orFalse() - && readyInfo?.methods?.contains(VERIFICATION_METHOD_SAS).orFalse() + return requestInfo?.methods?.contains(VERIFICATION_METHOD_SAS).orFalse() && + readyInfo?.methods?.contains(VERIFICATION_METHOD_SAS).orFalse() } /** @@ -57,11 +57,11 @@ data class PendingVerificationRequest( */ fun otherCanShowQrCode(): Boolean { return if (isIncoming) { - requestInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SHOW).orFalse() - && readyInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SCAN).orFalse() + requestInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SHOW).orFalse() && + readyInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SCAN).orFalse() } else { - requestInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SCAN).orFalse() - && readyInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SHOW).orFalse() + requestInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SCAN).orFalse() && + readyInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SHOW).orFalse() } } @@ -70,11 +70,11 @@ data class PendingVerificationRequest( */ fun otherCanScanQrCode(): Boolean { return if (isIncoming) { - requestInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SCAN).orFalse() - && readyInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SHOW).orFalse() + requestInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SCAN).orFalse() && + readyInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SHOW).orFalse() } else { - requestInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SHOW).orFalse() - && readyInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SCAN).orFalse() + requestInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SHOW).orFalse() && + readyInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SCAN).orFalse() } } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt index 3d82846e7e9..96b44ce8c90 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt @@ -18,6 +18,9 @@ package org.matrix.android.sdk.api.session.events.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass +import org.json.JSONObject +import org.matrix.android.sdk.api.extensions.tryOrNull +import org.matrix.android.sdk.api.failure.MatrixError import org.matrix.android.sdk.api.session.crypto.MXCryptoError import org.matrix.android.sdk.api.session.room.model.message.MessageContent import org.matrix.android.sdk.api.session.room.model.message.MessageType @@ -27,9 +30,6 @@ import org.matrix.android.sdk.api.util.JsonDict import org.matrix.android.sdk.internal.crypto.algorithms.olm.OlmDecryptionResult import org.matrix.android.sdk.internal.crypto.model.event.EncryptedEventContent import org.matrix.android.sdk.internal.di.MoshiProvider -import org.json.JSONObject -import org.matrix.android.sdk.api.extensions.tryOrNull -import org.matrix.android.sdk.api.failure.MatrixError import timber.log.Timber typealias Content = JsonDict @@ -238,8 +238,8 @@ data class Event( } fun Event.isTextMessage(): Boolean { - return getClearType() == EventType.MESSAGE - && when (getClearContent()?.toModel()?.msgType) { + return getClearType() == EventType.MESSAGE && + when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) { MessageType.MSGTYPE_TEXT, MessageType.MSGTYPE_EMOTE, MessageType.MSGTYPE_NOTICE -> true @@ -248,40 +248,40 @@ fun Event.isTextMessage(): Boolean { } fun Event.isImageMessage(): Boolean { - return getClearType() == EventType.MESSAGE - && when (getClearContent()?.toModel()?.msgType) { + return getClearType() == EventType.MESSAGE && + when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) { MessageType.MSGTYPE_IMAGE -> true else -> false } } fun Event.isVideoMessage(): Boolean { - return getClearType() == EventType.MESSAGE - && when (getClearContent()?.toModel()?.msgType) { + return getClearType() == EventType.MESSAGE && + when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) { MessageType.MSGTYPE_VIDEO -> true else -> false } } fun Event.isAudioMessage(): Boolean { - return getClearType() == EventType.MESSAGE - && when (getClearContent()?.toModel()?.msgType) { + return getClearType() == EventType.MESSAGE && + when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) { MessageType.MSGTYPE_AUDIO -> true else -> false } } fun Event.isFileMessage(): Boolean { - return getClearType() == EventType.MESSAGE - && when (getClearContent()?.toModel()?.msgType) { + return getClearType() == EventType.MESSAGE && + when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) { MessageType.MSGTYPE_FILE -> true else -> false } } fun Event.isAttachmentMessage(): Boolean { - return getClearType() == EventType.MESSAGE - && when (getClearContent()?.toModel()?.msgType) { + return getClearType() == EventType.MESSAGE && + when (getClearContent()?.get(MessageContent.MSG_TYPE_JSON_KEY)) { MessageType.MSGTYPE_IMAGE, MessageType.MSGTYPE_AUDIO, MessageType.MSGTYPE_VIDEO, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/EventType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/EventType.kt index 9c3fdd57daa..d0ce55070e6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/EventType.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/EventType.kt @@ -106,13 +106,13 @@ object EventType { internal const val DUMMY = "m.dummy" fun isCallEvent(type: String): Boolean { - return type == CALL_INVITE - || type == CALL_CANDIDATES - || type == CALL_ANSWER - || type == CALL_HANGUP - || type == CALL_SELECT_ANSWER - || type == CALL_NEGOTIATE - || type == CALL_REJECT - || type == CALL_REPLACES + return type == CALL_INVITE || + type == CALL_CANDIDATES || + type == CALL_ANSWER || + type == CALL_HANGUP || + type == CALL_SELECT_ANSWER || + type == CALL_NEGOTIATE || + type == CALL_REJECT || + type == CALL_REPLACES } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/homeserver/HomeServerCapabilities.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/homeserver/HomeServerCapabilities.kt index b49236c3382..3ed6a7ebb2d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/homeserver/HomeServerCapabilities.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/homeserver/HomeServerCapabilities.kt @@ -89,7 +89,7 @@ data class HomeServerCapabilities( * You can also use #isFeatureSupported prior to this call to check if the * feature is supported and report some feedback to user. */ - fun versionOverrideForFeature(feature: String) : String? { + fun versionOverrideForFeature(feature: String): String? { val cap = roomVersions?.capabilities?.get(feature) return cap?.preferred ?: cap?.support?.lastOrNull() } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/identity/IdentityService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/identity/IdentityService.kt index 1485ec478bb..a22cd572fa4 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/identity/IdentityService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/identity/IdentityService.kt @@ -132,7 +132,7 @@ interface IdentityService { * the identity server offers some crypto functionality to help in accepting invitations. * This is less secure than the client doing it itself, but may be useful where this isn't possible. */ - suspend fun sign3pidInvitation(identiyServer: String, token: String, secret: String) : SignInvitationResult + suspend fun sign3pidInvitation(identiyServer: String, token: String, secret: String): SignInvitationResult fun addListener(listener: IdentityServiceListener) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/initsync/SyncStatusService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/initsync/SyncStatusService.kt new file mode 100644 index 00000000000..daab6d97611 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/initsync/SyncStatusService.kt @@ -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 + + 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() + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/MatrixLinkify.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/MatrixLinkify.kt index 5e9f3e1eb93..3e27da0c41d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/MatrixLinkify.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/MatrixLinkify.kt @@ -50,11 +50,11 @@ object MatrixLinkify { if (startPos == 0 || text[startPos - 1] != '/') { val endPos = match.range.last + 1 var url = text.substring(match.range) - if (MatrixPatterns.isUserId(url) - || MatrixPatterns.isRoomAlias(url) - || MatrixPatterns.isRoomId(url) - || MatrixPatterns.isGroupId(url) - || MatrixPatterns.isEventId(url)) { + if (MatrixPatterns.isUserId(url) || + MatrixPatterns.isRoomAlias(url) || + MatrixPatterns.isRoomId(url) || + MatrixPatterns.isGroupId(url) || + MatrixPatterns.isEventId(url)) { url = PermalinkService.MATRIX_TO_URL_BASE + url } val span = MatrixPermalinkSpan(url, callback) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/MatrixToConverter.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/MatrixToConverter.kt new file mode 100644 index 00000000000..a904e89681a --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/MatrixToConverter.kt @@ -0,0 +1,55 @@ +/* + * 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.permalinks + +import android.net.Uri + +/** + * Mapping of an input URI to a matrix.to compliant URI. + */ +object MatrixToConverter { + + /** + * Try to convert a URL from an element web instance or from a client permalink to a matrix.to url. + * To be successfully converted, URL path should contain one of the [SUPPORTED_PATHS]. + * Examples: + * - https://riot.im/develop/#/room/#element-android:matrix.org -> https://matrix.to/#/#element-android:matrix.org + * - https://app.element.io/#/room/#element-android:matrix.org -> https://matrix.to/#/#element-android:matrix.org + * - https://www.example.org/#/room/#element-android:matrix.org -> https://matrix.to/#/#element-android:matrix.org + */ + fun convert(uri: Uri): Uri? { + val uriString = uri.toString() + + return when { + // URL is already a matrix.to + uriString.startsWith(PermalinkService.MATRIX_TO_URL_BASE) -> uri + // Web or client url + SUPPORTED_PATHS.any { it in uriString } -> { + val path = SUPPORTED_PATHS.first { it in uriString } + Uri.parse(PermalinkService.MATRIX_TO_URL_BASE + uriString.substringAfter(path)) + } + // URL is not supported + else -> null + } + } + + private val SUPPORTED_PATHS = listOf( + "/#/room/", + "/#/user/", + "/#/group/" + ) +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkParser.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkParser.kt index 005a2edae73..edb748c76eb 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkParser.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkParser.kt @@ -26,6 +26,7 @@ import java.net.URLDecoder * This class turns a uri to a [PermalinkData] * element-based domains (e.g. https://app.element.io/#/user/@chagai95:matrix.org) permalinks * or matrix.to permalinks (e.g. https://matrix.to/#/@chagai95:matrix.org) + * or client permalinks (e.g. user/@chagai95:matrix.org) */ object PermalinkParser { @@ -42,13 +43,15 @@ object PermalinkParser { * https://github.com/matrix-org/matrix-doc/blob/master/proposals/1704-matrix.to-permalinks.md */ fun parse(uri: Uri): PermalinkData { - if (!uri.toString().startsWith(PermalinkService.MATRIX_TO_URL_BASE)) { - return PermalinkData.FallbackLink(uri) - } + // the client or element-based domain permalinks (e.g. https://app.element.io/#/user/@chagai95:matrix.org) don't have the + // mxid in the first param (like matrix.to does - https://matrix.to/#/@chagai95:matrix.org) but rather in the second after /user/ so /user/mxid + // so convert URI to matrix.to to simplify parsing process + val matrixToUri = MatrixToConverter.convert(uri) ?: return PermalinkData.FallbackLink(uri) + // We can't use uri.fragment as it is decoding to early and it will break the parsing // of parameters that represents url (like signurl) - val fragment = uri.toString().substringAfter("#") // uri.fragment - if (fragment.isNullOrEmpty()) { + val fragment = matrixToUri.toString().substringAfter("#") // uri.fragment + if (fragment.isEmpty()) { return PermalinkData.FallbackLink(uri) } val safeFragment = fragment.substringBefore('?') @@ -61,20 +64,14 @@ object PermalinkParser { .map { URLDecoder.decode(it, "UTF-8") } .take(2) - // the element-based domain permalinks (e.g. https://app.element.io/#/user/@chagai95:matrix.org) don't have the - // mxid in the first param (like matrix.to does - https://matrix.to/#/@chagai95:matrix.org) but rather in the second after /user/ so /user/mxid - var identifier = params.getOrNull(0) - if (identifier.equals("user")) { - identifier = params.getOrNull(1) - } - + val identifier = params.getOrNull(0) val extraParameter = params.getOrNull(1) return when { identifier.isNullOrEmpty() -> PermalinkData.FallbackLink(uri) MatrixPatterns.isUserId(identifier) -> PermalinkData.UserLink(userId = identifier) MatrixPatterns.isGroupId(identifier) -> PermalinkData.GroupLink(groupId = identifier) MatrixPatterns.isRoomId(identifier) -> { - handleRoomIdCase(fragment, identifier, uri, extraParameter, viaQueryParameters) + handleRoomIdCase(fragment, identifier, matrixToUri, extraParameter, viaQueryParameters) } MatrixPatterns.isRoomAlias(identifier) -> { PermalinkData.RoomLink( @@ -125,12 +122,13 @@ object PermalinkParser { } } - private fun safeExtractParams(fragment: String) = fragment.substringAfter("?").split('&').mapNotNull { - val splitNameValue = it.split("=") - if (splitNameValue.size == 2) { - Pair(splitNameValue[0], URLDecoder.decode(splitNameValue[1], "UTF-8")) - } else null - } + private fun safeExtractParams(fragment: String) = + fragment.substringAfter("?").split('&').mapNotNull { + val splitNameValue = it.split("=") + if (splitNameValue.size == 2) { + Pair(splitNameValue[0], URLDecoder.decode(splitNameValue[1], "UTF-8")) + } else null + } private fun String.getViaParameters(): List { return UrlQuerySanitizer(this) @@ -138,9 +136,7 @@ object PermalinkParser { .filter { it.mParameter == "via" }.map { - it.mValue.let { - URLDecoder.decode(it, "UTF-8") - } + URLDecoder.decode(it.mValue, "UTF-8") } } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkService.kt index a6d4583c76f..920dc85c7a0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/permalinks/PermalinkService.kt @@ -19,7 +19,8 @@ package org.matrix.android.sdk.api.session.permalinks import org.matrix.android.sdk.api.session.events.model.Event /** - * Useful methods to create Matrix permalink (matrix.to links). + * Useful methods to create permalink (like matrix.to links or client permalinks). + * See [org.matrix.android.sdk.api.MatrixConfiguration.clientPermalinkBaseUrl] to setup a custom permalink base url. */ interface PermalinkService { @@ -32,10 +33,11 @@ interface PermalinkService { * Ex: "https://matrix.to/#/!nbzmcXAqpxBXjAdgoX:matrix.org/$1531497316352799BevdV:matrix.org" * * @param event the event + * @param forceMatrixTo whether we should force using matrix.to base URL * * @return the permalink, or null in case of error */ - fun createPermalink(event: Event): String? + fun createPermalink(event: Event, forceMatrixTo: Boolean = false): String? /** * Creates a permalink for an id (can be a user Id, etc.). @@ -43,18 +45,21 @@ interface PermalinkService { * Ex: "https://matrix.to/#/@benoit:matrix.org" * * @param id the id + * @param forceMatrixTo whether we should force using matrix.to base URL + * * @return the permalink, or null in case of error */ - fun createPermalink(id: String): String? + fun createPermalink(id: String, forceMatrixTo: Boolean = false): String? /** * Creates a permalink for a roomId, including the via parameters * * @param roomId the room id + * @param forceMatrixTo whether we should force using matrix.to base URL * * @return the permalink, or null in case of error */ - fun createRoomPermalink(roomId: String, viaServers: List? = null): String? + fun createRoomPermalink(roomId: String, viaServers: List? = null, forceMatrixTo: Boolean = false): String? /** * Creates a permalink for an event. If you have an event you can use [createPermalink] @@ -62,10 +67,11 @@ interface PermalinkService { * * @param roomId the id of the room * @param eventId the id of the event + * @param forceMatrixTo whether we should force using matrix.to base URL * * @return the permalink */ - fun createPermalink(roomId: String, eventId: String): String + fun createPermalink(roomId: String, eventId: String, forceMatrixTo: Boolean = false): String /** * Extract the linked id from the universal link diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/Pusher.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/Pusher.kt index eed75c9dafb..b85ab32b210 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/Pusher.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/Pusher.kt @@ -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, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/PushersService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/PushersService.kt index a5ec100f64c..2cd17952c68 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/PushersService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/PushersService.kt @@ -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 status = workManager.getWorkInfoByIdLiveData()) + * @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 */ diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/AliasAvailabilityResult.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/AliasAvailabilityResult.kt index 6f607569c08..fff6adecbf4 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/AliasAvailabilityResult.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/AliasAvailabilityResult.kt @@ -19,6 +19,6 @@ package org.matrix.android.sdk.api.session.room import org.matrix.android.sdk.api.session.room.alias.RoomAliasError sealed class AliasAvailabilityResult { - object Available: AliasAvailabilityResult() + object Available : AliasAvailabilityResult() data class NotAvailable(val roomAliasError: RoomAliasError) : AliasAvailabilityResult() } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomDirectoryService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomDirectoryService.kt index f3e3913bc12..9446f0fdffd 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomDirectoryService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomDirectoryService.kt @@ -41,5 +41,5 @@ interface RoomDirectoryService { */ suspend fun setRoomDirectoryVisibility(roomId: String, roomDirectoryVisibility: RoomDirectoryVisibility) - suspend fun checkAliasAvailability(aliasLocalPart: String?) : AliasAvailabilityResult + suspend fun checkAliasAvailability(aliasLocalPart: String?): AliasAvailabilityResult } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt index 5d26b213491..d80faa729c2 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt @@ -220,7 +220,7 @@ interface RoomService { .setPrefetchDistance(10) .build() - fun getFlattenRoomSummaryChildrenOf(spaceId: String?, memberships: List = Membership.activeMemberships()) : List + fun getFlattenRoomSummaryChildrenOf(spaceId: String?, memberships: List = Membership.activeMemberships()): List /** * Returns all the children of this space, as LiveData diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomStrippedState.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomStrippedState.kt new file mode 100644 index 00000000000..dc0c00b282c --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/RoomStrippedState.kt @@ -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? = 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() + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallAnswerContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallAnswerContent.kt index 180b32db054..6b4d7828326 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallAnswerContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallAnswerContent.kt @@ -44,7 +44,7 @@ data class CallAnswerContent( * Capability advertisement. */ @Json(name = "capabilities") val capabilities: CallCapabilities? = null -): CallSignalingContent { +) : CallSignalingContent { @JsonClass(generateAdapter = true) data class Answer( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallCandidatesContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallCandidatesContent.kt index dc0a1e3b2e3..deec80f4fff 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallCandidatesContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallCandidatesContent.kt @@ -41,4 +41,4 @@ data class CallCandidatesContent( * Required. The version of the VoIP specification this messages adheres to. */ @Json(name = "version") override val version: String? -): CallSignalingContent +) : CallSignalingContent diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallInviteContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallInviteContent.kt index e4332f0ea76..d70e63d1226 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallInviteContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallInviteContent.kt @@ -55,7 +55,7 @@ data class CallInviteContent( */ @Json(name = "capabilities") val capabilities: CallCapabilities? = null -): CallSignalingContent { +) : CallSignalingContent { @JsonClass(generateAdapter = true) data class Offer( /** diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallNegotiateContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallNegotiateContent.kt index 68dd5ef043e..bbbfbe68ab8 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallNegotiateContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallNegotiateContent.kt @@ -47,7 +47,7 @@ data class CallNegotiateContent( */ @Json(name = "version") override val version: String? - ): CallSignalingContent { + ) : CallSignalingContent { @JsonClass(generateAdapter = true) data class Description( /** diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallReplacesContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallReplacesContent.kt index 4559c5db6d5..7947b7d0bd1 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallReplacesContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallReplacesContent.kt @@ -61,7 +61,7 @@ data class CallReplacesContent( * Required. The version of the VoIP specification this messages adheres to. */ @Json(name = "version") override val version: String? -): CallSignalingContent { +) : CallSignalingContent { @JsonClass(generateAdapter = true) data class TargetUser( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallSelectAnswerContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallSelectAnswerContent.kt index 795f3327111..634bee1d83e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallSelectAnswerContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/call/CallSelectAnswerContent.kt @@ -41,4 +41,4 @@ data class CallSelectAnswerContent( * Required. The version of the VoIP specification this message adheres to. */ @Json(name = "version") override val version: String? -): CallSignalingContent +) : CallSignalingContent diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/AudioWaveformInfo.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/AudioWaveformInfo.kt index d576f1057a2..aa05eb71892 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/AudioWaveformInfo.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/AudioWaveformInfo.kt @@ -32,5 +32,5 @@ data class AudioWaveformInfo( * List of integers between zero and 1024, inclusive. */ @Json(name = "waveform") - val waveform: List? = null + val waveform: List? = null ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageAudioContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageAudioContent.kt index 1bcb10d88cf..ebf3d127ce4 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageAudioContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageAudioContent.kt @@ -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'. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageContent.kt index df5641a6220..5a1b66c91c1 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageContent.kt @@ -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? diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageDefaultContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageDefaultContent.kt index 65e89cdfee4..1dadc922717 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageDefaultContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageDefaultContent.kt @@ -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 diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageEmoteContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageEmoteContent.kt index 77983a031eb..a2ada416ba1 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageEmoteContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageEmoteContent.kt @@ -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. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageFileContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageFileContent.kt index 96877b4d9f1..78f9a5d2f2d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageFileContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageFileContent.kt @@ -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. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageImageContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageImageContent.kt index 73fd1eab565..ea7ab506887 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageImageContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageImageContent.kt @@ -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, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageLocationContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageLocationContent.kt index bdb54910dd0..6881c099246 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageLocationContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageLocationContent.kt @@ -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 diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageNoticeContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageNoticeContent.kt index b2fd8cb0c0c..dd960355cad 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageNoticeContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageNoticeContent.kt @@ -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. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageOptionsContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageOptionsContent.kt index 7924469884c..7a1a99bd5fd 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageOptionsContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageOptionsContent.kt @@ -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?, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessagePollResponseContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessagePollResponseContent.kt index d827475277e..9edfe118b09 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessagePollResponseContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessagePollResponseContent.kt @@ -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 diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageTextContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageTextContent.kt index e45245a9bf1..5968fecd43b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageTextContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageTextContent.kt @@ -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. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageVerificationRequestContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageVerificationRequestContent.kt index 25b5f448150..b2b3cdac909 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageVerificationRequestContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageVerificationRequestContent.kt @@ -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, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageVideoContent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageVideoContent.kt index 3f5d2dab2e7..e1b0cd86074 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageVideoContent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/MessageVideoContent.kt @@ -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'. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/version/RoomVersionService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/version/RoomVersionService.kt index ea67b55174c..d806e6007e5 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/version/RoomVersionService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/version/RoomVersionService.kt @@ -31,7 +31,7 @@ interface RoomVersionService { /** * Get the recommended room version for the current homeserver */ - fun getRecommendedVersion() : String + fun getRecommendedVersion(): String /** * Ask if the user has enough power level to upgrade the room diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/securestorage/SharedSecretStorageError.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/securestorage/SharedSecretStorageError.kt index 59325f99032..a91b97b86cd 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/securestorage/SharedSecretStorageError.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/securestorage/SharedSecretStorageError.kt @@ -22,8 +22,8 @@ sealed class SharedSecretStorageError(message: String?) : Throwable(message) { data class UnknownAlgorithm(val keyId: String) : SharedSecretStorageError("Unknown algorithm $keyId") data class UnsupportedAlgorithm(val algorithm: String) : SharedSecretStorageError("Unknown algorithm $algorithm") data class SecretNotEncrypted(val secretName: String) : SharedSecretStorageError("Missing content for secret $secretName") - data class SecretNotEncryptedWithKey(val secretName: String, val keyId: String) - : SharedSecretStorageError("Missing content for secret $secretName with key $keyId") + data class SecretNotEncryptedWithKey(val secretName: String, val keyId: String) : + SharedSecretStorageError("Missing content for secret $secretName with key $keyId") object BadKeyFormat : SharedSecretStorageError("Bad Key Format") object ParsingError : SharedSecretStorageError("parsing Error") diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/SpaceService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/SpaceService.kt index bcc36b579a3..f40572518f0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/SpaceService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/space/SpaceService.kt @@ -94,5 +94,7 @@ interface SpaceService { */ suspend fun setSpaceParent(childRoomId: String, parentSpaceId: String, canonical: Boolean, viaServers: List) + suspend fun removeSpaceParent(childRoomId: String, parentSpaceId: String) + fun getRootSpaceSummaries(): List } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/DeviceListResponse.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/DeviceListResponse.kt similarity index 90% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/DeviceListResponse.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/DeviceListResponse.kt index bfa8c342b6f..c05e1e51878 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/DeviceListResponse.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/DeviceListResponse.kt @@ -13,7 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.matrix.android.sdk.internal.session.sync.model + +package org.matrix.android.sdk.api.session.sync.model import com.squareup.moshi.JsonClass @@ -21,7 +22,7 @@ import com.squareup.moshi.JsonClass * This class describes the device list response from a sync request */ @JsonClass(generateAdapter = true) -internal data class DeviceListResponse( +data class DeviceListResponse( // user ids list which have new crypto devices val changed: List = emptyList(), // List of user ids who are no more tracked. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/DeviceOneTimeKeysCountSyncResponse.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/DeviceOneTimeKeysCountSyncResponse.kt similarity index 87% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/DeviceOneTimeKeysCountSyncResponse.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/DeviceOneTimeKeysCountSyncResponse.kt index d5b435ac275..930cfb153ff 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/DeviceOneTimeKeysCountSyncResponse.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/DeviceOneTimeKeysCountSyncResponse.kt @@ -14,12 +14,12 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.session.sync.model +package org.matrix.android.sdk.api.session.sync.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) -internal data class DeviceOneTimeKeysCountSyncResponse( +data class DeviceOneTimeKeysCountSyncResponse( @Json(name = "signed_curve25519") val signedCurve25519: Int? = null ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/GroupSyncProfile.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/GroupSyncProfile.kt similarity index 91% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/GroupSyncProfile.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/GroupSyncProfile.kt index ee6aabb0a99..581e6824ee5 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/GroupSyncProfile.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/GroupSyncProfile.kt @@ -14,13 +14,13 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.session.sync.model +package org.matrix.android.sdk.api.session.sync.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) -internal data class GroupSyncProfile( +data class GroupSyncProfile( /** * The name of the group, if any. May be nil. */ diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/GroupsSyncResponse.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/GroupsSyncResponse.kt similarity index 92% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/GroupsSyncResponse.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/GroupsSyncResponse.kt index 4c2dce3ba81..fd8710bbda4 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/GroupsSyncResponse.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/GroupsSyncResponse.kt @@ -14,13 +14,13 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.session.sync.model +package org.matrix.android.sdk.api.session.sync.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) -internal data class GroupsSyncResponse( +data class GroupsSyncResponse( /** * Joined groups: An array of groups ids. */ diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/InvitedGroupSync.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/InvitedGroupSync.kt similarity index 90% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/InvitedGroupSync.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/InvitedGroupSync.kt index 148c2aeab92..d41df9f0f62 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/InvitedGroupSync.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/InvitedGroupSync.kt @@ -14,13 +14,13 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.session.sync.model +package org.matrix.android.sdk.api.session.sync.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) -internal data class InvitedGroupSync( +data class InvitedGroupSync( /** * The identifier of the inviter. */ diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/InvitedRoomSync.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/InvitedRoomSync.kt similarity index 93% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/InvitedRoomSync.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/InvitedRoomSync.kt index c21a73abc2a..dc63c5ba078 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/InvitedRoomSync.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/InvitedRoomSync.kt @@ -13,14 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.matrix.android.sdk.internal.session.sync.model + +package org.matrix.android.sdk.api.session.sync.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass // InvitedRoomSync represents a room invitation during server sync v2. @JsonClass(generateAdapter = true) -internal data class InvitedRoomSync( +data class InvitedRoomSync( /** * The state of a room that the user has been invited to. These state events may only have the 'sender', 'type', 'state_key' diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/LazyRoomSyncEphemeral.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/LazyRoomSyncEphemeral.kt similarity index 77% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/LazyRoomSyncEphemeral.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/LazyRoomSyncEphemeral.kt index 83006c646b2..087a5f52dc5 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/LazyRoomSyncEphemeral.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/LazyRoomSyncEphemeral.kt @@ -1,11 +1,11 @@ /* - * Copyright (c) 2021 The Matrix.org Foundation C.I.C. + * 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 + * 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, @@ -14,12 +14,12 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.session.sync.model +package org.matrix.android.sdk.api.session.sync.model import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = false) -internal sealed class LazyRoomSyncEphemeral { +sealed class LazyRoomSyncEphemeral { data class Parsed(val _roomSyncEphemeral: RoomSyncEphemeral) : LazyRoomSyncEphemeral() object Stored : LazyRoomSyncEphemeral() } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/PresenceSyncResponse.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/PresenceSyncResponse.kt similarity index 90% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/PresenceSyncResponse.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/PresenceSyncResponse.kt index 92d09aa4f50..d632552888f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/PresenceSyncResponse.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/PresenceSyncResponse.kt @@ -14,14 +14,14 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.session.sync.model +package org.matrix.android.sdk.api.session.sync.model import com.squareup.moshi.JsonClass import org.matrix.android.sdk.api.session.events.model.Event // PresenceSyncResponse represents the updates to the presence status of other users during server sync v2. @JsonClass(generateAdapter = true) -internal data class PresenceSyncResponse( +data class PresenceSyncResponse( /** * List of presence events (array of Event with type m.presence). diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/RoomInviteState.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/RoomInviteState.kt similarity index 91% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/RoomInviteState.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/RoomInviteState.kt index ded9e2a3508..59b4b4fc327 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/RoomInviteState.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/RoomInviteState.kt @@ -13,7 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.matrix.android.sdk.internal.session.sync.model + +package org.matrix.android.sdk.api.session.sync.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass @@ -21,7 +22,7 @@ import org.matrix.android.sdk.api.session.events.model.Event // RoomInviteState represents the state of a room that the user has been invited to. @JsonClass(generateAdapter = true) -internal data class RoomInviteState( +data class RoomInviteState( /** * List of state events (array of MXEvent). diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/RoomSync.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/RoomSync.kt similarity index 95% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/RoomSync.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/RoomSync.kt index 9aed0d37d68..e3d07602c79 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/RoomSync.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/RoomSync.kt @@ -13,14 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.matrix.android.sdk.internal.session.sync.model + +package org.matrix.android.sdk.api.session.sync.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass // RoomSync represents the response for a room during server sync v2. @JsonClass(generateAdapter = true) -internal data class RoomSync( +data class RoomSync( /** * The state updates for the room. */ diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/RoomSyncAccountData.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/RoomSyncAccountData.kt similarity index 90% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/RoomSyncAccountData.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/RoomSyncAccountData.kt index a2375507d8b..f2c4ed551c8 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/RoomSyncAccountData.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/RoomSyncAccountData.kt @@ -14,14 +14,14 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.session.sync.model +package org.matrix.android.sdk.api.session.sync.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass import org.matrix.android.sdk.api.session.events.model.Event @JsonClass(generateAdapter = true) -internal data class RoomSyncAccountData( +data class RoomSyncAccountData( /** * List of account data events (array of Event). */ diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/RoomSyncEphemeral.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/RoomSyncEphemeral.kt similarity index 91% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/RoomSyncEphemeral.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/RoomSyncEphemeral.kt index f2135db6b7f..f4d831c16ff 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/RoomSyncEphemeral.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/RoomSyncEphemeral.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.session.sync.model +package org.matrix.android.sdk.api.session.sync.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass @@ -22,7 +22,7 @@ import org.matrix.android.sdk.api.session.events.model.Event // RoomSyncEphemeral represents the ephemeral events in the room that aren't recorded in the timeline or state of the room (e.g. typing). @JsonClass(generateAdapter = true) -internal data class RoomSyncEphemeral( +data class RoomSyncEphemeral( /** * List of ephemeral events (array of Event). */ diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/RoomSyncState.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/RoomSyncState.kt similarity index 91% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/RoomSyncState.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/RoomSyncState.kt index f86f05d0003..78224675648 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/RoomSyncState.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/RoomSyncState.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.session.sync.model +package org.matrix.android.sdk.api.session.sync.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass @@ -22,7 +22,7 @@ import org.matrix.android.sdk.api.session.events.model.Event // RoomSyncState represents the state updates for a room during server sync v2. @JsonClass(generateAdapter = true) -internal data class RoomSyncState( +data class RoomSyncState( /** * List of state events (array of Event). The resulting state corresponds to the *start* of the timeline. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/RoomSyncSummary.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/RoomSyncSummary.kt similarity index 95% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/RoomSyncSummary.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/RoomSyncSummary.kt index 228a71ec281..7216a0c992f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/RoomSyncSummary.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/RoomSyncSummary.kt @@ -14,13 +14,13 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.session.sync.model +package org.matrix.android.sdk.api.session.sync.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) -internal data class RoomSyncSummary( +data class RoomSyncSummary( /** * Present only if the room has no m.room.name or m.room.canonical_alias. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/RoomSyncTimeline.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/RoomSyncTimeline.kt similarity index 93% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/RoomSyncTimeline.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/RoomSyncTimeline.kt index 27bbc4343f7..82d29a52e2f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/RoomSyncTimeline.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/RoomSyncTimeline.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.session.sync.model +package org.matrix.android.sdk.api.session.sync.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass @@ -22,7 +22,7 @@ import org.matrix.android.sdk.api.session.events.model.Event // RoomSyncTimeline represents the timeline of messages and state changes for a room during server sync v2. @JsonClass(generateAdapter = true) -internal data class RoomSyncTimeline( +data class RoomSyncTimeline( /** * List of events (array of Event). diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/RoomSyncUnreadNotifications.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/RoomSyncUnreadNotifications.kt similarity index 92% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/RoomSyncUnreadNotifications.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/RoomSyncUnreadNotifications.kt index f01534b884d..6618bceacd9 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/RoomSyncUnreadNotifications.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/RoomSyncUnreadNotifications.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.session.sync.model +package org.matrix.android.sdk.api.session.sync.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass @@ -24,7 +24,7 @@ import org.matrix.android.sdk.api.session.events.model.Event * `MXRoomSyncUnreadNotifications` represents the unread counts for a room. */ @JsonClass(generateAdapter = true) -internal data class RoomSyncUnreadNotifications( +data class RoomSyncUnreadNotifications( /** * List of account data events (array of Event). */ diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/RoomsSyncResponse.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/RoomsSyncResponse.kt similarity index 93% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/RoomsSyncResponse.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/RoomsSyncResponse.kt index dd2f96c9882..ff3ed542640 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/RoomsSyncResponse.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/RoomsSyncResponse.kt @@ -13,14 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.matrix.android.sdk.internal.session.sync.model + +package org.matrix.android.sdk.api.session.sync.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass // RoomsSyncResponse represents the rooms list in server sync v2 response. @JsonClass(generateAdapter = true) -internal data class RoomsSyncResponse( +data class RoomsSyncResponse( /** * Joined rooms: keys are rooms ids. */ diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/SyncResponse.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/SyncResponse.kt similarity index 92% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/SyncResponse.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/SyncResponse.kt index 55790d09792..d7dff72288f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/SyncResponse.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/SyncResponse.kt @@ -14,15 +14,14 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.session.sync.model +package org.matrix.android.sdk.api.session.sync.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass -import org.matrix.android.sdk.internal.session.sync.model.accountdata.UserAccountDataSync // SyncResponse represents the request response for server sync v2. @JsonClass(generateAdapter = true) -internal data class SyncResponse( +data class SyncResponse( /** * The user private data. */ diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/ToDeviceSyncResponse.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/ToDeviceSyncResponse.kt similarity index 90% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/ToDeviceSyncResponse.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/ToDeviceSyncResponse.kt index 8f3af56cde1..082460cc2d6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/ToDeviceSyncResponse.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/ToDeviceSyncResponse.kt @@ -14,14 +14,14 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.session.sync.model +package org.matrix.android.sdk.api.session.sync.model import com.squareup.moshi.JsonClass import org.matrix.android.sdk.api.session.events.model.Event // ToDeviceSyncResponse represents the data directly sent to one of user's devices. @JsonClass(generateAdapter = true) -internal data class ToDeviceSyncResponse( +data class ToDeviceSyncResponse( /** * List of direct-to-device events. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/accountdata/UserAccountDataSync.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/UserAccountDataSync.kt similarity index 88% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/accountdata/UserAccountDataSync.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/UserAccountDataSync.kt index 05b50ab2c5e..9e1b791919c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/model/accountdata/UserAccountDataSync.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/sync/model/UserAccountDataSync.kt @@ -14,13 +14,13 @@ * limitations under the License. */ -package org.matrix.android.sdk.internal.session.sync.model.accountdata +package org.matrix.android.sdk.api.session.sync.model import com.squareup.moshi.Json import com.squareup.moshi.JsonClass import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent @JsonClass(generateAdapter = true) -internal data class UserAccountDataSync( +data class UserAccountDataSync( @Json(name = "events") val list: List = emptyList() ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/user/model/User.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/user/model/User.kt index 7cd939a5c25..54ae9e54f6a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/user/model/User.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/user/model/User.kt @@ -23,13 +23,8 @@ package org.matrix.android.sdk.api.session.user.model data class User( val userId: String, /** - * For usage in UI, consider using [getBestName] + * For usage in UI, consider converting to MatrixItem and call getBestName() */ val displayName: String? = null, val avatarUrl: String? = null -) { - /** - * Return the display name or the user id - */ - fun getBestName() = displayName?.takeIf { it.isNotEmpty() } ?: userId -} +) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/util/MatrixItem.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/util/MatrixItem.kt index 3d2773fb4be..3396c4a6c98 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/util/MatrixItem.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/util/MatrixItem.kt @@ -34,8 +34,8 @@ sealed class MatrixItem( ) { data class UserItem(override val id: String, override val displayName: String? = null, - override val avatarUrl: String? = null) - : MatrixItem(id, displayName?.removeSuffix(ircPattern), avatarUrl) { + override val avatarUrl: String? = null) : + MatrixItem(id, displayName?.removeSuffix(ircPattern), avatarUrl) { init { if (BuildConfig.DEBUG) checkId() } @@ -45,8 +45,8 @@ sealed class MatrixItem( data class EventItem(override val id: String, override val displayName: String? = null, - override val avatarUrl: String? = null) - : MatrixItem(id, displayName, avatarUrl) { + override val avatarUrl: String? = null) : + MatrixItem(id, displayName, avatarUrl) { init { if (BuildConfig.DEBUG) checkId() } @@ -56,8 +56,8 @@ sealed class MatrixItem( data class RoomItem(override val id: String, override val displayName: String? = null, - override val avatarUrl: String? = null) - : MatrixItem(id, displayName, avatarUrl) { + override val avatarUrl: String? = null) : + MatrixItem(id, displayName, avatarUrl) { init { if (BuildConfig.DEBUG) checkId() } @@ -67,8 +67,8 @@ sealed class MatrixItem( data class SpaceItem(override val id: String, override val displayName: String? = null, - override val avatarUrl: String? = null) - : MatrixItem(id, displayName, avatarUrl) { + override val avatarUrl: String? = null) : + MatrixItem(id, displayName, avatarUrl) { init { if (BuildConfig.DEBUG) checkId() } @@ -78,36 +78,26 @@ sealed class MatrixItem( data class RoomAliasItem(override val id: String, override val displayName: String? = null, - override val avatarUrl: String? = null) - : MatrixItem(id, displayName, avatarUrl) { + override val avatarUrl: String? = null) : + MatrixItem(id, displayName, avatarUrl) { init { if (BuildConfig.DEBUG) checkId() } - // Best name is the id, and we keep the displayName of the room for the case we need the first letter - override fun getBestName() = id - override fun updateAvatar(newAvatar: String?) = copy(avatarUrl = newAvatar) } data class GroupItem(override val id: String, override val displayName: String? = null, - override val avatarUrl: String? = null) - : MatrixItem(id, displayName, avatarUrl) { + override val avatarUrl: String? = null) : + MatrixItem(id, displayName, avatarUrl) { init { if (BuildConfig.DEBUG) checkId() } - // Best name is the id, and we keep the displayName of the room for the case we need the first letter - override fun getBestName() = id - override fun updateAvatar(newAvatar: String?) = copy(avatarUrl = newAvatar) } - open fun getBestName(): String { - return displayName?.takeIf { it.isNotBlank() } ?: id - } - protected fun checkId() { if (!id.startsWith(getIdPrefix())) { error("Wrong usage of MatrixItem: check the id $id should start with ${getIdPrefix()}") diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt index e76dc28734a..641a8f1bb6e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/DefaultAuthenticationService.kt @@ -172,8 +172,8 @@ internal class DefaultAuthenticationService @Inject constructor( return try { getWellknownLoginFlowInternal(homeServerConnectionConfig) } catch (failure: Throwable) { - if (failure is Failure.OtherServerError - && failure.httpCode == HttpsURLConnection.HTTP_NOT_FOUND /* 404 */) { + if (failure is Failure.OtherServerError && + failure.httpCode == HttpsURLConnection.HTTP_NOT_FOUND /* 404 */) { // 404, no well-known data, try direct access to the API // First check the homeserver version return runCatching { @@ -190,8 +190,8 @@ internal class DefaultAuthenticationService @Inject constructor( it }, { - if (it is Failure.OtherServerError - && it.httpCode == HttpsURLConnection.HTTP_NOT_FOUND /* 404 */) { + if (it is Failure.OtherServerError && + it.httpCode == HttpsURLConnection.HTTP_NOT_FOUND /* 404 */) { // It's maybe a Web client url? getWebClientDomainLoginFlowInternal(homeServerConnectionConfig) } else { @@ -225,8 +225,8 @@ internal class DefaultAuthenticationService @Inject constructor( it }, { - if (it is Failure.OtherServerError - && it.httpCode == HttpsURLConnection.HTTP_NOT_FOUND /* 404 */) { + if (it is Failure.OtherServerError && + it.httpCode == HttpsURLConnection.HTTP_NOT_FOUND /* 404 */) { // Try with config.json getWebClientLoginFlowInternal(homeServerConnectionConfig) } else { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/IsValidClientServerApiTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/IsValidClientServerApiTask.kt index bc3d8870007..94b301649f2 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/IsValidClientServerApiTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/IsValidClientServerApiTask.kt @@ -54,8 +54,8 @@ internal class DefaultIsValidClientServerApiTask @Inject constructor( // We get a response, so the API is valid true } catch (failure: Throwable) { - if (failure is Failure.OtherServerError - && failure.httpCode == HttpsURLConnection.HTTP_NOT_FOUND /* 404 */) { + if (failure is Failure.OtherServerError && + failure.httpCode == HttpsURLConnection.HTTP_NOT_FOUND /* 404 */) { // Probably not valid false } else { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/RealmPendingSessionStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/RealmPendingSessionStore.kt index 968ae22eda7..13f26e321da 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/RealmPendingSessionStore.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/RealmPendingSessionStore.kt @@ -16,11 +16,11 @@ package org.matrix.android.sdk.internal.auth.db +import io.realm.Realm +import io.realm.RealmConfiguration import org.matrix.android.sdk.internal.auth.PendingSessionStore import org.matrix.android.sdk.internal.database.awaitTransaction import org.matrix.android.sdk.internal.di.AuthDatabase -import io.realm.Realm -import io.realm.RealmConfiguration import javax.inject.Inject internal class RealmPendingSessionStore @Inject constructor(private val mapper: PendingSessionMapper, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/RealmSessionParamsStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/RealmSessionParamsStore.kt index edd3e2bed57..235ef6b7092 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/RealmSessionParamsStore.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/db/RealmSessionParamsStore.kt @@ -16,15 +16,15 @@ package org.matrix.android.sdk.internal.auth.db +import io.realm.Realm +import io.realm.RealmConfiguration +import io.realm.exceptions.RealmPrimaryKeyConstraintException import org.matrix.android.sdk.api.auth.data.Credentials import org.matrix.android.sdk.api.auth.data.SessionParams import org.matrix.android.sdk.api.auth.data.sessionId import org.matrix.android.sdk.internal.auth.SessionParamsStore import org.matrix.android.sdk.internal.database.awaitTransaction import org.matrix.android.sdk.internal.di.AuthDatabase -import io.realm.Realm -import io.realm.RealmConfiguration -import io.realm.exceptions.RealmPrimaryKeyConstraintException import timber.log.Timber import javax.inject.Inject diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/DirectLoginTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/DirectLoginTask.kt index 3888633723b..8f61afe3742 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/DirectLoginTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/login/DirectLoginTask.kt @@ -17,6 +17,7 @@ package org.matrix.android.sdk.internal.auth.login import dagger.Lazy +import okhttp3.OkHttpClient import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.session.Session @@ -29,7 +30,6 @@ import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.network.httpclient.addSocketFactory import org.matrix.android.sdk.internal.network.ssl.UnrecognizedCertificateException import org.matrix.android.sdk.internal.task.Task -import okhttp3.OkHttpClient import javax.inject.Inject internal interface DirectLoginTask : Task { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/version/Versions.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/version/Versions.kt index 4e599516ed2..74cb3de2acf 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/version/Versions.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/auth/version/Versions.kt @@ -63,9 +63,9 @@ internal fun Versions.isSupportedBySdk(): Boolean { * Return true if the SDK supports this homeserver version for login and registration */ internal fun Versions.isLoginAndRegistrationSupportedBySdk(): Boolean { - return !doesServerRequireIdentityServerParam() - && doesServerAcceptIdentityAccessToken() - && doesServerSeparatesAddAndBind() + return !doesServerRequireIdentityServerParam() && + doesServerAcceptIdentityAccessToken() && + doesServerSeparatesAddAndBind() } /** @@ -74,8 +74,8 @@ internal fun Versions.isLoginAndRegistrationSupportedBySdk(): Boolean { * @return true if the server support the lazy loading of room members */ private fun Versions.supportLazyLoadMembers(): Boolean { - return getMaxVersion() >= HomeServerVersion.r0_5_0 - || unstableFeatures?.get(FEATURE_LAZY_LOAD_MEMBERS) == true + return getMaxVersion() >= HomeServerVersion.r0_5_0 || + unstableFeatures?.get(FEATURE_LAZY_LOAD_MEMBERS) == true } /** @@ -92,13 +92,13 @@ private fun Versions.doesServerRequireIdentityServerParam(): Boolean { * Some homeservers may trigger errors if they are not prepared for the new parameter. */ private fun Versions.doesServerAcceptIdentityAccessToken(): Boolean { - return getMaxVersion() >= HomeServerVersion.r0_6_0 - || unstableFeatures?.get(FEATURE_ID_ACCESS_TOKEN) ?: false + return getMaxVersion() >= HomeServerVersion.r0_6_0 || + unstableFeatures?.get(FEATURE_ID_ACCESS_TOKEN) ?: false } private fun Versions.doesServerSeparatesAddAndBind(): Boolean { - return getMaxVersion() >= HomeServerVersion.r0_6_0 - || unstableFeatures?.get(FEATURE_SEPARATE_ADD_AND_BIND) ?: false + return getMaxVersion() >= HomeServerVersion.r0_6_0 || + unstableFeatures?.get(FEATURE_SEPARATE_ADD_AND_BIND) ?: false } private fun Versions.getMaxVersion(): HomeServerVersion { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/CancelGossipRequestWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/CancelGossipRequestWorker.kt index 0ec020bc461..c11d00278b0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/CancelGossipRequestWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/CancelGossipRequestWorker.kt @@ -35,8 +35,8 @@ import org.matrix.android.sdk.internal.worker.SessionWorkerParams import javax.inject.Inject internal class CancelGossipRequestWorker(context: Context, - params: WorkerParameters) - : SessionSafeCoroutineWorker(context, params, Params::class.java) { + params: WorkerParameters) : + SessionSafeCoroutineWorker(context, params, Params::class.java) { @JsonClass(generateAdapter = true) internal data class Params( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/CryptoModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/CryptoModule.kt index 84d4fef5afd..fe388b44e22 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/CryptoModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/CryptoModule.kt @@ -19,6 +19,9 @@ package org.matrix.android.sdk.internal.crypto import dagger.Binds import dagger.Module import dagger.Provides +import io.realm.RealmConfiguration +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.SupervisorJob import org.matrix.android.sdk.api.session.crypto.CryptoService import org.matrix.android.sdk.api.session.crypto.crosssigning.CrossSigningService import org.matrix.android.sdk.internal.crypto.api.CryptoApi @@ -93,9 +96,6 @@ import org.matrix.android.sdk.internal.di.UserMd5 import org.matrix.android.sdk.internal.session.SessionScope import org.matrix.android.sdk.internal.session.cache.ClearCacheTask import org.matrix.android.sdk.internal.session.cache.RealmClearCacheTask -import io.realm.RealmConfiguration -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.SupervisorJob import retrofit2.Retrofit import java.io.File diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt index 67d8ba4515c..dbcd5bdd58b 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt @@ -51,6 +51,7 @@ import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibilityContent import org.matrix.android.sdk.api.session.room.model.RoomMemberContent +import org.matrix.android.sdk.api.session.sync.model.SyncResponse import org.matrix.android.sdk.internal.crypto.actions.MegolmSessionDataImporter import org.matrix.android.sdk.internal.crypto.actions.SetDeviceVerificationAction import org.matrix.android.sdk.internal.crypto.algorithms.IMXEncrypting @@ -89,7 +90,6 @@ import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.extensions.foldToCallback import org.matrix.android.sdk.internal.session.SessionScope import org.matrix.android.sdk.internal.session.room.membership.LoadRoomMembersTask -import org.matrix.android.sdk.internal.session.sync.model.SyncResponse import org.matrix.android.sdk.internal.task.TaskExecutor import org.matrix.android.sdk.internal.task.TaskThread import org.matrix.android.sdk.internal.task.configureWith @@ -880,8 +880,8 @@ internal class DefaultCryptoService @Inject constructor( } private fun getRoomUserIds(roomId: String): List { - val encryptForInvitedMembers = isEncryptionEnabledForInvitedUser() - && shouldEncryptForInvitedMembers(roomId) + val encryptForInvitedMembers = isEncryptionEnabledForInvitedUser() && + shouldEncryptForInvitedMembers(roomId) return cryptoSessionInfoProvider.getRoomUserIds(roomId, encryptForInvitedMembers) } @@ -899,9 +899,9 @@ internal class DefaultCryptoService @Inject constructor( if (membership == Membership.JOIN) { // make sure we are tracking the deviceList for this user. deviceListManager.startTrackingDeviceList(listOf(userId)) - } else if (membership == Membership.INVITE - && shouldEncryptForInvitedMembers(roomId) - && isEncryptionEnabledForInvitedUser()) { + } else if (membership == Membership.INVITE && + shouldEncryptForInvitedMembers(roomId) && + isEncryptionEnabledForInvitedUser()) { // track the deviceList for this invited user. // Caution: there's a big edge case here in that federated servers do not // know what other servers are in the room at the time they've been invited. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DeviceListManager.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DeviceListManager.kt index 79910c6de29..8a91376b607 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DeviceListManager.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DeviceListManager.kt @@ -475,8 +475,8 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM } if (!isVerified) { - Timber.e("## CRYPTO | validateDeviceKeys() : Unable to verify signature on device " + userId + ":" - + deviceKeys.deviceId + " with error " + errorMessage) + Timber.e("## CRYPTO | validateDeviceKeys() : Unable to verify signature on device " + userId + ":" + + deviceKeys.deviceId + " with error " + errorMessage) return false } @@ -486,9 +486,9 @@ internal class DeviceListManager @Inject constructor(private val cryptoStore: IM // best off sticking with the original keys. // // Should we warn the user about it somehow? - Timber.e("## CRYPTO | validateDeviceKeys() : WARNING:Ed25519 key for device " + userId + ":" - + deviceKeys.deviceId + " has changed : " - + previouslyStoredDeviceKeys.fingerprint() + " -> " + signKey) + Timber.e("## CRYPTO | validateDeviceKeys() : WARNING:Ed25519 key for device " + userId + ":" + + deviceKeys.deviceId + " has changed : " + + previouslyStoredDeviceKeys.fingerprint() + " -> " + signKey) Timber.e("## CRYPTO | validateDeviceKeys() : $previouslyStoredDeviceKeys -> $deviceKeys") Timber.e("## CRYPTO | validateDeviceKeys() : ${previouslyStoredDeviceKeys.keys} -> ${deviceKeys.keys}") diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/EventDecryptor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/EventDecryptor.kt index 8d86380e39e..fe17dd08e47 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/EventDecryptor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/EventDecryptor.kt @@ -107,8 +107,8 @@ internal class EventDecryptor @Inject constructor( } catch (mxCryptoError: MXCryptoError) { Timber.v("## CRYPTO | internalDecryptEvent : Failed to decrypt ${event.eventId} reason: $mxCryptoError") if (algorithm == MXCRYPTO_ALGORITHM_OLM) { - if (mxCryptoError is MXCryptoError.Base - && mxCryptoError.errorType == MXCryptoError.ErrorType.BAD_ENCRYPTED_MESSAGE) { + if (mxCryptoError is MXCryptoError.Base && + mxCryptoError.errorType == MXCryptoError.ErrorType.BAD_ENCRYPTED_MESSAGE) { // need to find sending device cryptoCoroutineScope.launch(coroutineDispatchers.crypto) { val olmContent = event.content.toModel() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/InboundGroupSessionStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/InboundGroupSessionStore.kt index 06c667ee4ad..3825a5dab2e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/InboundGroupSessionStore.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/InboundGroupSessionStore.kt @@ -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 diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MXOlmDevice.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MXOlmDevice.kt index b3c2cfae66c..6479a8ddced 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MXOlmDevice.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/MXOlmDevice.kt @@ -595,7 +595,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 } @@ -721,7 +722,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(JSON_DICT_PARAMETERIZED_TYPE) val payloadString = convertFromUTF8(decryptResult.mDecryptedMessage) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/OutgoingGossipingRequestManager.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/OutgoingGossipingRequestManager.kt index ccdb5ab1376..6fc71036688 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/OutgoingGossipingRequestManager.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/OutgoingGossipingRequestManager.kt @@ -16,17 +16,17 @@ package org.matrix.android.sdk.internal.crypto +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch import org.matrix.android.sdk.internal.crypto.model.rest.RoomKeyRequestBody import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore +import org.matrix.android.sdk.internal.crypto.tasks.createUniqueTxnId +import org.matrix.android.sdk.internal.crypto.util.RequestIdHelper import org.matrix.android.sdk.internal.di.SessionId import org.matrix.android.sdk.internal.session.SessionScope import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers import org.matrix.android.sdk.internal.worker.WorkerParamsFactory -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import org.matrix.android.sdk.internal.crypto.tasks.createUniqueTxnId -import org.matrix.android.sdk.internal.crypto.util.RequestIdHelper import timber.log.Timber import javax.inject.Inject @@ -112,9 +112,8 @@ internal class OutgoingGossipingRequestManager @Inject constructor( * @param andResend true to resend the key request */ private fun cancelRoomKeyRequest(requestBody: RoomKeyRequestBody, andResend: Boolean) { - val req = cryptoStore.getOutgoingRoomKeyRequest(requestBody) - ?: // no request was made for this key - return Unit.also { + val req = cryptoStore.getOutgoingRoomKeyRequest(requestBody) // no request was made for this key + ?: return Unit.also { Timber.v("## CRYPTO - GOSSIP cancelRoomKeyRequest() Unknown request $requestBody") } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipRequestWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipRequestWorker.kt index 2e26720abb2..b2ba189b65e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipRequestWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipRequestWorker.kt @@ -38,8 +38,8 @@ import timber.log.Timber import javax.inject.Inject internal class SendGossipRequestWorker(context: Context, - params: WorkerParameters) - : SessionSafeCoroutineWorker(context, params, Params::class.java) { + params: WorkerParameters) : + SessionSafeCoroutineWorker(context, params, Params::class.java) { @JsonClass(generateAdapter = true) internal data class Params( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipWorker.kt index c5c6d26f791..b96943e4aef 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/SendGossipWorker.kt @@ -38,8 +38,8 @@ import timber.log.Timber import javax.inject.Inject internal class SendGossipWorker(context: Context, - params: WorkerParameters) - : SessionSafeCoroutineWorker(context, params, Params::class.java) { + params: WorkerParameters) : + SessionSafeCoroutineWorker(context, params, Params::class.java) { @JsonClass(generateAdapter = true) internal data class Params( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/actions/EnsureOlmSessionsForDevicesAction.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/actions/EnsureOlmSessionsForDevicesAction.kt index 95b99c54e86..52876b0fff8 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/actions/EnsureOlmSessionsForDevicesAction.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/actions/EnsureOlmSessionsForDevicesAction.kt @@ -90,8 +90,8 @@ internal class EnsureOlmSessionsForDevicesAction @Inject constructor( oneTimeKey = key } if (oneTimeKey == null) { - Timber.w("## CRYPTO | ensureOlmSessionsForDevices() : No one-time keys " + oneTimeKeyAlgorithm - + " for device " + userId + " : " + deviceId) + Timber.w("## CRYPTO | ensureOlmSessionsForDevices() : No one-time keys " + oneTimeKeyAlgorithm + + " for device " + userId + " : " + deviceId) continue } // Update the result for this device in results @@ -126,15 +126,15 @@ internal class EnsureOlmSessionsForDevicesAction @Inject constructor( sessionId = olmDevice.createOutboundSession(deviceInfo.identityKey()!!, oneTimeKey.value) if (!sessionId.isNullOrEmpty()) { - Timber.v("## CRYPTO | verifyKeyAndStartSession() : Started new sessionid " + sessionId - + " for device " + deviceInfo + "(theirOneTimeKey: " + oneTimeKey.value + ")") + Timber.v("## CRYPTO | verifyKeyAndStartSession() : Started new sessionid " + sessionId + + " for device " + deviceInfo + "(theirOneTimeKey: " + oneTimeKey.value + ")") } else { // Possibly a bad key Timber.e("## CRYPTO | verifyKeyAndStartSession() : Error starting session with device $userId:$deviceId") } } else { - Timber.e("## CRYPTO | verifyKeyAndStartSession() : Unable to verify signature on one-time key for device " + userId - + ":" + deviceId + " Error " + errorMessage) + Timber.e("## CRYPTO | verifyKeyAndStartSession() : Unable to verify signature on one-time key for device " + userId + + ":" + deviceId + " Error " + errorMessage) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/actions/EnsureOlmSessionsForUsersAction.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/actions/EnsureOlmSessionsForUsersAction.kt index a276394eaf5..a3cfbd91f00 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/actions/EnsureOlmSessionsForUsersAction.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/actions/EnsureOlmSessionsForUsersAction.kt @@ -38,9 +38,9 @@ internal class EnsureOlmSessionsForUsersAction @Inject constructor(private val o devices.filter { // Don't bother setting up session to ourself - it.identityKey() != olmDevice.deviceCurve25519Key + it.identityKey() != olmDevice.deviceCurve25519Key && // Don't bother setting up sessions with blocked users - && !(it.trustLevel?.isVerified() ?: false) + !(it.trustLevel?.isVerified() ?: false) } } return ensureOlmSessionsForDevicesAction.handle(devicesByUser) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt index 70d20222993..d2f6bd0382b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt @@ -16,6 +16,8 @@ package org.matrix.android.sdk.internal.crypto.algorithms.megolm +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.crypto.MXCryptoError import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.EventType @@ -40,8 +42,6 @@ import org.matrix.android.sdk.internal.crypto.model.rest.RoomKeyRequestBody import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch import timber.log.Timber internal class MXMegolmDecryption(private val userId: String, @@ -82,9 +82,9 @@ internal class MXMegolmDecryption(private val userId: String, val encryptedEventContent = event.content.toModel() ?: throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON) - if (encryptedEventContent.senderKey.isNullOrBlank() - || encryptedEventContent.sessionId.isNullOrBlank() - || encryptedEventContent.ciphertext.isNullOrBlank()) { + if (encryptedEventContent.senderKey.isNullOrBlank() || + encryptedEventContent.sessionId.isNullOrBlank() || + encryptedEventContent.ciphertext.isNullOrBlank()) { throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryptionFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryptionFactory.kt index 95a4342dbf4..91640523fc0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryptionFactory.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryptionFactory.kt @@ -16,6 +16,7 @@ package org.matrix.android.sdk.internal.crypto.algorithms.megolm +import kotlinx.coroutines.CoroutineScope import org.matrix.android.sdk.internal.crypto.DeviceListManager import org.matrix.android.sdk.internal.crypto.MXOlmDevice import org.matrix.android.sdk.internal.crypto.OutgoingGossipingRequestManager @@ -25,7 +26,6 @@ import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers -import kotlinx.coroutines.CoroutineScope import javax.inject.Inject internal class MXMegolmDecryptionFactory @Inject constructor( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt index a7564444759..63fe678229f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt @@ -155,11 +155,11 @@ internal class MXMegolmEncryption( private suspend fun ensureOutboundSession(devicesInRoom: MXUsersDevicesMap): MXOutboundSessionInfo { Timber.v("## CRYPTO | ensureOutboundSession start") var session = outboundSession - if (session == null + if (session == null || // Need to make a brand new session? - || session.needsRotation(sessionRotationPeriodMsgs, sessionRotationPeriodMs) + session.needsRotation(sessionRotationPeriodMsgs, sessionRotationPeriodMs) || // Determine if we have shared with anyone we shouldn't have - || session.sharedWithTooManyDevices(devicesInRoom)) { + session.sharedWithTooManyDevices(devicesInRoom)) { session = prepareNewSessionInRoom() outboundSession = session } @@ -170,7 +170,7 @@ internal class MXMegolmEncryption( val deviceIds = devicesInRoom.getUserDeviceIds(userId) for (deviceId in deviceIds!!) { val deviceInfo = devicesInRoom.getObject(userId, deviceId) - if (deviceInfo != null && !cryptoStore.getSharedSessionInfo(roomId, safeSession.sessionId, userId, deviceId).found) { + if (deviceInfo != null && !cryptoStore.getSharedSessionInfo(roomId, safeSession.sessionId, deviceInfo).found) { val devices = shareMap.getOrPut(userId) { ArrayList() } devices.add(deviceInfo) } @@ -270,8 +270,8 @@ internal class MXMegolmEncryption( // for dead devices on every message. val gossipingEventBuffer = arrayListOf() for ((userId, devicesToShareWith) in devicesByUser) { - for ((deviceId) in devicesToShareWith) { - session.sharedWithHelper.markedSessionAsShared(userId, deviceId, chainIndex) + for (deviceInfo in devicesToShareWith) { + session.sharedWithHelper.markedSessionAsShared(deviceInfo, chainIndex) gossipingEventBuffer.add( Event( type = EventType.ROOM_KEY, @@ -279,7 +279,7 @@ internal class MXMegolmEncryption( content = submap.apply { this["session_key"] = "" // we add a fake key for trail - this["_dest"] = "$userId|$deviceId" + this["_dest"] = "$userId|${deviceInfo.deviceId}" } )) } @@ -380,8 +380,8 @@ internal class MXMegolmEncryption( // with them, which means that they will have announced any new devices via // an m.new_device. val keys = deviceListManager.downloadKeys(userIds, false) - val encryptToVerifiedDevicesOnly = cryptoStore.getGlobalBlacklistUnverifiedDevices() - || cryptoStore.getRoomsListBlacklistUnverifiedDevices().contains(roomId) + val encryptToVerifiedDevicesOnly = cryptoStore.getGlobalBlacklistUnverifiedDevices() || + cryptoStore.getRoomsListBlacklistUnverifiedDevices().contains(roomId) val devicesInRoom = DeviceInRoomInfo() val unknownDevices = MXUsersDevicesMap() @@ -429,7 +429,7 @@ internal class MXMegolmEncryption( .also { Timber.w("## Crypto reshareKey: Device not found") } // Get the chain index of the key we previously sent this device - val wasSessionSharedWithUser = cryptoStore.getSharedSessionInfo(roomId, sessionId, userId, deviceId) + val wasSessionSharedWithUser = cryptoStore.getSharedSessionInfo(roomId, sessionId, deviceInfo) if (!wasSessionSharedWithUser.found) { // This session was never shared with this user // Send a room key with held @@ -446,10 +446,9 @@ internal class MXMegolmEncryption( val devicesByUser = mapOf(userId to listOf(deviceInfo)) val usersDeviceMap = ensureOlmSessionsForDevicesAction.handle(devicesByUser) val olmSessionResult = usersDeviceMap.getObject(userId, deviceId) - olmSessionResult?.sessionId - ?: // no session with this device, probably because there were no one-time keys. + olmSessionResult?.sessionId // no session with this device, probably because there were no one-time keys. // ensureOlmSessionsForDevicesAction has already done the logging, so just skip it. - return false.also { + ?: return false.also { Timber.w("## Crypto reshareKey: no session with this device, probably because there were no one-time keys") } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/SharedWithHelper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/SharedWithHelper.kt index f17168a6d22..a64e5af0d33 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/SharedWithHelper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/SharedWithHelper.kt @@ -16,6 +16,7 @@ package org.matrix.android.sdk.internal.crypto.algorithms.megolm +import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore @@ -28,7 +29,13 @@ internal class SharedWithHelper( return cryptoStore.getSharedWithInfo(roomId, sessionId) } - fun markedSessionAsShared(userId: String, deviceId: String, chainIndex: Int) { - cryptoStore.markedSessionAsShared(roomId, sessionId, userId, deviceId, chainIndex) + fun markedSessionAsShared(deviceInfo: CryptoDeviceInfo, chainIndex: Int) { + cryptoStore.markedSessionAsShared( + roomId = roomId, + sessionId = sessionId, + userId = deviceInfo.userId, + deviceId = deviceInfo.deviceId, + deviceIdentityKey = deviceInfo.identityKey() ?: "", + chainIndex = chainIndex) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/olm/MXOlmDecryption.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/olm/MXOlmDecryption.kt index 082b86c9da6..f1bca4fbc6b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/olm/MXOlmDecryption.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/olm/MXOlmDecryption.kt @@ -34,8 +34,8 @@ internal class MXOlmDecryption( // The olm device interface private val olmDevice: MXOlmDevice, // the matrix userId - private val userId: String) - : IMXDecrypting { + private val userId: String) : + IMXDecrypting { @Throws(MXCryptoError::class) override fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/olm/MXOlmEncryption.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/olm/MXOlmEncryption.kt index 65f78e11f02..63f2533ac32 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/olm/MXOlmEncryption.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/olm/MXOlmEncryption.kt @@ -32,8 +32,8 @@ internal class MXOlmEncryption( private val cryptoStore: IMXCryptoStore, private val messageEncrypter: MessageEncrypter, private val deviceListManager: DeviceListManager, - private val ensureOlmSessionsForUsersAction: EnsureOlmSessionsForUsersAction) - : IMXEncrypting { + private val ensureOlmSessionsForUsersAction: EnsureOlmSessionsForUsersAction) : + IMXEncrypting { override suspend fun encryptEventContent(eventContent: Content, eventType: String, userIds: List): Content { // pick the list of recipients based on the membership list. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/attachments/ElementToDecrypt.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/attachments/ElementToDecrypt.kt index c071384df46..3d00e178a0c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/attachments/ElementToDecrypt.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/attachments/ElementToDecrypt.kt @@ -17,8 +17,8 @@ package org.matrix.android.sdk.internal.crypto.attachments import android.os.Parcelable -import org.matrix.android.sdk.internal.crypto.model.rest.EncryptedFileInfo import kotlinx.parcelize.Parcelize +import org.matrix.android.sdk.internal.crypto.model.rest.EncryptedFileInfo fun EncryptedFileInfo.toElementToDecrypt(): ElementToDecrypt? { // Check the validity of some fields diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/ComputeTrustTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/ComputeTrustTask.kt index ee5aab977b0..113255bb7ea 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/ComputeTrustTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/ComputeTrustTask.kt @@ -15,6 +15,7 @@ */ package org.matrix.android.sdk.internal.crypto.crosssigning +import kotlinx.coroutines.withContext import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo @@ -22,7 +23,6 @@ import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.task.Task import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers -import kotlinx.coroutines.withContext import javax.inject.Inject internal interface ComputeTrustTask : Task { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/DefaultCrossSigningService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/DefaultCrossSigningService.kt index 0289fadbd8f..8a851b12674 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/DefaultCrossSigningService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/DefaultCrossSigningService.kt @@ -529,13 +529,13 @@ internal class DefaultCrossSigningService @Inject constructor( } override fun canCrossSign(): Boolean { - return checkSelfTrust().isVerified() && cryptoStore.getCrossSigningPrivateKeys()?.selfSigned != null - && cryptoStore.getCrossSigningPrivateKeys()?.user != null + return checkSelfTrust().isVerified() && cryptoStore.getCrossSigningPrivateKeys()?.selfSigned != null && + cryptoStore.getCrossSigningPrivateKeys()?.user != null } override fun allPrivateKeysKnown(): Boolean { - return checkSelfTrust().isVerified() - && cryptoStore.getCrossSigningPrivateKeys()?.allKnown().orFalse() + return checkSelfTrust().isVerified() && + cryptoStore.getCrossSigningPrivateKeys()?.allKnown().orFalse() } override fun trustUser(otherUserId: String, callback: MatrixCallback) { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/UpdateTrustWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/UpdateTrustWorker.kt index 76b63b7798e..3326d3707a5 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/UpdateTrustWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/UpdateTrustWorker.kt @@ -51,8 +51,8 @@ import timber.log.Timber import javax.inject.Inject internal class UpdateTrustWorker(context: Context, - params: WorkerParameters) - : SessionSafeCoroutineWorker(context, params, Params::class.java) { + params: WorkerParameters) : + SessionSafeCoroutineWorker(context, params, Params::class.java) { @JsonClass(generateAdapter = true) internal data class Params( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt index fbcf5cfdeb2..c6e2c1217f8 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/DefaultKeysBackupService.kt @@ -21,6 +21,10 @@ import android.os.Looper import androidx.annotation.UiThread import androidx.annotation.VisibleForTesting import androidx.annotation.WorkerThread +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.auth.data.Credentials import org.matrix.android.sdk.api.failure.Failure @@ -40,6 +44,7 @@ import org.matrix.android.sdk.internal.crypto.keysbackup.model.KeysBackupVersion import org.matrix.android.sdk.internal.crypto.keysbackup.model.KeysBackupVersionTrustSignature import org.matrix.android.sdk.internal.crypto.keysbackup.model.MegolmBackupAuthData import org.matrix.android.sdk.internal.crypto.keysbackup.model.MegolmBackupCreationInfo +import org.matrix.android.sdk.internal.crypto.keysbackup.model.SignalableMegolmBackupAuthData import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.BackupKeysResult import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.CreateKeysBackupVersionBody import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeyBackupData @@ -80,11 +85,6 @@ import org.matrix.android.sdk.internal.task.configureWith import org.matrix.android.sdk.internal.util.JsonCanonicalizer import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers import org.matrix.android.sdk.internal.util.awaitCallback -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import org.matrix.android.sdk.internal.crypto.keysbackup.model.SignalableMegolmBackupAuthData import org.matrix.olm.OlmException import org.matrix.olm.OlmPkDecryption import org.matrix.olm.OlmPkEncryption @@ -860,8 +860,8 @@ internal class DefaultKeysBackupService @Inject constructor( } override fun onFailure(failure: Throwable) { - if (failure is Failure.ServerError - && failure.error.code == MatrixError.M_NOT_FOUND) { + if (failure is Failure.ServerError && + failure.error.code == MatrixError.M_NOT_FOUND) { // Workaround because the homeserver currently returns M_NOT_FOUND when there is no key backup callback.onSuccess(null) } else { @@ -883,8 +883,8 @@ internal class DefaultKeysBackupService @Inject constructor( } override fun onFailure(failure: Throwable) { - if (failure is Failure.ServerError - && failure.error.code == MatrixError.M_NOT_FOUND) { + if (failure is Failure.ServerError && + failure.error.code == MatrixError.M_NOT_FOUND) { // Workaround because the homeserver currently returns M_NOT_FOUND when there is no key backup callback.onSuccess(null) } else { @@ -1042,8 +1042,8 @@ internal class DefaultKeysBackupService @Inject constructor( return null } - if (authData.privateKeySalt.isNullOrBlank() - || authData.privateKeyIterations == null) { + if (authData.privateKeySalt.isNullOrBlank() || + authData.privateKeyIterations == null) { Timber.w("recoveryKeyFromPassword: Salt and/or iterations not found in key backup auth data") return null diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupStateManager.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupStateManager.kt index 7c0c741a2cf..78ef958bbff 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupStateManager.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupStateManager.kt @@ -44,16 +44,16 @@ internal class KeysBackupStateManager(private val uiHandler: Handler) { } val isEnabled: Boolean - get() = state == KeysBackupState.ReadyToBackUp - || state == KeysBackupState.WillBackUp - || state == KeysBackupState.BackingUp + get() = state == KeysBackupState.ReadyToBackUp || + state == KeysBackupState.WillBackUp || + state == KeysBackupState.BackingUp // True if unknown or bad state val isStucked: Boolean - get() = state == KeysBackupState.Unknown - || state == KeysBackupState.Disabled - || state == KeysBackupState.WrongBackUpVersion - || state == KeysBackupState.NotTrusted + get() = state == KeysBackupState.Unknown || + state == KeysBackupState.Disabled || + state == KeysBackupState.WrongBackUpVersion || + state == KeysBackupState.NotTrusted fun addListener(listener: KeysBackupStateListener) { synchronized(listeners) { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/secrets/DefaultSharedSecretStorageService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/secrets/DefaultSharedSecretStorageService.kt index fb10cf4482b..ad3bc012dfb 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/secrets/DefaultSharedSecretStorageService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/secrets/DefaultSharedSecretStorageService.kt @@ -16,6 +16,8 @@ package org.matrix.android.sdk.internal.crypto.secrets +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.withContext import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.listeners.ProgressListener import org.matrix.android.sdk.api.session.accountdata.SessionAccountDataService @@ -43,8 +45,6 @@ import org.matrix.android.sdk.internal.crypto.tools.HkdfSha256 import org.matrix.android.sdk.internal.crypto.tools.withOlmDecryption import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.withContext import org.matrix.olm.OlmPkMessage import java.security.SecureRandom import javax.crypto.Cipher @@ -359,8 +359,8 @@ internal class DefaultSharedSecretStorageService @Inject constructor( val keyInfo = (keyInfoResult as? KeyInfoResult.Success)?.keyInfo ?: return IntegrityResult.Error(SharedSecretStorageError.UnknownKey(keyId ?: "")) - if (keyInfo.content.algorithm != SSSS_ALGORITHM_AES_HMAC_SHA2 - && keyInfo.content.algorithm != SSSS_ALGORITHM_CURVE25519_AES_SHA2) { + if (keyInfo.content.algorithm != SSSS_ALGORITHM_AES_HMAC_SHA2 && + keyInfo.content.algorithm != SSSS_ALGORITHM_CURVE25519_AES_SHA2) { // Unsupported algorithm return IntegrityResult.Error( SharedSecretStorageError.UnsupportedAlgorithm(keyInfo.content.algorithm ?: "") diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt index 3d12e74fcdd..238d06738cc 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt @@ -450,7 +450,8 @@ internal interface IMXCryptoStore { fun addWithHeldMegolmSession(withHeldContent: RoomKeyWithHeldContent) fun getWithHeldMegolmSession(roomId: String, sessionId: String): RoomKeyWithHeldContent? - fun markedSessionAsShared(roomId: String?, sessionId: String, userId: String, deviceId: String, chainIndex: Int) + fun markedSessionAsShared(roomId: String?, sessionId: String, userId: String, deviceId: String, + deviceIdentityKey: String, chainIndex: Int) /** * Query for information on this session sharing history. @@ -459,7 +460,7 @@ internal interface IMXCryptoStore { * in this case chainIndex is not nullindicates the ratchet position. * In found is false, chainIndex is null */ - fun getSharedSessionInfo(roomId: String?, sessionId: String, userId: String, deviceId: String): SharedSessionResult + fun getSharedSessionInfo(roomId: String?, sessionId: String, deviceInfo: CryptoDeviceInfo): SharedSessionResult data class SharedSessionResult(val found: Boolean, val chainIndex: Int?) fun getSharedWithInfo(roomId: String?, sessionId: String): MXUsersDevicesMap diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt index 3c8353e83e3..3c8f74d419e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt @@ -152,8 +152,8 @@ internal class RealmCryptoStore @Inject constructor( // Check credentials // The device id may not have been provided in credentials. // Check it only if provided, else trust the stored one. - if (currentMetadata.userId != userId - || (deviceId != null && deviceId != currentMetadata.deviceId)) { + if (currentMetadata.userId != userId || + (deviceId != null && deviceId != currentMetadata.deviceId)) { Timber.w("## open() : Credentials do not match, close this store and delete data") deleteAll = true currentMetadata = null @@ -178,9 +178,9 @@ internal class RealmCryptoStore @Inject constructor( override fun hasData(): Boolean { return doWithRealm(realmConfiguration) { - !it.isEmpty + !it.isEmpty && // Check if there is a MetaData object - && it.where().count() > 0 + it.where().count() > 0 } } @@ -1025,10 +1025,10 @@ internal class RealmCryptoStore @Inject constructor( }.mapNotNull { it.toOutgoingGossipingRequest() as? OutgoingRoomKeyRequest }.firstOrNull { - it.requestBody?.algorithm == requestBody.algorithm - && it.requestBody?.roomId == requestBody.roomId - && it.requestBody?.senderKey == requestBody.senderKey - && it.requestBody?.sessionId == requestBody.sessionId + it.requestBody?.algorithm == requestBody.algorithm && + it.requestBody?.roomId == requestBody.roomId && + it.requestBody?.senderKey == requestBody.senderKey && + it.requestBody?.sessionId == requestBody.sessionId } } @@ -1113,10 +1113,10 @@ internal class RealmCryptoStore @Inject constructor( .mapNotNull { it.toOutgoingGossipingRequest() as? OutgoingRoomKeyRequest }.firstOrNull { - it.requestBody?.algorithm == requestBody.algorithm - && it.requestBody?.sessionId == requestBody.sessionId - && it.requestBody?.senderKey == requestBody.senderKey - && it.requestBody?.roomId == requestBody.roomId + it.requestBody?.algorithm == requestBody.algorithm && + it.requestBody?.sessionId == requestBody.sessionId && + it.requestBody?.senderKey == requestBody.senderKey && + it.requestBody?.roomId == requestBody.roomId } if (existing == null) { @@ -1681,7 +1681,12 @@ internal class RealmCryptoStore @Inject constructor( } } - override fun markedSessionAsShared(roomId: String?, sessionId: String, userId: String, deviceId: String, chainIndex: Int) { + override fun markedSessionAsShared(roomId: String?, + sessionId: String, + userId: String, + deviceId: String, + deviceIdentityKey: String, + chainIndex: Int) { doRealmTransaction(realmConfiguration) { realm -> SharedSessionEntity.create( realm = realm, @@ -1689,14 +1694,22 @@ internal class RealmCryptoStore @Inject constructor( sessionId = sessionId, userId = userId, deviceId = deviceId, + deviceIdentityKey = deviceIdentityKey, chainIndex = chainIndex ) } } - override fun getSharedSessionInfo(roomId: String?, sessionId: String, userId: String, deviceId: String): IMXCryptoStore.SharedSessionResult { + override fun getSharedSessionInfo(roomId: String?, sessionId: String, deviceInfo: CryptoDeviceInfo): IMXCryptoStore.SharedSessionResult { return doWithRealm(realmConfiguration) { realm -> - SharedSessionEntity.get(realm, roomId, sessionId, userId, deviceId)?.let { + SharedSessionEntity.get( + realm = realm, + roomId = roomId, + sessionId = sessionId, + userId = deviceInfo.userId, + deviceId = deviceInfo.deviceId, + deviceIdentityKey = deviceInfo.identityKey() + )?.let { IMXCryptoStore.SharedSessionResult(true, it.chainIndex) } ?: IMXCryptoStore.SharedSessionResult(false, null) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStoreMigration.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStoreMigration.kt index 2846be99325..f73cbaf480a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStoreMigration.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStoreMigration.kt @@ -55,7 +55,7 @@ internal object RealmCryptoStoreMigration : RealmMigration { // 0, 1, 2: legacy Riot-Android // 3: migrate to RiotX schema // 4, 5, 6, 7, 8, 9: migrations from RiotX (which was previously 1, 2, 3, 4, 5, 6) - const val CRYPTO_STORE_SCHEMA_VERSION = 13L + const val CRYPTO_STORE_SCHEMA_VERSION = 14L private fun RealmObjectSchema.addFieldIfNotExists(fieldName: String, fieldType: Class<*>): RealmObjectSchema { if (!hasField(fieldName)) { @@ -94,6 +94,7 @@ internal object RealmCryptoStoreMigration : RealmMigration { if (oldVersion <= 10) migrateTo11(realm) if (oldVersion <= 11) migrateTo12(realm) if (oldVersion <= 12) migrateTo13(realm) + if (oldVersion <= 13) migrateTo14(realm) } private fun migrateTo1Legacy(realm: DynamicRealm) { @@ -554,4 +555,21 @@ internal object RealmCryptoStoreMigration : RealmMigration { Timber.e("TrustLevelEntity cleanup: Something is not correct...") } } + + // Version 14L Update the way we remember key sharing + private fun migrateTo14(realm: DynamicRealm) { + Timber.d("Step 13 -> 14") + realm.schema.get("SharedSessionEntity") + ?.addField(SharedSessionEntityFields.DEVICE_IDENTITY_KEY, String::class.java) + ?.addIndex(SharedSessionEntityFields.DEVICE_IDENTITY_KEY) + ?.transform { + val sharedUserId = it.getString(SharedSessionEntityFields.USER_ID) + val sharedDeviceId = it.getString(SharedSessionEntityFields.DEVICE_ID) + val knownDevice = realm.where("DeviceInfoEntity") + .equalTo(DeviceInfoEntityFields.USER_ID, sharedUserId) + .equalTo(DeviceInfoEntityFields.DEVICE_ID, sharedDeviceId) + .findFirst() + it.setString(SharedSessionEntityFields.DEVICE_IDENTITY_KEY, knownDevice?.getString(DeviceInfoEntityFields.IDENTITY_KEY)) + } + } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStoreModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStoreModule.kt index 6aae68c83ee..a2f2f8e97ae 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStoreModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStoreModule.kt @@ -16,6 +16,7 @@ package org.matrix.android.sdk.internal.crypto.store.db +import io.realm.annotations.RealmModule import org.matrix.android.sdk.internal.crypto.store.db.model.CrossSigningInfoEntity import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoMetadataEntity import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoRoomEntity @@ -27,13 +28,12 @@ import org.matrix.android.sdk.internal.crypto.store.db.model.KeysBackupDataEntit import org.matrix.android.sdk.internal.crypto.store.db.model.MyDeviceLastSeenInfoEntity import org.matrix.android.sdk.internal.crypto.store.db.model.OlmInboundGroupSessionEntity import org.matrix.android.sdk.internal.crypto.store.db.model.OlmSessionEntity +import org.matrix.android.sdk.internal.crypto.store.db.model.OutboundGroupSessionInfoEntity import org.matrix.android.sdk.internal.crypto.store.db.model.OutgoingGossipingRequestEntity import org.matrix.android.sdk.internal.crypto.store.db.model.SharedSessionEntity import org.matrix.android.sdk.internal.crypto.store.db.model.TrustLevelEntity import org.matrix.android.sdk.internal.crypto.store.db.model.UserEntity import org.matrix.android.sdk.internal.crypto.store.db.model.WithHeldSessionEntity -import io.realm.annotations.RealmModule -import org.matrix.android.sdk.internal.crypto.store.db.model.OutboundGroupSessionInfoEntity /** * Realm module for Crypto store classes diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/mapper/CrossSigningKeysMapper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/mapper/CrossSigningKeysMapper.kt index 9e739855927..c15414a8dd3 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/mapper/CrossSigningKeysMapper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/mapper/CrossSigningKeysMapper.kt @@ -18,10 +18,10 @@ package org.matrix.android.sdk.internal.crypto.store.db.mapper import com.squareup.moshi.Moshi import com.squareup.moshi.Types +import io.realm.RealmList import org.matrix.android.sdk.internal.crypto.crosssigning.DeviceTrustLevel import org.matrix.android.sdk.internal.crypto.model.CryptoCrossSigningKey import org.matrix.android.sdk.internal.crypto.store.db.model.KeyInfoEntity -import io.realm.RealmList import timber.log.Timber import javax.inject.Inject diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/CryptoMetadataEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/CryptoMetadataEntity.kt index 5166f6c31f3..35ae86db8bb 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/CryptoMetadataEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/CryptoMetadataEntity.kt @@ -16,10 +16,10 @@ package org.matrix.android.sdk.internal.crypto.store.db.model -import org.matrix.android.sdk.internal.crypto.store.db.deserializeFromRealm -import org.matrix.android.sdk.internal.crypto.store.db.serializeForRealm import io.realm.RealmObject import io.realm.annotations.PrimaryKey +import org.matrix.android.sdk.internal.crypto.store.db.deserializeFromRealm +import org.matrix.android.sdk.internal.crypto.store.db.serializeForRealm import org.matrix.olm.OlmAccount internal open class CryptoMetadataEntity( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/CryptoRoomEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/CryptoRoomEntity.kt index e226f3eaa8f..711b6984641 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/CryptoRoomEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/CryptoRoomEntity.kt @@ -28,8 +28,8 @@ internal open class CryptoRoomEntity( // to avoid re-create and re-share at each startup (if rotation not needed..) // This is specific to megolm but not sure how to model it better var outboundSessionInfo: OutboundGroupSessionInfoEntity? = null - ) - : RealmObject() { + ) : + RealmObject() { companion object } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/GossipingEventEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/GossipingEventEntity.kt index b8675d0823a..75094f01db5 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/GossipingEventEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/GossipingEventEntity.kt @@ -17,6 +17,8 @@ package org.matrix.android.sdk.internal.crypto.store.db.model import com.squareup.moshi.JsonDataException +import io.realm.RealmObject +import io.realm.annotations.Index import org.matrix.android.sdk.api.session.crypto.MXCryptoError import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.room.send.SendState @@ -24,8 +26,6 @@ import org.matrix.android.sdk.internal.crypto.MXEventDecryptionResult import org.matrix.android.sdk.internal.crypto.algorithms.olm.OlmDecryptionResult import org.matrix.android.sdk.internal.database.mapper.ContentMapper import org.matrix.android.sdk.internal.di.MoshiProvider -import io.realm.RealmObject -import io.realm.annotations.Index import timber.log.Timber /** diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/IncomingGossipingRequestEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/IncomingGossipingRequestEntity.kt index df45568d18b..4457a44cb2d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/IncomingGossipingRequestEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/IncomingGossipingRequestEntity.kt @@ -16,6 +16,8 @@ package org.matrix.android.sdk.internal.crypto.store.db.model +import io.realm.RealmObject +import io.realm.annotations.Index import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.internal.crypto.GossipRequestType import org.matrix.android.sdk.internal.crypto.GossipingRequestState @@ -23,8 +25,6 @@ import org.matrix.android.sdk.internal.crypto.IncomingRoomKeyRequest import org.matrix.android.sdk.internal.crypto.IncomingSecretShareRequest import org.matrix.android.sdk.internal.crypto.IncomingShareRequestCommon import org.matrix.android.sdk.internal.crypto.model.rest.RoomKeyRequestBody -import io.realm.RealmObject -import io.realm.annotations.Index internal open class IncomingGossipingRequestEntity(@Index var requestId: String? = "", @Index var typeStr: String? = null, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/OlmInboundGroupSessionEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/OlmInboundGroupSessionEntity.kt index d0e16bbe11c..f330e8822ac 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/OlmInboundGroupSessionEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/OlmInboundGroupSessionEntity.kt @@ -16,11 +16,11 @@ package org.matrix.android.sdk.internal.crypto.store.db.model +import io.realm.RealmObject +import io.realm.annotations.PrimaryKey import org.matrix.android.sdk.internal.crypto.model.OlmInboundGroupSessionWrapper2 import org.matrix.android.sdk.internal.crypto.store.db.deserializeFromRealm import org.matrix.android.sdk.internal.crypto.store.db.serializeForRealm -import io.realm.RealmObject -import io.realm.annotations.PrimaryKey import timber.log.Timber internal fun OlmInboundGroupSessionEntity.Companion.createPrimaryKey(sessionId: String?, senderKey: String?) = "$sessionId|$senderKey" @@ -33,8 +33,8 @@ internal open class OlmInboundGroupSessionEntity( // olmInboundGroupSessionData contains Json var olmInboundGroupSessionData: String? = null, // Indicate if the key has been backed up to the homeserver - var backedUp: Boolean = false) - : RealmObject() { + var backedUp: Boolean = false) : + RealmObject() { fun getInboundGroupSession(): OlmInboundGroupSessionWrapper2? { return try { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/OlmSessionEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/OlmSessionEntity.kt index 8f410578077..0b69311c57d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/OlmSessionEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/OlmSessionEntity.kt @@ -16,10 +16,10 @@ package org.matrix.android.sdk.internal.crypto.store.db.model -import org.matrix.android.sdk.internal.crypto.store.db.deserializeFromRealm -import org.matrix.android.sdk.internal.crypto.store.db.serializeForRealm import io.realm.RealmObject import io.realm.annotations.PrimaryKey +import org.matrix.android.sdk.internal.crypto.store.db.deserializeFromRealm +import org.matrix.android.sdk.internal.crypto.store.db.serializeForRealm import org.matrix.olm.OlmSession internal fun OlmSessionEntity.Companion.createPrimaryKey(sessionId: String, deviceKey: String) = "$sessionId|$deviceKey" @@ -29,8 +29,8 @@ internal open class OlmSessionEntity(@PrimaryKey var primaryKey: String = "", var sessionId: String? = null, var deviceKey: String? = null, var olmSessionData: String? = null, - var lastReceivedMessageTs: Long = 0) - : RealmObject() { + var lastReceivedMessageTs: Long = 0) : + RealmObject() { fun getOlmSession(): OlmSession? { return deserializeFromRealm(olmSessionData) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/OutgoingGossipingRequestEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/OutgoingGossipingRequestEntity.kt index 442dda1d717..a19547fddc3 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/OutgoingGossipingRequestEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/OutgoingGossipingRequestEntity.kt @@ -18,6 +18,8 @@ package org.matrix.android.sdk.internal.crypto.store.db.model import com.squareup.moshi.JsonAdapter import com.squareup.moshi.Types +import io.realm.RealmObject +import io.realm.annotations.Index import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.internal.crypto.GossipRequestType import org.matrix.android.sdk.internal.crypto.OutgoingGossipingRequest @@ -26,8 +28,6 @@ import org.matrix.android.sdk.internal.crypto.OutgoingRoomKeyRequest import org.matrix.android.sdk.internal.crypto.OutgoingSecretRequest import org.matrix.android.sdk.internal.crypto.model.rest.RoomKeyRequestBody import org.matrix.android.sdk.internal.di.MoshiProvider -import io.realm.RealmObject -import io.realm.annotations.Index internal open class OutgoingGossipingRequestEntity( @Index var requestId: String? = null, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/SharedSessionEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/SharedSessionEntity.kt index c0ed1ac409a..e2ae512afd3 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/SharedSessionEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/SharedSessionEntity.kt @@ -30,6 +30,7 @@ internal open class SharedSessionEntity( @Index var sessionId: String? = null, @Index var userId: String? = null, @Index var deviceId: String? = null, + @Index var deviceIdentityKey: String? = null, var chainIndex: Int? = null ) : RealmObject() { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/WithHeldSessionEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/WithHeldSessionEntity.kt index 2864ab768df..6d7889053b9 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/WithHeldSessionEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/WithHeldSessionEntity.kt @@ -16,9 +16,9 @@ package org.matrix.android.sdk.internal.crypto.store.db.model -import org.matrix.android.sdk.internal.crypto.model.event.WithHeldCode import io.realm.RealmObject import io.realm.annotations.Index +import org.matrix.android.sdk.internal.crypto.model.event.WithHeldCode /** * When an encrypted message is sent in a room, the megolm key might not be sent to all devices present in the room. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/query/CrossSigningInfoEntityQueries.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/query/CrossSigningInfoEntityQueries.kt index eea2f6f31b5..05eed9256e6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/query/CrossSigningInfoEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/query/CrossSigningInfoEntityQueries.kt @@ -16,11 +16,11 @@ package org.matrix.android.sdk.internal.crypto.store.db.query -import org.matrix.android.sdk.internal.crypto.store.db.model.CrossSigningInfoEntity -import org.matrix.android.sdk.internal.crypto.store.db.model.UserEntityFields import io.realm.Realm import io.realm.kotlin.createObject import io.realm.kotlin.where +import org.matrix.android.sdk.internal.crypto.store.db.model.CrossSigningInfoEntity +import org.matrix.android.sdk.internal.crypto.store.db.model.UserEntityFields internal fun CrossSigningInfoEntity.Companion.getOrCreate(realm: Realm, userId: String): CrossSigningInfoEntity { return realm.where() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/query/CryptoRoomEntityQueries.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/query/CryptoRoomEntityQueries.kt index 5ebf8b1ed5a..5750cc1f676 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/query/CryptoRoomEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/query/CryptoRoomEntityQueries.kt @@ -16,11 +16,11 @@ package org.matrix.android.sdk.internal.crypto.store.db.query -import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoRoomEntity -import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoRoomEntityFields import io.realm.Realm import io.realm.kotlin.createObject import io.realm.kotlin.where +import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoRoomEntity +import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoRoomEntityFields /** * Get or create a room diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/query/DeviceInfoEntityQueries.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/query/DeviceInfoEntityQueries.kt index 1d5ca2d3ccc..c9523d63ce3 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/query/DeviceInfoEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/query/DeviceInfoEntityQueries.kt @@ -16,12 +16,12 @@ package org.matrix.android.sdk.internal.crypto.store.db.query -import org.matrix.android.sdk.internal.crypto.store.db.model.DeviceInfoEntity -import org.matrix.android.sdk.internal.crypto.store.db.model.DeviceInfoEntityFields -import org.matrix.android.sdk.internal.crypto.store.db.model.createPrimaryKey import io.realm.Realm import io.realm.kotlin.createObject import io.realm.kotlin.where +import org.matrix.android.sdk.internal.crypto.store.db.model.DeviceInfoEntity +import org.matrix.android.sdk.internal.crypto.store.db.model.DeviceInfoEntityFields +import org.matrix.android.sdk.internal.crypto.store.db.model.createPrimaryKey /** * Get or create a device info diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/query/SharedSessionQueries.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/query/SharedSessionQueries.kt index fa37734fe58..2784e58425a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/query/SharedSessionQueries.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/query/SharedSessionQueries.kt @@ -16,27 +16,31 @@ package org.matrix.android.sdk.internal.crypto.store.db.query -import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM -import org.matrix.android.sdk.internal.crypto.store.db.model.SharedSessionEntity -import org.matrix.android.sdk.internal.crypto.store.db.model.SharedSessionEntityFields import io.realm.Realm import io.realm.RealmResults import io.realm.kotlin.createObject import io.realm.kotlin.where +import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM +import org.matrix.android.sdk.internal.crypto.store.db.model.SharedSessionEntity +import org.matrix.android.sdk.internal.crypto.store.db.model.SharedSessionEntityFields -internal fun SharedSessionEntity.Companion.get(realm: Realm, roomId: String?, sessionId: String, userId: String, deviceId: String) - : SharedSessionEntity? { +internal fun SharedSessionEntity.Companion.get(realm: Realm, + roomId: String?, + sessionId: String, + userId: String, + deviceId: String, + deviceIdentityKey: String?): SharedSessionEntity? { return realm.where() .equalTo(SharedSessionEntityFields.ROOM_ID, roomId) .equalTo(SharedSessionEntityFields.SESSION_ID, sessionId) .equalTo(SharedSessionEntityFields.ALGORITHM, MXCRYPTO_ALGORITHM_MEGOLM) .equalTo(SharedSessionEntityFields.USER_ID, userId) .equalTo(SharedSessionEntityFields.DEVICE_ID, deviceId) + .equalTo(SharedSessionEntityFields.DEVICE_IDENTITY_KEY, deviceIdentityKey) .findFirst() } -internal fun SharedSessionEntity.Companion.get(realm: Realm, roomId: String?, sessionId: String) - : RealmResults { +internal fun SharedSessionEntity.Companion.get(realm: Realm, roomId: String?, sessionId: String): RealmResults { return realm.where() .equalTo(SharedSessionEntityFields.ROOM_ID, roomId) .equalTo(SharedSessionEntityFields.SESSION_ID, sessionId) @@ -44,14 +48,19 @@ internal fun SharedSessionEntity.Companion.get(realm: Realm, roomId: String?, se .findAll() } -internal fun SharedSessionEntity.Companion.create(realm: Realm, roomId: String?, sessionId: String, userId: String, deviceId: String, chainIndex: Int) - : SharedSessionEntity { +internal fun SharedSessionEntity.Companion.create(realm: Realm, roomId: String?, + sessionId: String, + userId: String, + deviceId: String, + deviceIdentityKey: String, + chainIndex: Int): SharedSessionEntity { return realm.createObject().apply { this.roomId = roomId this.algorithm = MXCRYPTO_ALGORITHM_MEGOLM this.sessionId = sessionId this.userId = userId this.deviceId = deviceId + this.deviceIdentityKey = deviceIdentityKey this.chainIndex = chainIndex } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/query/WithHeldSessionQueries.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/query/WithHeldSessionQueries.kt index 3c6c594a70b..b8a3e361371 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/query/WithHeldSessionQueries.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/query/WithHeldSessionQueries.kt @@ -16,12 +16,12 @@ package org.matrix.android.sdk.internal.crypto.store.db.query -import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM -import org.matrix.android.sdk.internal.crypto.store.db.model.WithHeldSessionEntity -import org.matrix.android.sdk.internal.crypto.store.db.model.WithHeldSessionEntityFields import io.realm.Realm import io.realm.kotlin.createObject import io.realm.kotlin.where +import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM +import org.matrix.android.sdk.internal.crypto.store.db.model.WithHeldSessionEntity +import org.matrix.android.sdk.internal.crypto.store.db.model.WithHeldSessionEntityFields internal fun WithHeldSessionEntity.Companion.get(realm: Realm, roomId: String, sessionId: String): WithHeldSessionEntity? { return realm.where() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/DeleteDeviceTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/DeleteDeviceTask.kt index bdb8e8d137a..ca04bac5d5d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/DeleteDeviceTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/DeleteDeviceTask.kt @@ -16,11 +16,11 @@ package org.matrix.android.sdk.internal.crypto.tasks +import org.matrix.android.sdk.api.auth.UIABaseAuth import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor import org.matrix.android.sdk.internal.auth.registration.handleUIA import org.matrix.android.sdk.internal.crypto.api.CryptoApi import org.matrix.android.sdk.internal.crypto.model.rest.DeleteDeviceParams -import org.matrix.android.sdk.api.auth.UIABaseAuth import org.matrix.android.sdk.internal.network.GlobalErrorReceiver import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.task.Task @@ -46,8 +46,8 @@ internal class DefaultDeleteDeviceTask @Inject constructor( cryptoApi.deleteDevice(params.deviceId, DeleteDeviceParams(params.userAuthParam?.asMap())) } } catch (throwable: Throwable) { - if (params.userInteractiveAuthInterceptor == null - || !handleUIA( + if (params.userInteractiveAuthInterceptor == null || + !handleUIA( failure = throwable, interceptor = params.userInteractiveAuthInterceptor, retryBlock = { authUpdate -> diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/InitializeCrossSigningTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/InitializeCrossSigningTask.kt index 1d40e5defd3..e2fd54f0d87 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/InitializeCrossSigningTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/InitializeCrossSigningTask.kt @@ -125,8 +125,8 @@ internal class DefaultInitializeCrossSigningTask @Inject constructor( try { uploadSigningKeysTask.execute(uploadSigningKeysParams) } catch (failure: Throwable) { - if (params.interactiveAuthInterceptor == null - || !handleUIA( + if (params.interactiveAuthInterceptor == null || + !handleUIA( failure = failure, interceptor = params.interactiveAuthInterceptor, retryBlock = { authUpdate -> diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/UploadSigningKeysTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/UploadSigningKeysTask.kt index 08c767ba348..6cb14ded631 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/UploadSigningKeysTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tasks/UploadSigningKeysTask.kt @@ -16,10 +16,10 @@ package org.matrix.android.sdk.internal.crypto.tasks +import org.matrix.android.sdk.api.auth.UIABaseAuth import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.internal.crypto.api.CryptoApi import org.matrix.android.sdk.internal.crypto.model.CryptoCrossSigningKey -import org.matrix.android.sdk.api.auth.UIABaseAuth import org.matrix.android.sdk.internal.crypto.model.rest.UploadSigningKeysBody import org.matrix.android.sdk.internal.crypto.model.toRest import org.matrix.android.sdk.internal.network.GlobalErrorReceiver diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/DefaultIncomingSASDefaultVerificationTransaction.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/DefaultIncomingSASDefaultVerificationTransaction.kt index fde9f70e7b7..68f1cf62d52 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/DefaultIncomingSASDefaultVerificationTransaction.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/DefaultIncomingSASDefaultVerificationTransaction.kt @@ -114,8 +114,8 @@ internal class DefaultIncomingSASDefaultVerificationTransaction( // No common key sharing/hashing/hmac/SAS methods. // If a device is unable to complete the verification because the devices are unable to find a common key sharing, // hashing, hmac, or SAS method, then it should send a m.key.verification.cancel message - if (listOf(agreedProtocol, agreedHash, agreedMac).any { it.isNullOrBlank() } - || agreedShortCode.isNullOrEmpty()) { + if (listOf(agreedProtocol, agreedHash, agreedMac).any { it.isNullOrBlank() } || + agreedShortCode.isNullOrEmpty()) { // Failed to find agreement Timber.e("## SAS Failed to find agreement ") cancel(CancelCode.UnknownMethod) @@ -241,12 +241,12 @@ internal class DefaultIncomingSASDefaultVerificationTransaction( override fun onKeyVerificationMac(vMac: ValidVerificationInfoMac) { Timber.v("## SAS I: received mac for request id:$transactionId") // Check for state? - if (state != VerificationTxState.SendingKey - && state != VerificationTxState.KeySent - && state != VerificationTxState.ShortCodeReady - && state != VerificationTxState.ShortCodeAccepted - && state != VerificationTxState.SendingMac - && state != VerificationTxState.MacSent) { + if (state != VerificationTxState.SendingKey && + state != VerificationTxState.KeySent && + state != VerificationTxState.ShortCodeReady && + state != VerificationTxState.ShortCodeAccepted && + state != VerificationTxState.SendingMac && + state != VerificationTxState.MacSent) { Timber.e("## SAS I: received key from invalid state $state") cancel(CancelCode.UnexpectedMessage) return diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/DefaultOutgoingSASDefaultVerificationTransaction.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/DefaultOutgoingSASDefaultVerificationTransaction.kt index 1a41f890066..e203f03b06f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/DefaultOutgoingSASDefaultVerificationTransaction.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/DefaultOutgoingSASDefaultVerificationTransaction.kt @@ -144,10 +144,10 @@ internal class DefaultOutgoingSASDefaultVerificationTransaction( return } // Check that the agreement is correct - if (!KNOWN_AGREEMENT_PROTOCOLS.contains(accept.keyAgreementProtocol) - || !KNOWN_HASHES.contains(accept.hash) - || !KNOWN_MACS.contains(accept.messageAuthenticationCode) - || accept.shortAuthenticationStrings.intersect(KNOWN_SHORT_CODES).isEmpty()) { + if (!KNOWN_AGREEMENT_PROTOCOLS.contains(accept.keyAgreementProtocol) || + !KNOWN_HASHES.contains(accept.hash) || + !KNOWN_MACS.contains(accept.messageAuthenticationCode) || + accept.shortAuthenticationStrings.intersect(KNOWN_SHORT_CODES).isEmpty()) { Timber.e("## SAS O: received invalid accept") cancel(CancelCode.UnknownMethod) return @@ -233,12 +233,12 @@ internal class DefaultOutgoingSASDefaultVerificationTransaction( override fun onKeyVerificationMac(vMac: ValidVerificationInfoMac) { Timber.v("## SAS O: onKeyVerificationMac id:$transactionId") // There is starting to be a huge amount of state / race here :/ - if (state != VerificationTxState.OnKeyReceived - && state != VerificationTxState.ShortCodeReady - && state != VerificationTxState.ShortCodeAccepted - && state != VerificationTxState.KeySent - && state != VerificationTxState.SendingMac - && state != VerificationTxState.MacSent) { + if (state != VerificationTxState.OnKeyReceived && + state != VerificationTxState.ShortCodeReady && + state != VerificationTxState.ShortCodeAccepted && + state != VerificationTxState.KeySent && + state != VerificationTxState.SendingMac && + state != VerificationTxState.MacSent) { Timber.e("## SAS O: received mac from invalid state $state") cancel(CancelCode.UnexpectedMessage) return diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/DefaultVerificationService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/DefaultVerificationService.kt index d9da88770cd..768109979d3 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/DefaultVerificationService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/DefaultVerificationService.kt @@ -19,6 +19,8 @@ package org.matrix.android.sdk.internal.crypto.verification import android.os.Handler import android.os.Looper import dagger.Lazy +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.crypto.crosssigning.CrossSigningService import org.matrix.android.sdk.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME import org.matrix.android.sdk.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NAME @@ -82,8 +84,6 @@ import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.session.SessionScope import org.matrix.android.sdk.internal.task.TaskExecutor import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch import timber.log.Timber import java.util.UUID import javax.inject.Inject @@ -537,8 +537,8 @@ internal class DefaultVerificationService @Inject constructor( // as we are the one requesting in first place (or we accepted the request) // I need to check if the pending request was related to this device also val autoAccept = getExistingVerificationRequests(otherUserId).any { - it.transactionId == startReq.transactionId - && (it.requestInfo?.fromDevice == this.deviceId || it.readyInfo?.fromDevice == this.deviceId) + it.transactionId == startReq.transactionId && + (it.requestInfo?.fromDevice == this.deviceId || it.readyInfo?.fromDevice == this.deviceId) } val tx = DefaultIncomingSASDefaultVerificationTransaction( // this, @@ -1126,8 +1126,10 @@ internal class DefaultVerificationService @Inject constructor( } } - override fun requestKeyVerificationInDMs(methods: List, otherUserId: String, roomId: String, localId: String?) - : PendingVerificationRequest { + override fun requestKeyVerificationInDMs(methods: List, + otherUserId: String, + roomId: String, + localId: String?): PendingVerificationRequest { Timber.i("## SAS Requesting verification to user: $otherUserId in room $roomId") val requestsForUser = pendingRequests.getOrPut(otherUserId) { mutableListOf() } @@ -1278,8 +1280,8 @@ internal class DefaultVerificationService @Inject constructor( private fun updatePendingRequest(updated: PendingVerificationRequest) { val requestsForUser = pendingRequests.getOrPut(updated.otherUserId) { mutableListOf() } val index = requestsForUser.indexOfFirst { - it.transactionId == updated.transactionId - || it.transactionId == null && it.localId == updated.localId + it.transactionId == updated.transactionId || + it.transactionId == null && it.localId == updated.localId } if (index != -1) { requestsForUser.removeAt(index) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SendVerificationMessageWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SendVerificationMessageWorker.kt index 538d7b56e92..481ce85f70b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SendVerificationMessageWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/SendVerificationMessageWorker.kt @@ -34,8 +34,8 @@ import javax.inject.Inject * Possible next worker : None */ internal class SendVerificationMessageWorker(context: Context, - params: WorkerParameters) - : SessionSafeCoroutineWorker(context, params, Params::class.java) { + params: WorkerParameters) : + SessionSafeCoroutineWorker(context, params, Params::class.java) { @JsonClass(generateAdapter = true) internal data class Params( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationInfoStart.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationInfoStart.kt index 21a6ba41b14..f727aff39d3 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationInfoStart.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/verification/VerificationInfoStart.kt @@ -73,8 +73,8 @@ internal interface VerificationInfoStart : VerificationInfo) - : VerificationInfoAccept = MessageVerificationAcceptContent.create( + shortAuthenticationStrings: List): VerificationInfoAccept = + MessageVerificationAcceptContent.create( tid, keyAgreementProtocol, hash, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/DatabaseCleaner.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/DatabaseCleaner.kt index ee58880eb83..7341d4656a4 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/DatabaseCleaner.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/DatabaseCleaner.kt @@ -21,6 +21,7 @@ import io.realm.RealmConfiguration import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.SessionLifecycleObserver import org.matrix.android.sdk.internal.database.helper.nextDisplayIndex import org.matrix.android.sdk.internal.database.model.ChunkEntity import org.matrix.android.sdk.internal.database.model.ChunkEntityFields @@ -30,7 +31,6 @@ import org.matrix.android.sdk.internal.database.model.TimelineEventEntity import org.matrix.android.sdk.internal.database.model.TimelineEventEntityFields import org.matrix.android.sdk.internal.database.model.deleteOnCascade import org.matrix.android.sdk.internal.di.SessionDatabase -import org.matrix.android.sdk.api.session.SessionLifecycleObserver import org.matrix.android.sdk.internal.session.room.timeline.PaginationDirection import org.matrix.android.sdk.internal.task.TaskExecutor import timber.log.Timber diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/EventInsertLiveObserver.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/EventInsertLiveObserver.kt index 758c7aa5b9d..115025cc7d8 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/EventInsertLiveObserver.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/EventInsertLiveObserver.kt @@ -31,8 +31,8 @@ import timber.log.Timber import javax.inject.Inject internal class EventInsertLiveObserver @Inject constructor(@SessionDatabase realmConfiguration: RealmConfiguration, - private val processors: Set<@JvmSuppressWildcards EventInsertLiveProcessor>) - : RealmLiveEntityObserver(realmConfiguration) { + private val processors: Set<@JvmSuppressWildcards EventInsertLiveProcessor>) : + RealmLiveEntityObserver(realmConfiguration) { override val query = Monarchy.Query { it.where(EventInsertEntity::class.java).equalTo(EventInsertEntityFields.CAN_BE_PROCESSED, true) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmKeysUtils.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmKeysUtils.kt index d5ff7a0f849..7c622146d34 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmKeysUtils.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmKeysUtils.kt @@ -19,9 +19,9 @@ import android.content.Context import android.util.Base64 import androidx.core.content.edit import io.realm.Realm +import io.realm.RealmConfiguration import org.matrix.android.sdk.BuildConfig import org.matrix.android.sdk.internal.session.securestorage.SecretStoringUtils -import io.realm.RealmConfiguration import timber.log.Timber import java.security.SecureRandom import javax.inject.Inject diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmLiveEntityObserver.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmLiveEntityObserver.kt index c602ed70759..50eb086f9ab 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmLiveEntityObserver.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmLiveEntityObserver.kt @@ -17,8 +17,6 @@ package org.matrix.android.sdk.internal.database import com.zhuinden.monarchy.Monarchy -import org.matrix.android.sdk.api.session.SessionLifecycleObserver -import org.matrix.android.sdk.internal.util.createBackgroundHandler import io.realm.Realm import io.realm.RealmChangeListener import io.realm.RealmConfiguration @@ -29,13 +27,15 @@ import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.android.asCoroutineDispatcher import kotlinx.coroutines.cancelChildren import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.SessionLifecycleObserver +import org.matrix.android.sdk.internal.util.createBackgroundHandler import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicReference internal interface LiveEntityObserver : SessionLifecycleObserver -internal abstract class RealmLiveEntityObserver(protected val realmConfiguration: RealmConfiguration) - : LiveEntityObserver, RealmChangeListener> { +internal abstract class RealmLiveEntityObserver(protected val realmConfiguration: RealmConfiguration) : + LiveEntityObserver, RealmChangeListener> { private companion object { val BACKGROUND_HANDLER = createBackgroundHandler("LIVE_ENTITY_BACKGROUND") diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionProvider.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionProvider.kt index 52fbabb49fe..8c62c345d0c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionProvider.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionProvider.kt @@ -21,8 +21,8 @@ import androidx.annotation.MainThread import com.zhuinden.monarchy.Monarchy import io.realm.Realm import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.api.session.SessionLifecycleObserver +import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.session.SessionScope import javax.inject.Inject import kotlin.concurrent.getOrSet @@ -32,8 +32,8 @@ import kotlin.concurrent.getOrSet * instance. This does check each time if you are on the main thread or not and returns the appropriate realm instance. */ @SessionScope -internal class RealmSessionProvider @Inject constructor(@SessionDatabase private val monarchy: Monarchy) - : SessionLifecycleObserver { +internal class RealmSessionProvider @Inject constructor(@SessionDatabase private val monarchy: Monarchy) : + SessionLifecycleObserver { private val realmThreadLocal = ThreadLocal() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ChunkEntityHelper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ChunkEntityHelper.kt index e262b404194..f74e4b0f4c6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ChunkEntityHelper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ChunkEntityHelper.kt @@ -16,6 +16,9 @@ package org.matrix.android.sdk.internal.database.helper +import io.realm.Realm +import io.realm.Sort +import io.realm.kotlin.createObject import org.matrix.android.sdk.api.session.room.model.RoomMemberContent import org.matrix.android.sdk.internal.database.model.ChunkEntity import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntityFields @@ -33,9 +36,6 @@ import org.matrix.android.sdk.internal.database.query.getOrCreate import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.extensions.assertIsManaged import org.matrix.android.sdk.internal.session.room.timeline.PaginationDirection -import io.realm.Realm -import io.realm.Sort -import io.realm.kotlin.createObject import timber.log.Timber internal fun ChunkEntity.merge(roomId: String, chunkToMerge: ChunkEntity, direction: PaginationDirection) { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/TimelineEventEntityHelper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/TimelineEventEntityHelper.kt index 90e867749ee..3993e8e7991 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/TimelineEventEntityHelper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/TimelineEventEntityHelper.kt @@ -16,9 +16,9 @@ package org.matrix.android.sdk.internal.database.helper +import io.realm.Realm import org.matrix.android.sdk.internal.database.model.TimelineEventEntity import org.matrix.android.sdk.internal.database.model.TimelineEventEntityFields -import io.realm.Realm internal fun TimelineEventEntity.Companion.nextId(realm: Realm): Long { val currentIdNum = realm.where(TimelineEventEntity::class.java).max(TimelineEventEntityFields.LOCAL_ID) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/IsUselessResolver.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/IsUselessResolver.kt index 5d7afc50d63..d704ecac8e0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/IsUselessResolver.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/IsUselessResolver.kt @@ -29,8 +29,8 @@ internal object IsUselessResolver { return when (event.type) { EventType.STATE_ROOM_MEMBER -> { // Call toContent(), to filter out null value - event.content != null - && event.content.toContent() == event.resolvedPrevContent()?.toContent() + event.content != null && + event.content.toContent() == event.resolvedPrevContent()?.toContent() } else -> false } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/PollResponseAggregatedSummaryEntityMapper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/PollResponseAggregatedSummaryEntityMapper.kt index b26e7e88e35..00998af9bbd 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/PollResponseAggregatedSummaryEntityMapper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/mapper/PollResponseAggregatedSummaryEntityMapper.kt @@ -16,11 +16,11 @@ package org.matrix.android.sdk.internal.database.mapper +import io.realm.RealmList 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.PollResponseAggregatedSummary import org.matrix.android.sdk.internal.database.model.PollResponseAggregatedSummaryEntity -import io.realm.RealmList internal object PollResponseAggregatedSummaryEntityMapper { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/EventEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/EventEntity.kt index 4dc8712afbe..bcd30cb54bb 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/EventEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/EventEntity.kt @@ -16,12 +16,12 @@ package org.matrix.android.sdk.internal.database.model +import io.realm.RealmObject +import io.realm.annotations.Index import org.matrix.android.sdk.api.session.room.send.SendState import org.matrix.android.sdk.internal.crypto.MXEventDecryptionResult import org.matrix.android.sdk.internal.crypto.algorithms.olm.OlmDecryptionResult import org.matrix.android.sdk.internal.di.MoshiProvider -import io.realm.RealmObject -import io.realm.annotations.Index import org.matrix.android.sdk.internal.extensions.assertIsManaged internal open class EventEntity(@Index var eventId: String = "", diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/GroupEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/GroupEntity.kt index 25a041e3d07..527f7823590 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/GroupEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/GroupEntity.kt @@ -16,16 +16,16 @@ package org.matrix.android.sdk.internal.database.model -import org.matrix.android.sdk.api.session.room.model.Membership import io.realm.RealmObject import io.realm.annotations.PrimaryKey +import org.matrix.android.sdk.api.session.room.model.Membership /** * This class is used to store group info (groupId and membership) from the sync response. * Then GetGroupDataTask is called regularly to fetch group information from the homeserver. */ -internal open class GroupEntity(@PrimaryKey var groupId: String = "") - : RealmObject() { +internal open class GroupEntity(@PrimaryKey var groupId: String = "") : + RealmObject() { private var membershipStr: String = Membership.NONE.name var membership: Membership diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/GroupSummaryEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/GroupSummaryEntity.kt index 8982436ccc4..4ba45dcda2d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/GroupSummaryEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/GroupSummaryEntity.kt @@ -16,10 +16,10 @@ package org.matrix.android.sdk.internal.database.model -import org.matrix.android.sdk.api.session.room.model.Membership import io.realm.RealmList import io.realm.RealmObject import io.realm.annotations.PrimaryKey +import org.matrix.android.sdk.api.session.room.model.Membership internal open class GroupSummaryEntity(@PrimaryKey var groupId: String = "", var displayName: String = "", diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/PushRulesEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/PushRulesEntity.kt index 571bc71c273..4125d908912 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/PushRulesEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/PushRulesEntity.kt @@ -15,9 +15,9 @@ */ package org.matrix.android.sdk.internal.database.model -import org.matrix.android.sdk.api.pushrules.RuleKind import io.realm.RealmList import io.realm.RealmObject +import org.matrix.android.sdk.api.pushrules.RuleKind import org.matrix.android.sdk.internal.extensions.clearWith internal open class PushRulesEntity( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomEntity.kt index 65483e05bf6..2997d5d7d8a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomEntity.kt @@ -16,10 +16,10 @@ package org.matrix.android.sdk.internal.database.model -import org.matrix.android.sdk.api.session.room.model.Membership import io.realm.RealmList import io.realm.RealmObject import io.realm.annotations.PrimaryKey +import org.matrix.android.sdk.api.session.room.model.Membership internal open class RoomEntity(@PrimaryKey var roomId: String = "", var chunks: RealmList = RealmList(), diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomMemberSummaryEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomMemberSummaryEntity.kt index e970fab3979..75771ff12c7 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomMemberSummaryEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/RoomMemberSummaryEntity.kt @@ -16,10 +16,11 @@ package org.matrix.android.sdk.internal.database.model -import org.matrix.android.sdk.api.session.room.model.Membership import io.realm.RealmObject import io.realm.annotations.Index import io.realm.annotations.PrimaryKey +import org.matrix.android.sdk.api.session.room.model.Membership +import org.matrix.android.sdk.api.util.MatrixItem internal open class RoomMemberSummaryEntity(@PrimaryKey var primaryKey: String = "", @Index var userId: String = "", @@ -39,7 +40,7 @@ internal open class RoomMemberSummaryEntity(@PrimaryKey var primaryKey: String = membershipStr = value.name } - fun getBestName() = displayName?.takeIf { it.isNotBlank() } ?: userId + fun toMatrixItem() = MatrixItem.UserItem(userId, displayName, avatarUrl) companion object } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/BreadcrumbsEntityQuery.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/BreadcrumbsEntityQuery.kt index 0463d52fffc..3b24ff5f9d1 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/BreadcrumbsEntityQuery.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/BreadcrumbsEntityQuery.kt @@ -16,10 +16,10 @@ package org.matrix.android.sdk.internal.database.query -import org.matrix.android.sdk.internal.database.model.BreadcrumbsEntity import io.realm.Realm import io.realm.kotlin.createObject import io.realm.kotlin.where +import org.matrix.android.sdk.internal.database.model.BreadcrumbsEntity internal fun BreadcrumbsEntity.Companion.get(realm: Realm): BreadcrumbsEntity? { return realm.where().findFirst() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/ChunkEntityQueries.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/ChunkEntityQueries.kt index f7d2823303b..156a8dd767c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/ChunkEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/ChunkEntityQueries.kt @@ -16,13 +16,13 @@ package org.matrix.android.sdk.internal.database.query -import org.matrix.android.sdk.internal.database.model.ChunkEntity -import org.matrix.android.sdk.internal.database.model.ChunkEntityFields import io.realm.Realm import io.realm.RealmQuery import io.realm.RealmResults import io.realm.kotlin.createObject import io.realm.kotlin.where +import org.matrix.android.sdk.internal.database.model.ChunkEntity +import org.matrix.android.sdk.internal.database.model.ChunkEntityFields internal fun ChunkEntity.Companion.where(realm: Realm, roomId: String): RealmQuery { return realm.where() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/CurrentStateEventEntityQueries.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/CurrentStateEventEntityQueries.kt index 9a3622e2dcf..716783f2bab 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/CurrentStateEventEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/CurrentStateEventEntityQueries.kt @@ -17,33 +17,46 @@ package org.matrix.android.sdk.internal.database.query -import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity -import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntityFields import io.realm.Realm import io.realm.RealmQuery import io.realm.kotlin.createObject +import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity +import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntityFields -internal fun CurrentStateEventEntity.Companion.whereType(realm: Realm, roomId: String, type: String): RealmQuery { +internal fun CurrentStateEventEntity.Companion.whereType(realm: Realm, + roomId: String, + type: String): RealmQuery { return realm.where(CurrentStateEventEntity::class.java) .equalTo(CurrentStateEventEntityFields.ROOM_ID, roomId) .equalTo(CurrentStateEventEntityFields.TYPE, type) } -internal fun CurrentStateEventEntity.Companion.whereStateKey(realm: Realm, roomId: String, type: String, stateKey: String) - : RealmQuery { +internal fun CurrentStateEventEntity.Companion.whereStateKey(realm: Realm, + roomId: String, + type: String, + stateKey: String): RealmQuery { return whereType(realm = realm, roomId = roomId, type = type) .equalTo(CurrentStateEventEntityFields.STATE_KEY, stateKey) } -internal fun CurrentStateEventEntity.Companion.getOrNull(realm: Realm, roomId: String, stateKey: String, type: String): CurrentStateEventEntity? { +internal fun CurrentStateEventEntity.Companion.getOrNull(realm: Realm, + roomId: String, + stateKey: String, + type: String): CurrentStateEventEntity? { return whereStateKey(realm = realm, roomId = roomId, type = type, stateKey = stateKey).findFirst() } -internal fun CurrentStateEventEntity.Companion.getOrCreate(realm: Realm, roomId: String, stateKey: String, type: String): CurrentStateEventEntity { +internal fun CurrentStateEventEntity.Companion.getOrCreate(realm: Realm, + roomId: String, + stateKey: String, + type: String): CurrentStateEventEntity { return getOrNull(realm = realm, roomId = roomId, stateKey = stateKey, type = type) ?: create(realm, roomId, stateKey, type) } -private fun create(realm: Realm, roomId: String, stateKey: String, type: String): CurrentStateEventEntity { +private fun create(realm: Realm, + roomId: String, + stateKey: String, + type: String): CurrentStateEventEntity { return realm.createObject().apply { this.type = type this.roomId = roomId diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/EventAnnotationsSummaryEntityQuery.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/EventAnnotationsSummaryEntityQuery.kt index c3cae3d2685..14cb7e22da2 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/EventAnnotationsSummaryEntityQuery.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/EventAnnotationsSummaryEntityQuery.kt @@ -16,12 +16,12 @@ package org.matrix.android.sdk.internal.database.query -import org.matrix.android.sdk.internal.database.model.EventAnnotationsSummaryEntity -import org.matrix.android.sdk.internal.database.model.EventAnnotationsSummaryEntityFields -import org.matrix.android.sdk.internal.database.model.TimelineEventEntity import io.realm.Realm import io.realm.RealmQuery import io.realm.kotlin.where +import org.matrix.android.sdk.internal.database.model.EventAnnotationsSummaryEntity +import org.matrix.android.sdk.internal.database.model.EventAnnotationsSummaryEntityFields +import org.matrix.android.sdk.internal.database.model.TimelineEventEntity internal fun EventAnnotationsSummaryEntity.Companion.where(realm: Realm, roomId: String, eventId: String): RealmQuery { return realm.where() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/EventEntityQueries.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/EventEntityQueries.kt index 57e24cf88fe..240b2a06911 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/EventEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/EventEntityQueries.kt @@ -16,15 +16,15 @@ package org.matrix.android.sdk.internal.database.query -import org.matrix.android.sdk.internal.database.model.EventEntity -import org.matrix.android.sdk.internal.database.model.EventEntityFields -import org.matrix.android.sdk.internal.database.model.EventInsertEntity -import org.matrix.android.sdk.internal.database.model.EventInsertType import io.realm.Realm import io.realm.RealmList import io.realm.RealmQuery import io.realm.kotlin.where import org.matrix.android.sdk.api.session.events.model.EventType +import org.matrix.android.sdk.internal.database.model.EventEntity +import org.matrix.android.sdk.internal.database.model.EventEntityFields +import org.matrix.android.sdk.internal.database.model.EventInsertEntity +import org.matrix.android.sdk.internal.database.model.EventInsertType internal fun EventEntity.copyToRealmOrIgnore(realm: Realm, insertType: EventInsertType): EventEntity { val eventEntity = realm.where() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/FilterEntityQueries.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/FilterEntityQueries.kt index c76e6068054..3968169e083 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/FilterEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/FilterEntityQueries.kt @@ -16,11 +16,11 @@ package org.matrix.android.sdk.internal.database.query -import org.matrix.android.sdk.internal.database.model.FilterEntity -import org.matrix.android.sdk.internal.session.filter.FilterFactory import io.realm.Realm import io.realm.kotlin.createObject import io.realm.kotlin.where +import org.matrix.android.sdk.internal.database.model.FilterEntity +import org.matrix.android.sdk.internal.session.filter.FilterFactory /** * Get the current filter diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/GroupEntityQueries.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/GroupEntityQueries.kt index 9a1f2b3782f..020592d1dd3 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/GroupEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/GroupEntityQueries.kt @@ -16,13 +16,13 @@ package org.matrix.android.sdk.internal.database.query +import io.realm.Realm +import io.realm.RealmQuery +import io.realm.kotlin.where import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.internal.database.model.GroupEntity import org.matrix.android.sdk.internal.database.model.GroupEntityFields import org.matrix.android.sdk.internal.query.process -import io.realm.Realm -import io.realm.RealmQuery -import io.realm.kotlin.where internal fun GroupEntity.Companion.where(realm: Realm, groupId: String): RealmQuery { return realm.where() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/GroupSummaryEntityQueries.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/GroupSummaryEntityQueries.kt index fbfd8bd19e1..8131598d95b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/GroupSummaryEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/GroupSummaryEntityQueries.kt @@ -16,12 +16,12 @@ package org.matrix.android.sdk.internal.database.query -import org.matrix.android.sdk.internal.database.model.GroupSummaryEntity -import org.matrix.android.sdk.internal.database.model.GroupSummaryEntityFields import io.realm.Realm import io.realm.RealmQuery import io.realm.kotlin.createObject import io.realm.kotlin.where +import org.matrix.android.sdk.internal.database.model.GroupSummaryEntity +import org.matrix.android.sdk.internal.database.model.GroupSummaryEntityFields internal fun GroupSummaryEntity.Companion.where(realm: Realm, groupId: String? = null): RealmQuery { val query = realm.where() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/HomeServerCapabilitiesQueries.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/HomeServerCapabilitiesQueries.kt index b0b4f5a83d4..4f8ac20e944 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/HomeServerCapabilitiesQueries.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/HomeServerCapabilitiesQueries.kt @@ -16,10 +16,10 @@ package org.matrix.android.sdk.internal.database.query -import org.matrix.android.sdk.internal.database.model.HomeServerCapabilitiesEntity import io.realm.Realm import io.realm.kotlin.createObject import io.realm.kotlin.where +import org.matrix.android.sdk.internal.database.model.HomeServerCapabilitiesEntity /** * Get the current HomeServerCapabilitiesEntity, return null if it does not exist diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/PushersQueries.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/PushersQueries.kt index 359b2568443..1f6b210252a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/PushersQueries.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/PushersQueries.kt @@ -15,6 +15,9 @@ */ package org.matrix.android.sdk.internal.database.query +import io.realm.Realm +import io.realm.RealmQuery +import io.realm.kotlin.where import org.matrix.android.sdk.api.pushrules.RuleKind import org.matrix.android.sdk.internal.database.model.PushRuleEntity import org.matrix.android.sdk.internal.database.model.PushRuleEntityFields @@ -22,9 +25,6 @@ import org.matrix.android.sdk.internal.database.model.PushRulesEntity import org.matrix.android.sdk.internal.database.model.PushRulesEntityFields import org.matrix.android.sdk.internal.database.model.PusherEntity import org.matrix.android.sdk.internal.database.model.PusherEntityFields -import io.realm.Realm -import io.realm.RealmQuery -import io.realm.kotlin.where internal fun PusherEntity.Companion.where(realm: Realm, pushKey: String? = null): RealmQuery { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/ReadMarkerEntityQueries.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/ReadMarkerEntityQueries.kt index 35fb2b068bf..d80fe86aae1 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/ReadMarkerEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/ReadMarkerEntityQueries.kt @@ -16,12 +16,12 @@ package org.matrix.android.sdk.internal.database.query -import org.matrix.android.sdk.internal.database.model.ReadMarkerEntity -import org.matrix.android.sdk.internal.database.model.ReadMarkerEntityFields import io.realm.Realm import io.realm.RealmQuery import io.realm.kotlin.createObject import io.realm.kotlin.where +import org.matrix.android.sdk.internal.database.model.ReadMarkerEntity +import org.matrix.android.sdk.internal.database.model.ReadMarkerEntityFields internal fun ReadMarkerEntity.Companion.where(realm: Realm, roomId: String): RealmQuery { return realm.where() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/ReadQueries.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/ReadQueries.kt index 5423025823d..60096777d9f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/ReadQueries.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/ReadQueries.kt @@ -15,13 +15,13 @@ */ package org.matrix.android.sdk.internal.database.query +import io.realm.Realm +import io.realm.RealmConfiguration import org.matrix.android.sdk.api.session.events.model.LocalEcho import org.matrix.android.sdk.internal.database.model.ChunkEntity import org.matrix.android.sdk.internal.database.model.ReadMarkerEntity import org.matrix.android.sdk.internal.database.model.ReadReceiptEntity import org.matrix.android.sdk.internal.database.model.TimelineEventEntity -import io.realm.Realm -import io.realm.RealmConfiguration internal fun isEventRead(realmConfiguration: RealmConfiguration, userId: String?, @@ -39,19 +39,18 @@ internal fun isEventRead(realmConfiguration: RealmConfiguration, val liveChunk = ChunkEntity.findLastForwardChunkOfRoom(realm, roomId) ?: return@use val eventToCheck = liveChunk.timelineEvents.find(eventId) isEventRead = when { - eventToCheck == null -> { - // This can happen in case of fast lane Event - false - } + eventToCheck == null -> hasReadMissingEvent( + realm = realm, + latestChunkEntity = liveChunk, + roomId = roomId, + userId = userId, + eventId = eventId + ) eventToCheck.root?.sender == userId -> true else -> { - val readReceipt = ReadReceiptEntity.where(realm, roomId, userId).findFirst() - ?: return@use - val readReceiptIndex = liveChunk.timelineEvents.find(readReceipt.eventId)?.displayIndex - ?: Int.MIN_VALUE - val eventToCheckIndex = eventToCheck.displayIndex - - eventToCheckIndex <= readReceiptIndex + val readReceipt = ReadReceiptEntity.where(realm, roomId, userId).findFirst() ?: return@use + val readReceiptIndex = liveChunk.timelineEvents.find(readReceipt.eventId)?.displayIndex ?: Int.MIN_VALUE + eventToCheck.displayIndex <= readReceiptIndex } } } @@ -59,6 +58,24 @@ internal fun isEventRead(realmConfiguration: RealmConfiguration, return isEventRead } +/** + * Missing events can be caused by the latest timeline chunk no longer contain an older event or + * by fast lane eagerly displaying events before the database has finished updating + */ +private fun hasReadMissingEvent(realm: Realm, latestChunkEntity: ChunkEntity, roomId: String, userId: String, eventId: String): Boolean { + return realm.doesEventExistInChunkHistory(eventId) && realm.hasReadReceiptInLatestChunk(latestChunkEntity, roomId, userId) +} + +private fun Realm.doesEventExistInChunkHistory(eventId: String): Boolean { + return ChunkEntity.findIncludingEvent(this, eventId) != null +} + +private fun Realm.hasReadReceiptInLatestChunk(latestChunkEntity: ChunkEntity, roomId: String, userId: String): Boolean { + return ReadReceiptEntity.where(this, roomId = roomId, userId = userId).findFirst()?.let { + latestChunkEntity.timelineEvents.find(it.eventId) + } != null +} + internal fun isReadMarkerMoreRecent(realmConfiguration: RealmConfiguration, roomId: String?, eventId: String?): Boolean { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/ReadReceiptEntityQueries.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/ReadReceiptEntityQueries.kt index 1a5e8fcf89f..b180c06e2c2 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/ReadReceiptEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/ReadReceiptEntityQueries.kt @@ -16,12 +16,12 @@ package org.matrix.android.sdk.internal.database.query -import org.matrix.android.sdk.internal.database.model.ReadReceiptEntity -import org.matrix.android.sdk.internal.database.model.ReadReceiptEntityFields import io.realm.Realm import io.realm.RealmQuery import io.realm.kotlin.createObject import io.realm.kotlin.where +import org.matrix.android.sdk.internal.database.model.ReadReceiptEntity +import org.matrix.android.sdk.internal.database.model.ReadReceiptEntityFields internal fun ReadReceiptEntity.Companion.where(realm: Realm, roomId: String, userId: String): RealmQuery { return realm.where() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/ReadReceiptsSummaryEntityQueries.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/ReadReceiptsSummaryEntityQueries.kt index 97f84184033..a35df57b7b3 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/ReadReceiptsSummaryEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/ReadReceiptsSummaryEntityQueries.kt @@ -16,11 +16,11 @@ package org.matrix.android.sdk.internal.database.query -import org.matrix.android.sdk.internal.database.model.ReadReceiptsSummaryEntity -import org.matrix.android.sdk.internal.database.model.ReadReceiptsSummaryEntityFields import io.realm.Realm import io.realm.RealmQuery import io.realm.kotlin.where +import org.matrix.android.sdk.internal.database.model.ReadReceiptsSummaryEntity +import org.matrix.android.sdk.internal.database.model.ReadReceiptsSummaryEntityFields internal fun ReadReceiptsSummaryEntity.Companion.where(realm: Realm, eventId: String): RealmQuery { return realm.where() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/ReferencesAggregatedSummaryEntityQueries.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/ReferencesAggregatedSummaryEntityQueries.kt index 8b3929cd60f..14beb2d853c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/ReferencesAggregatedSummaryEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/ReferencesAggregatedSummaryEntityQueries.kt @@ -16,11 +16,11 @@ package org.matrix.android.sdk.internal.database.query -import org.matrix.android.sdk.internal.database.model.ReferencesAggregatedSummaryEntity -import org.matrix.android.sdk.internal.database.model.ReferencesAggregatedSummaryEntityFields import io.realm.Realm import io.realm.RealmQuery import io.realm.kotlin.where +import org.matrix.android.sdk.internal.database.model.ReferencesAggregatedSummaryEntity +import org.matrix.android.sdk.internal.database.model.ReferencesAggregatedSummaryEntityFields internal fun ReferencesAggregatedSummaryEntity.Companion.where(realm: Realm, eventId: String): RealmQuery { val query = realm.where() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/RoomEntityQueries.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/RoomEntityQueries.kt index a551f973793..08bb9e7ff32 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/RoomEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/RoomEntityQueries.kt @@ -16,13 +16,13 @@ package org.matrix.android.sdk.internal.database.query +import io.realm.Realm +import io.realm.RealmQuery +import io.realm.kotlin.where import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.internal.database.model.EventEntity import org.matrix.android.sdk.internal.database.model.RoomEntity import org.matrix.android.sdk.internal.database.model.RoomEntityFields -import io.realm.Realm -import io.realm.RealmQuery -import io.realm.kotlin.where internal fun RoomEntity.Companion.where(realm: Realm, roomId: String): RealmQuery { return realm.where() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/RoomMemberEntityQueries.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/RoomMemberEntityQueries.kt index 0747b126650..a19a9cf725c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/RoomMemberEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/RoomMemberEntityQueries.kt @@ -16,11 +16,11 @@ package org.matrix.android.sdk.internal.database.query -import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntity -import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntityFields import io.realm.Realm import io.realm.RealmQuery import io.realm.kotlin.where +import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntity +import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntityFields internal fun RoomMemberSummaryEntity.Companion.where(realm: Realm, roomId: String, userId: String? = null): RealmQuery { val query = realm diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/RoomSummaryEntityQueries.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/RoomSummaryEntityQueries.kt index 2af5dcf0ae5..5294f849afb 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/RoomSummaryEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/RoomSummaryEntityQueries.kt @@ -16,13 +16,13 @@ package org.matrix.android.sdk.internal.database.query -import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity -import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields import io.realm.Realm import io.realm.RealmQuery import io.realm.RealmResults import io.realm.kotlin.createObject import io.realm.kotlin.where +import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity +import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields internal fun RoomSummaryEntity.Companion.where(realm: Realm, roomId: String? = null): RealmQuery { val query = realm.where() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/ScalarTokenQuery.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/ScalarTokenQuery.kt index 53fd5250921..a7d85d676c7 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/ScalarTokenQuery.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/ScalarTokenQuery.kt @@ -16,11 +16,11 @@ package org.matrix.android.sdk.internal.database.query -import org.matrix.android.sdk.internal.database.model.ScalarTokenEntity -import org.matrix.android.sdk.internal.database.model.ScalarTokenEntityFields import io.realm.Realm import io.realm.RealmQuery import io.realm.kotlin.where +import org.matrix.android.sdk.internal.database.model.ScalarTokenEntity +import org.matrix.android.sdk.internal.database.model.ScalarTokenEntityFields internal fun ScalarTokenEntity.Companion.where(realm: Realm, serverUrl: String): RealmQuery { return realm diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/TimelineEventEntityQueries.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/TimelineEventEntityQueries.kt index 148232cf94e..aa1ce41bb7c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/TimelineEventEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/TimelineEventEntityQueries.kt @@ -119,8 +119,7 @@ internal fun RealmList.find(eventId: String): TimelineEvent internal fun TimelineEventEntity.Companion.findAllInRoomWithSendStates(realm: Realm, roomId: String, - sendStates: List) - : RealmResults { + sendStates: List): RealmResults { return whereRoomId(realm, roomId) .filterSendStates(sendStates) .findAll() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/UserDraftsEntityQueries.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/UserDraftsEntityQueries.kt index 4af4da0a22a..aa9d409a2ac 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/UserDraftsEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/UserDraftsEntityQueries.kt @@ -16,11 +16,11 @@ package org.matrix.android.sdk.internal.database.query -import org.matrix.android.sdk.internal.database.model.UserDraftsEntity -import org.matrix.android.sdk.internal.database.model.UserDraftsEntityFields import io.realm.Realm import io.realm.RealmQuery import io.realm.kotlin.where +import org.matrix.android.sdk.internal.database.model.UserDraftsEntity +import org.matrix.android.sdk.internal.database.model.UserDraftsEntityFields internal fun UserDraftsEntity.Companion.where(realm: Realm, roomId: String? = null): RealmQuery { val query = realm.where() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/UserEntityQueries.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/UserEntityQueries.kt index 6a5528e3db3..3159f89311f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/UserEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/query/UserEntityQueries.kt @@ -16,11 +16,11 @@ package org.matrix.android.sdk.internal.database.query -import org.matrix.android.sdk.internal.database.model.UserEntity -import org.matrix.android.sdk.internal.database.model.UserEntityFields import io.realm.Realm import io.realm.RealmQuery import io.realm.kotlin.where +import org.matrix.android.sdk.internal.database.model.UserEntity +import org.matrix.android.sdk.internal.database.model.UserEntityFields internal fun UserEntity.Companion.where(realm: Realm, userId: String): RealmQuery { return realm diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixModule.kt index b58fb3e6830..4cd960f4264 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/MatrixModule.kt @@ -20,11 +20,11 @@ import android.content.Context import android.content.res.Resources import dagger.Module import dagger.Provides -import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers -import org.matrix.android.sdk.internal.util.createBackgroundHandler import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.android.asCoroutineDispatcher import kotlinx.coroutines.asCoroutineDispatcher +import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers +import org.matrix.android.sdk.internal.util.createBackgroundHandler import org.matrix.olm.OlmManager import java.io.File import java.util.concurrent.Executors diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/NetworkModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/NetworkModule.kt index 0d0892b608d..fa59b94c175 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/NetworkModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/NetworkModule.kt @@ -20,15 +20,15 @@ import com.facebook.stetho.okhttp3.StethoInterceptor import com.squareup.moshi.Moshi import dagger.Module import dagger.Provides +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor import org.matrix.android.sdk.BuildConfig import org.matrix.android.sdk.api.MatrixConfiguration +import org.matrix.android.sdk.internal.network.ApiInterceptor import org.matrix.android.sdk.internal.network.TimeOutInterceptor import org.matrix.android.sdk.internal.network.UserAgentInterceptor import org.matrix.android.sdk.internal.network.interceptors.CurlLoggingInterceptor import org.matrix.android.sdk.internal.network.interceptors.FormattedJsonHttpLogger -import okhttp3.OkHttpClient -import okhttp3.logging.HttpLoggingInterceptor -import org.matrix.android.sdk.internal.network.ApiInterceptor import java.util.concurrent.TimeUnit @Module diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/legacy/DefaultLegacySessionImporter.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/legacy/DefaultLegacySessionImporter.kt index ad2aff4c9d9..445b6be8e87 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/legacy/DefaultLegacySessionImporter.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/legacy/DefaultLegacySessionImporter.kt @@ -17,6 +17,9 @@ package org.matrix.android.sdk.internal.legacy import android.content.Context +import io.realm.Realm +import io.realm.RealmConfiguration +import kotlinx.coroutines.runBlocking import org.matrix.android.sdk.api.auth.data.Credentials import org.matrix.android.sdk.api.auth.data.DiscoveryInformation import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig @@ -30,9 +33,6 @@ import org.matrix.android.sdk.internal.database.RealmKeysUtils import org.matrix.android.sdk.internal.legacy.riot.LoginStorage import org.matrix.android.sdk.internal.network.ssl.Fingerprint import org.matrix.android.sdk.internal.util.md5 -import io.realm.Realm -import io.realm.RealmConfiguration -import kotlinx.coroutines.runBlocking import timber.log.Timber import java.io.File import javax.inject.Inject diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/legacy/riot/WellKnown.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/legacy/riot/WellKnown.kt index 56d372faa57..17fd0925f88 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/legacy/riot/WellKnown.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/legacy/riot/WellKnown.kt @@ -75,9 +75,9 @@ class WellKnown { (config as? Map<*, *>)?.let { map -> val apiUrl = map["api_url"] as? String val uiUrl = map["ui_url"] as? String ?: apiUrl - if (apiUrl != null - && apiUrl.startsWith("https://") - && uiUrl!!.startsWith("https://")) { + if (apiUrl != null && + apiUrl.startsWith("https://") && + uiUrl!!.startsWith("https://")) { managers.add(WellKnownManagerConfig( apiUrl = apiUrl, uiUrl = uiUrl diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/AccessTokenInterceptor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/AccessTokenInterceptor.kt index b11fb6a5ee5..a34606a6bbd 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/AccessTokenInterceptor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/AccessTokenInterceptor.kt @@ -16,9 +16,9 @@ package org.matrix.android.sdk.internal.network -import org.matrix.android.sdk.internal.network.token.AccessTokenProvider import okhttp3.Interceptor import okhttp3.Response +import org.matrix.android.sdk.internal.network.token.AccessTokenProvider internal class AccessTokenInterceptor(private val accessTokenProvider: AccessTokenProvider) : Interceptor { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/NetworkConnectivityChecker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/NetworkConnectivityChecker.kt index c149ed2591b..e32f6be6fcb 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/NetworkConnectivityChecker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/NetworkConnectivityChecker.kt @@ -17,10 +17,10 @@ package org.matrix.android.sdk.internal.network import androidx.annotation.WorkerThread +import kotlinx.coroutines.runBlocking import org.matrix.android.sdk.internal.session.SessionScope import org.matrix.android.sdk.internal.session.homeserver.HomeServerPinger import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver -import kotlinx.coroutines.runBlocking import java.util.Collections import java.util.concurrent.atomic.AtomicBoolean import javax.inject.Inject @@ -43,8 +43,8 @@ interface NetworkConnectivityChecker { @SessionScope internal class DefaultNetworkConnectivityChecker @Inject constructor(private val homeServerPinger: HomeServerPinger, private val backgroundDetectionObserver: BackgroundDetectionObserver, - private val networkCallbackStrategy: NetworkCallbackStrategy) - : NetworkConnectivityChecker { + private val networkCallbackStrategy: NetworkCallbackStrategy) : + NetworkConnectivityChecker { private val hasInternetAccess = AtomicBoolean(true) private val listeners = Collections.synchronizedSet(LinkedHashSet()) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/Request.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/Request.kt index e045cebd3ed..927d9f7dd2c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/Request.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/Request.kt @@ -74,10 +74,10 @@ internal suspend inline fun executeRequest(globalErrorReceiver: GlobalErr currentRetryCount++ - if (exception is Failure.ServerError - && exception.httpCode == 429 - && exception.error.code == MatrixError.M_LIMIT_EXCEEDED - && currentRetryCount < maxRetriesCount) { + if (exception is Failure.ServerError && + exception.httpCode == 429 && + exception.error.code == MatrixError.M_LIMIT_EXCEEDED && + currentRetryCount < maxRetriesCount) { // 429, we can retry delay(exception.getRetryDelay(1_000)) } else if (canRetry && currentRetryCount < maxRetriesCount && exception.shouldBeRetried()) { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/RetrofitExtensions.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/RetrofitExtensions.kt index 8a031025279..60055be9ec1 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/RetrofitExtensions.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/RetrofitExtensions.kt @@ -91,8 +91,8 @@ private fun toFailure(errorBody: ResponseBody?, httpCode: Int, globalErrorReceiv matrixError.code == MatrixError.M_CONSENT_NOT_GIVEN && !matrixError.consentUri.isNullOrBlank() -> { globalErrorReceiver?.handleGlobalError(GlobalError.ConsentNotGivenError(matrixError.consentUri)) } - httpCode == HttpURLConnection.HTTP_UNAUTHORIZED /* 401 */ - && matrixError.code == MatrixError.M_UNKNOWN_TOKEN -> { + httpCode == HttpURLConnection.HTTP_UNAUTHORIZED && /* 401 */ + matrixError.code == MatrixError.M_UNKNOWN_TOKEN -> { globalErrorReceiver?.handleGlobalError(GlobalError.InvalidToken(matrixError.isSoftLogout.orFalse())) } matrixError.code == MatrixError.ORG_MATRIX_EXPIRED_ACCOUNT -> { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/UserAgentHolder.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/UserAgentHolder.kt index 1a884041280..00e15c283e2 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/UserAgentHolder.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/UserAgentHolder.kt @@ -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 */ @@ -73,14 +73,14 @@ 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 + ")") + userAgent = (appName + "/" + appVersion + " ( Flavour " + flavorDescription + + "; 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 + ")" } } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/httpclient/OkHttpClientUtil.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/httpclient/OkHttpClientUtil.kt index b4a2d191e23..3920c3b527b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/httpclient/OkHttpClientUtil.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/httpclient/OkHttpClientUtil.kt @@ -16,12 +16,12 @@ package org.matrix.android.sdk.internal.network.httpclient +import okhttp3.OkHttpClient import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig import org.matrix.android.sdk.internal.network.AccessTokenInterceptor import org.matrix.android.sdk.internal.network.interceptors.CurlLoggingInterceptor import org.matrix.android.sdk.internal.network.ssl.CertUtil import org.matrix.android.sdk.internal.network.token.AccessTokenProvider -import okhttp3.OkHttpClient import timber.log.Timber internal fun OkHttpClient.Builder.addAccessTokenInterceptor(accessTokenProvider: AccessTokenProvider): OkHttpClient.Builder { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/parsing/CheckNumberType.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/parsing/CheckNumberType.kt index 14d275e021b..27684bbf1af 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/parsing/CheckNumberType.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/parsing/CheckNumberType.kt @@ -20,7 +20,6 @@ import androidx.annotation.Nullable import com.squareup.moshi.JsonAdapter import com.squareup.moshi.JsonReader import com.squareup.moshi.JsonWriter - import com.squareup.moshi.Moshi import java.io.IOException import java.lang.reflect.Type diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/ssl/CertUtil.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/ssl/CertUtil.kt index 976751446b1..92c7f3f2369 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/ssl/CertUtil.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/ssl/CertUtil.kt @@ -16,9 +16,9 @@ package org.matrix.android.sdk.internal.network.ssl -import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig import okhttp3.ConnectionSpec import okhttp3.internal.tls.OkHostnameVerifier +import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig import timber.log.Timber import java.security.KeyStore import java.security.MessageDigest diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/ssl/PinnedTrustManager.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/ssl/PinnedTrustManager.kt index b1001bd39c8..6f245aa6d8d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/ssl/PinnedTrustManager.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/ssl/PinnedTrustManager.kt @@ -18,7 +18,6 @@ package org.matrix.android.sdk.internal.network.ssl import java.security.cert.CertificateException import java.security.cert.X509Certificate - import javax.net.ssl.X509TrustManager /** diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultSession.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultSession.kt index c2bd1e24ede..d41bf8a7021 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultSession.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/DefaultSession.kt @@ -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, + private val syncStatusService: Lazy, private val homeServerCapabilitiesService: Lazy, private val accountDataService: Lazy, private val _sharedSecretStorageService: Lazy, @@ -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(), @@ -223,6 +223,8 @@ internal class DefaultSession @Inject constructor( override fun getSyncStateLive() = getSyncThread().liveState() + override fun syncFlow() = getSyncThread().syncFlow() + override fun getSyncState() = getSyncThread().currentState() override fun hasAlreadySynced(): Boolean { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/EventInsertLiveProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/EventInsertLiveProcessor.kt index 7a687b774bf..a650fa2d64c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/EventInsertLiveProcessor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/EventInsertLiveProcessor.kt @@ -16,9 +16,9 @@ package org.matrix.android.sdk.internal.session +import io.realm.Realm import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.internal.database.model.EventInsertType -import io.realm.Realm internal interface EventInsertLiveProcessor { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionComponent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionComponent.kt index 9a936b73c23..71031a4614e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionComponent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionComponent.kt @@ -43,10 +43,9 @@ 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 import org.matrix.android.sdk.internal.session.room.send.MultipleEventSendingDispatcherWorker import org.matrix.android.sdk.internal.session.room.send.RedactEventWorker import org.matrix.android.sdk.internal.session.room.send.SendEventWorker @@ -115,8 +114,6 @@ internal interface SessionComponent { fun inject(worker: SendEventWorker) - fun inject(worker: SendRelationWorker) - fun inject(worker: MultipleEventSendingDispatcherWorker) fun inject(worker: RedactEventWorker) @@ -127,7 +124,7 @@ internal interface SessionComponent { fun inject(worker: SyncWorker) - fun inject(worker: AddHttpPusherWorker) + fun inject(worker: AddPusherWorker) fun inject(worker: SendVerificationMessageWorker) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionCoroutineScopeHolder.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionCoroutineScopeHolder.kt index 82a8f79fd51..2a741ddb9b1 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionCoroutineScopeHolder.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionCoroutineScopeHolder.kt @@ -20,12 +20,12 @@ import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancelChildren -import javax.inject.Inject import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.SessionLifecycleObserver +import javax.inject.Inject @SessionScope -internal class SessionCoroutineScopeHolder @Inject constructor(): SessionLifecycleObserver { +internal class SessionCoroutineScopeHolder @Inject constructor() : SessionLifecycleObserver { val scope: CoroutineScope = CoroutineScope(SupervisorJob()) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt index cb29cb4819c..dc59277f64e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt @@ -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 diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/account/ChangePasswordTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/account/ChangePasswordTask.kt index 02c37359988..7b21ba2e63b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/account/ChangePasswordTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/account/ChangePasswordTask.kt @@ -45,9 +45,9 @@ internal class DefaultChangePasswordTask @Inject constructor( } catch (throwable: Throwable) { val registrationFlowResponse = throwable.toRegistrationFlowResponse() - if (registrationFlowResponse != null + if (registrationFlowResponse != null && /* Avoid infinite loop */ - && changePasswordParams.auth?.session == null) { + changePasswordParams.auth?.session == null) { // Retry with authentication executeRequest(globalErrorReceiver) { accountAPI.changePassword( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/cache/CacheModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/cache/CacheModule.kt index 83c7d2b0b56..60adb212422 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/cache/CacheModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/cache/CacheModule.kt @@ -19,9 +19,9 @@ package org.matrix.android.sdk.internal.session.cache import dagger.Binds import dagger.Module import dagger.Provides +import io.realm.RealmConfiguration import org.matrix.android.sdk.api.session.cache.CacheService import org.matrix.android.sdk.internal.di.SessionDatabase -import io.realm.RealmConfiguration @Module internal abstract class CacheModule { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/cache/ClearCacheTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/cache/ClearCacheTask.kt index 894c3a47237..7f6b545c979 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/cache/ClearCacheTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/cache/ClearCacheTask.kt @@ -16,9 +16,9 @@ package org.matrix.android.sdk.internal.session.cache +import io.realm.RealmConfiguration import org.matrix.android.sdk.internal.database.awaitTransaction import org.matrix.android.sdk.internal.task.Task -import io.realm.RealmConfiguration import javax.inject.Inject internal interface ClearCacheTask : Task diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/CallEventProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/CallEventProcessor.kt index bdc254fc99c..3f199c5cce9 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/CallEventProcessor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/call/CallEventProcessor.kt @@ -17,10 +17,10 @@ package org.matrix.android.sdk.internal.session.call import io.realm.Realm +import org.matrix.android.sdk.api.logger.LoggerTag import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.internal.database.model.EventInsertType -import org.matrix.android.sdk.api.logger.LoggerTag import org.matrix.android.sdk.internal.session.EventInsertLiveProcessor import org.matrix.android.sdk.internal.session.SessionScope import timber.log.Timber @@ -29,8 +29,8 @@ import javax.inject.Inject private val loggerTag = LoggerTag("CallEventProcessor", LoggerTag.VOIP) @SessionScope -internal class CallEventProcessor @Inject constructor(private val callSignalingHandler: CallSignalingHandler) - : EventInsertLiveProcessor { +internal class CallEventProcessor @Inject constructor(private val callSignalingHandler: CallSignalingHandler) : + EventInsertLiveProcessor { private val allowedTypes = listOf( EventType.CALL_ANSWER, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/cleanup/CleanupSession.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/cleanup/CleanupSession.kt index 8870d92a357..d4374e0702f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/cleanup/CleanupSession.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/cleanup/CleanupSession.kt @@ -16,22 +16,22 @@ package org.matrix.android.sdk.internal.session.cleanup +import io.realm.Realm +import io.realm.RealmConfiguration import org.matrix.android.sdk.BuildConfig import org.matrix.android.sdk.internal.SessionManager import org.matrix.android.sdk.internal.auth.SessionParamsStore import org.matrix.android.sdk.internal.crypto.CryptoModule import org.matrix.android.sdk.internal.database.RealmKeysUtils import org.matrix.android.sdk.internal.di.CryptoDatabase -import org.matrix.android.sdk.internal.di.SessionDownloadsDirectory import org.matrix.android.sdk.internal.di.SessionDatabase +import org.matrix.android.sdk.internal.di.SessionDownloadsDirectory import org.matrix.android.sdk.internal.di.SessionFilesDirectory import org.matrix.android.sdk.internal.di.SessionId import org.matrix.android.sdk.internal.di.UserMd5 import org.matrix.android.sdk.internal.di.WorkManagerProvider import org.matrix.android.sdk.internal.session.SessionModule import org.matrix.android.sdk.internal.session.cache.ClearCacheTask -import io.realm.Realm -import io.realm.RealmConfiguration import timber.log.Timber import java.io.File import javax.inject.Inject diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/DefaultContentUrlResolver.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/DefaultContentUrlResolver.kt index e4efdaa254f..5c8cf99dc6c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/DefaultContentUrlResolver.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/DefaultContentUrlResolver.kt @@ -16,14 +16,14 @@ package org.matrix.android.sdk.internal.session.content +import org.matrix.android.sdk.api.MatrixUrls +import org.matrix.android.sdk.api.MatrixUrls.isMxcUrl import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig import org.matrix.android.sdk.api.session.content.ContentUrlResolver import org.matrix.android.sdk.internal.network.NetworkConstants import org.matrix.android.sdk.internal.util.ensureTrailingSlash import javax.inject.Inject -private const val MATRIX_CONTENT_URI_SCHEME = "mxc://" - internal class DefaultContentUrlResolver @Inject constructor(homeServerConnectionConfig: HomeServerConnectionConfig) : ContentUrlResolver { private val baseUrl = homeServerConnectionConfig.homeServerUriBase.toString().ensureTrailingSlash() @@ -33,7 +33,7 @@ internal class DefaultContentUrlResolver @Inject constructor(homeServerConnectio override fun resolveFullSize(contentUrl: String?): String? { return contentUrl // do not allow non-mxc content URLs - ?.takeIf { it.isValidMatrixContentUrl() } + ?.takeIf { it.isMxcUrl() } ?.let { resolve( contentUrl = it, @@ -45,7 +45,7 @@ internal class DefaultContentUrlResolver @Inject constructor(homeServerConnectio override fun resolveThumbnail(contentUrl: String?, width: Int, height: Int, method: ContentUrlResolver.ThumbnailMethod): String? { return contentUrl // do not allow non-mxc content URLs - ?.takeIf { it.isValidMatrixContentUrl() } + ?.takeIf { it.isMxcUrl() } ?.let { resolve( contentUrl = it, @@ -58,7 +58,7 @@ internal class DefaultContentUrlResolver @Inject constructor(homeServerConnectio private fun resolve(contentUrl: String, prefix: String, params: String = ""): String? { - var serverAndMediaId = contentUrl.removePrefix(MATRIX_CONTENT_URI_SCHEME) + var serverAndMediaId = contentUrl.removePrefix(MatrixUrls.MATRIX_CONTENT_URI_SCHEME) val fragmentOffset = serverAndMediaId.indexOf("#") var fragment = "" if (fragmentOffset >= 0) { @@ -68,8 +68,4 @@ internal class DefaultContentUrlResolver @Inject constructor(homeServerConnectio return baseUrl + prefix + serverAndMediaId + params + fragment } - - private fun String.isValidMatrixContentUrl(): Boolean { - return startsWith(MATRIX_CONTENT_URI_SCHEME) - } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/FileUploader.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/FileUploader.kt index 6a4dd263920..bdebb0addf5 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/FileUploader.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/FileUploader.kt @@ -67,8 +67,8 @@ internal class FileUploader @Inject constructor( // Check size limit val maxUploadFileSize = homeServerCapabilitiesService.getHomeServerCapabilities().maxUploadFileSize - if (maxUploadFileSize != HomeServerCapabilities.MAX_UPLOAD_FILE_SIZE_UNKNOWN - && file.length() > maxUploadFileSize) { + if (maxUploadFileSize != HomeServerCapabilities.MAX_UPLOAD_FILE_SIZE_UNKNOWN && + file.length() > maxUploadFileSize) { // Known limitation and file too big for the server, save the pain to upload it throw Failure.ServerError( error = MatrixError( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt index f14c85cf804..11c200c54b0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/content/UploadContentWorker.kt @@ -63,8 +63,8 @@ private data class NewAttachmentAttributes( * Possible previous worker: None * Possible next worker : Always [MultipleEventSendingDispatcherWorker] */ -internal class UploadContentWorker(val context: Context, params: WorkerParameters) - : SessionSafeCoroutineWorker(context, params, Params::class.java) { +internal class UploadContentWorker(val context: Context, params: WorkerParameters) : + SessionSafeCoroutineWorker(context, params, Params::class.java) { @JsonClass(generateAdapter = true) internal data class Params( @@ -157,10 +157,10 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter params.attachment.size ) - if (attachment.type == ContentAttachmentData.Type.IMAGE + if (attachment.type == ContentAttachmentData.Type.IMAGE && // Do not compress gif - && attachment.mimeType != MimeTypes.Gif - && params.compressBeforeSending) { + attachment.mimeType != MimeTypes.Gif && + params.compressBeforeSending) { notifyTracker(params) { contentUploadStateTracker.setCompressingImage(it) } fileToUpload = imageCompressor.compress(workingFile, MAX_IMAGE_SIZE, MAX_IMAGE_SIZE) @@ -177,10 +177,10 @@ internal class UploadContentWorker(val context: Context, params: WorkerParameter } } .also { filesToDelete.add(it) } - } else if (attachment.type == ContentAttachmentData.Type.VIDEO + } else if (attachment.type == ContentAttachmentData.Type.VIDEO && // Do not compress gif - && attachment.mimeType != MimeTypes.Gif - && params.compressBeforeSending) { + attachment.mimeType != MimeTypes.Gif && + params.compressBeforeSending) { fileToUpload = videoCompressor.compress(workingFile, object : ProgressListener { override fun onProgress(progress: Int, total: Int) { notifyTracker(params) { contentUploadStateTracker.setCompressingVideo(it, progress.toFloat()) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/displayname/DisplayNameResolver.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/displayname/DisplayNameResolver.kt new file mode 100644 index 00000000000..76d956f9a5c --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/displayname/DisplayNameResolver.kt @@ -0,0 +1,37 @@ +/* + * 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.displayname + +import org.matrix.android.sdk.api.MatrixConfiguration +import org.matrix.android.sdk.api.util.MatrixItem +import javax.inject.Inject + +internal class DisplayNameResolver @Inject constructor( + private val matrixConfiguration: MatrixConfiguration +) { + fun getBestName(matrixItem: MatrixItem): String { + return if (matrixItem is MatrixItem.GroupItem || matrixItem is MatrixItem.RoomAliasItem) { + // Best name is the id, and we keep the displayName of the room for the case we need the first letter + matrixItem.id + } else { + matrixItem.displayName?.takeIf { it.isNotBlank() } + ?: matrixConfiguration.matrixItemDisplayNameFallbackProvider?.getDefaultName(matrixItem) + ?: matrixItem.id + } + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/DefaultFilterRepository.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/DefaultFilterRepository.kt index 095c39a4850..1d1bb0e7150 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/DefaultFilterRepository.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/DefaultFilterRepository.kt @@ -17,14 +17,14 @@ package org.matrix.android.sdk.internal.session.filter import com.zhuinden.monarchy.Monarchy +import io.realm.Realm +import io.realm.kotlin.where import org.matrix.android.sdk.internal.database.model.FilterEntity import org.matrix.android.sdk.internal.database.model.FilterEntityFields import org.matrix.android.sdk.internal.database.query.get import org.matrix.android.sdk.internal.database.query.getOrCreate import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.util.awaitTransaction -import io.realm.Realm -import io.realm.kotlin.where import javax.inject.Inject internal class DefaultFilterRepository @Inject constructor(@SessionDatabase private val monarchy: Monarchy) : FilterRepository { @@ -33,9 +33,9 @@ internal class DefaultFilterRepository @Inject constructor(@SessionDatabase priv return Realm.getInstance(monarchy.realmConfiguration).use { realm -> val filterEntity = FilterEntity.get(realm) // Filter has changed, or no filter Id yet - filterEntity == null - || filterEntity.filterBodyJson != filter.toJSONString() - || filterEntity.filterId.isBlank() + filterEntity == null || + filterEntity.filterBodyJson != filter.toJSONString() || + filterEntity.filterId.isBlank() }.also { hasChanged -> if (hasChanged) { // Filter is new or has changed, store it and reset the filter Id. diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/EventFilter.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/EventFilter.kt index 1be62304a19..37630ef8bad 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/EventFilter.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/EventFilter.kt @@ -50,10 +50,10 @@ data class EventFilter( @Json(name = "not_types") val notTypes: List? = null ) { fun hasData(): Boolean { - return limit != null - || senders != null - || notSenders != null - || types != null - || notTypes != null + return limit != null || + senders != null || + notSenders != null || + types != null || + notTypes != null } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/RoomEventFilter.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/RoomEventFilter.kt index d6089f9f5b6..7047d38260e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/RoomEventFilter.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/RoomEventFilter.kt @@ -73,14 +73,14 @@ data class RoomEventFilter( } fun hasData(): Boolean { - return (limit != null - || notSenders != null - || notTypes != null - || senders != null - || types != null - || rooms != null - || notRooms != null - || containsUrl != null - || lazyLoadMembers != null) + return (limit != null || + notSenders != null || + notTypes != null || + senders != null || + types != null || + rooms != null || + notRooms != null || + containsUrl != null || + lazyLoadMembers != null) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/RoomFilter.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/RoomFilter.kt index fbf22fde51f..2c56a30d394 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/RoomFilter.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/filter/RoomFilter.kt @@ -59,12 +59,12 @@ data class RoomFilter( ) { fun hasData(): Boolean { - return (notRooms != null - || rooms != null - || ephemeral != null - || includeLeave != null - || state != null - || timeline != null - || accountData != null) + return (notRooms != null || + rooms != null || + ephemeral != null || + includeLeave != null || + state != null || + timeline != null || + accountData != null) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/DefaultGroupService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/DefaultGroupService.kt index 425d6a9acaa..8dc5f3931d2 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/DefaultGroupService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/DefaultGroupService.kt @@ -18,6 +18,8 @@ package org.matrix.android.sdk.internal.session.group import androidx.lifecycle.LiveData import com.zhuinden.monarchy.Monarchy +import io.realm.Realm +import io.realm.RealmQuery import org.matrix.android.sdk.api.session.group.Group import org.matrix.android.sdk.api.session.group.GroupService import org.matrix.android.sdk.api.session.group.GroupSummaryQueryParams @@ -30,8 +32,6 @@ import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.query.process import org.matrix.android.sdk.internal.util.fetchCopyMap -import io.realm.Realm -import io.realm.RealmQuery import javax.inject.Inject internal class DefaultGroupService @Inject constructor(@SessionDatabase private val monarchy: Monarchy, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/GetGroupDataWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/GetGroupDataWorker.kt index d6b9363d54a..338f43bdbb5 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/GetGroupDataWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/group/GetGroupDataWorker.kt @@ -28,8 +28,8 @@ import javax.inject.Inject * Possible previous worker: None * Possible next worker : None */ -internal class GetGroupDataWorker(context: Context, params: WorkerParameters) - : SessionSafeCoroutineWorker(context, params, Params::class.java) { +internal class GetGroupDataWorker(context: Context, params: WorkerParameters) : + SessionSafeCoroutineWorker(context, params, Params::class.java) { @JsonClass(generateAdapter = true) internal data class Params( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/HomeServerPinger.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/HomeServerPinger.kt index bb526adf4a8..70e1e551aa0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/HomeServerPinger.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/homeserver/HomeServerPinger.kt @@ -16,10 +16,10 @@ package org.matrix.android.sdk.internal.session.homeserver +import kotlinx.coroutines.launch import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.task.TaskExecutor -import kotlinx.coroutines.launch import javax.inject.Inject internal class HomeServerPinger @Inject constructor(private val taskExecutor: TaskExecutor, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/DefaultIdentityService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/DefaultIdentityService.kt index fdb6caf53fe..da37948cd4f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/DefaultIdentityService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/DefaultIdentityService.kt @@ -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): List { + if (getCurrentIdentityServerUrl() == null) throw IdentityServiceError.NoIdentityServerConfigured + if (!getUserConsent()) { throw IdentityServiceError.UserConsentNotProvided } @@ -316,12 +320,12 @@ internal class DefaultIdentityService @Inject constructor( } private fun Throwable.isInvalidToken(): Boolean { - return this is Failure.ServerError - && httpCode == HttpsURLConnection.HTTP_UNAUTHORIZED /* 401 */ + return this is Failure.ServerError && + httpCode == HttpsURLConnection.HTTP_UNAUTHORIZED /* 401 */ } private fun Throwable.isTermsNotSigned(): Boolean { - return this is Failure.ServerError - && httpCode == HttpsURLConnection.HTTP_FORBIDDEN /* 403 */ - && error.code == MatrixError.M_TERMS_NOT_SIGNED + return this is Failure.ServerError && + httpCode == HttpsURLConnection.HTTP_FORBIDDEN && /* 403 */ + error.code == MatrixError.M_TERMS_NOT_SIGNED } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/EnsureIdentityToken.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/EnsureIdentityToken.kt index 5e1434403e3..657d1f3ac74 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/EnsureIdentityToken.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/EnsureIdentityToken.kt @@ -17,13 +17,13 @@ package org.matrix.android.sdk.internal.session.identity import dagger.Lazy +import okhttp3.OkHttpClient import org.matrix.android.sdk.api.session.identity.IdentityServiceError import org.matrix.android.sdk.internal.di.UnauthenticatedWithCertificate import org.matrix.android.sdk.internal.network.RetrofitFactory import org.matrix.android.sdk.internal.session.identity.data.IdentityStore import org.matrix.android.sdk.internal.session.openid.GetOpenIdTokenTask import org.matrix.android.sdk.internal.task.Task -import okhttp3.OkHttpClient import javax.inject.Inject internal interface EnsureIdentityTokenTask : Task diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityBulkLookupTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityBulkLookupTask.kt index 114695062c8..f6ef370f8d3 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityBulkLookupTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityBulkLookupTask.kt @@ -117,8 +117,8 @@ internal class DefaultIdentityBulkLookupTask @Inject constructor( return withOlmUtility { olmUtility -> threePids.map { threePid -> base64ToBase64Url( - olmUtility.sha256(threePid.value.lowercase(Locale.ROOT) - + " " + threePid.toMedium() + " " + pepper) + olmUtility.sha256(threePid.value.lowercase(Locale.ROOT) + + " " + threePid.toMedium() + " " + pepper) ) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityModule.kt index 4d664b76bed..19e602d7a71 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/IdentityModule.kt @@ -19,6 +19,8 @@ package org.matrix.android.sdk.internal.session.identity import dagger.Binds import dagger.Module import dagger.Provides +import io.realm.RealmConfiguration +import okhttp3.OkHttpClient import org.matrix.android.sdk.internal.database.RealmKeysUtils import org.matrix.android.sdk.internal.di.AuthenticatedIdentity import org.matrix.android.sdk.internal.di.IdentityDatabase @@ -32,8 +34,6 @@ 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.db.IdentityRealmModule import org.matrix.android.sdk.internal.session.identity.db.RealmIdentityStore -import io.realm.RealmConfiguration -import okhttp3.OkHttpClient import org.matrix.android.sdk.internal.session.identity.db.RealmIdentityStoreMigration import java.io.File diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/IdentityPendingBindingEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/IdentityPendingBindingEntity.kt index be68e17a495..fcc91b0121c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/IdentityPendingBindingEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/IdentityPendingBindingEntity.kt @@ -16,10 +16,10 @@ package org.matrix.android.sdk.internal.session.identity.db -import org.matrix.android.sdk.api.session.identity.ThreePid -import org.matrix.android.sdk.api.session.identity.toMedium import io.realm.RealmObject import io.realm.annotations.PrimaryKey +import org.matrix.android.sdk.api.session.identity.ThreePid +import org.matrix.android.sdk.api.session.identity.toMedium internal open class IdentityPendingBindingEntity( @PrimaryKey var threePid: String = "", diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/IdentityPendingBindingEntityQuery.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/IdentityPendingBindingEntityQuery.kt index aa2f4dd5b29..8d4afc7beb8 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/IdentityPendingBindingEntityQuery.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/IdentityPendingBindingEntityQuery.kt @@ -16,10 +16,10 @@ package org.matrix.android.sdk.internal.session.identity.db -import org.matrix.android.sdk.api.session.identity.ThreePid import io.realm.Realm import io.realm.kotlin.createObject import io.realm.kotlin.where +import org.matrix.android.sdk.api.session.identity.ThreePid internal fun IdentityPendingBindingEntity.Companion.get(realm: Realm, threePid: ThreePid): IdentityPendingBindingEntity? { return realm.where() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/RealmIdentityStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/RealmIdentityStore.kt index 2fa3fc0cfb2..ce8b78b2d0e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/RealmIdentityStore.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/identity/db/RealmIdentityStore.kt @@ -16,15 +16,15 @@ package org.matrix.android.sdk.internal.session.identity.db +import io.realm.Realm +import io.realm.RealmConfiguration import org.matrix.android.sdk.api.session.identity.ThreePid import org.matrix.android.sdk.internal.di.IdentityDatabase import org.matrix.android.sdk.internal.session.SessionScope -import org.matrix.android.sdk.internal.session.identity.data.IdentityPendingBinding import org.matrix.android.sdk.internal.session.identity.data.IdentityData +import org.matrix.android.sdk.internal.session.identity.data.IdentityPendingBinding import org.matrix.android.sdk.internal.session.identity.data.IdentityStore import org.matrix.android.sdk.internal.session.identity.model.IdentityHashDetailResponse -import io.realm.Realm -import io.realm.RealmConfiguration import javax.inject.Inject @SessionScope diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/initsync/DefaultInitialSyncProgressService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/initsync/DefaultSyncStatusService.kt similarity index 78% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/initsync/DefaultInitialSyncProgressService.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/initsync/DefaultSyncStatusService.kt index eb3e3066b12..079b0d0115e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/initsync/DefaultInitialSyncProgressService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/initsync/DefaultSyncStatusService.kt @@ -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() + private val status = MutableLiveData() private var rootTask: TaskInfo? = null - override fun getInitialSyncProgressStatus(): LiveData { + override fun getSyncStatusLive(): LiveData { 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) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/integrationmanager/IntegrationManager.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/integrationmanager/IntegrationManager.kt index b654b8610de..30b15891696 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/integrationmanager/IntegrationManager.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/integrationmanager/IntegrationManager.kt @@ -22,6 +22,9 @@ import androidx.lifecycle.LifecycleRegistry import com.zhuinden.monarchy.Monarchy import org.matrix.android.sdk.api.MatrixConfiguration import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.SessionLifecycleObserver +import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent +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.integrationmanager.IntegrationManagerConfig import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerService @@ -30,12 +33,9 @@ import org.matrix.android.sdk.api.session.widgets.model.WidgetType import org.matrix.android.sdk.internal.database.model.WellknownIntegrationManagerConfigEntity import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.extensions.observeNotNull -import org.matrix.android.sdk.api.session.SessionLifecycleObserver import org.matrix.android.sdk.internal.session.SessionScope -import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes -import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent -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.session.widgets.helper.WidgetFactory import org.matrix.android.sdk.internal.session.widgets.helper.extractWidgetSequence import timber.log.Timber @@ -58,8 +58,8 @@ internal class IntegrationManager @Inject constructor(matrixConfiguration: Matri @SessionDatabase private val monarchy: Monarchy, private val updateUserAccountDataTask: UpdateUserAccountDataTask, private val accountDataDataSource: UserAccountDataDataSource, - private val widgetFactory: WidgetFactory) - : SessionLifecycleObserver { + private val widgetFactory: WidgetFactory) : + SessionLifecycleObserver { private val currentConfigs = ArrayList() private val lifecycleOwner: LifecycleOwner = LifecycleOwner { lifecycleRegistry } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/integrationmanager/IntegrationManagerConfigExtractor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/integrationmanager/IntegrationManagerConfigExtractor.kt index f7a8b68515f..8c63dbeb0d9 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/integrationmanager/IntegrationManagerConfigExtractor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/integrationmanager/IntegrationManagerConfigExtractor.kt @@ -29,9 +29,9 @@ internal class IntegrationManagerConfigExtractor @Inject constructor() { (config as? Map<*, *>)?.let { map -> val apiUrl = map["api_url"] as? String val uiUrl = map["ui_url"] as? String ?: apiUrl - if (apiUrl != null - && apiUrl.startsWith("https://") - && uiUrl!!.startsWith("https://")) { + if (apiUrl != null && + apiUrl.startsWith("https://") && + uiUrl!!.startsWith("https://")) { return WellknownIntegrationManagerConfigEntity( apiUrl = apiUrl, uiUrl = uiUrl diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/UrlsExtractor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/UrlsExtractor.kt index d1fb5b98ffa..85a126ef782 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/UrlsExtractor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/UrlsExtractor.kt @@ -33,9 +33,9 @@ internal class UrlsExtractor @Inject constructor() { return event.takeIf { it.root.getClearType() == EventType.MESSAGE } ?.getLastMessageContent() ?.takeIf { - it.msgType == MessageType.MSGTYPE_TEXT - || it.msgType == MessageType.MSGTYPE_NOTICE - || it.msgType == MessageType.MSGTYPE_EMOTE + it.msgType == MessageType.MSGTYPE_TEXT || + it.msgType == MessageType.MSGTYPE_NOTICE || + it.msgType == MessageType.MSGTYPE_EMOTE } ?.let { messageContent -> if (event.isReply()) { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/notification/ProcessEventForPushTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/notification/ProcessEventForPushTask.kt index 0ece07fc157..1321f8dd624 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/notification/ProcessEventForPushTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/notification/ProcessEventForPushTask.kt @@ -18,8 +18,8 @@ package org.matrix.android.sdk.internal.session.notification import org.matrix.android.sdk.api.pushrules.rest.PushRule import org.matrix.android.sdk.api.session.events.model.EventType +import org.matrix.android.sdk.api.session.sync.model.RoomsSyncResponse import org.matrix.android.sdk.internal.di.UserId -import org.matrix.android.sdk.internal.session.sync.model.RoomsSyncResponse import org.matrix.android.sdk.internal.task.Task import timber.log.Timber import javax.inject.Inject diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/openid/DefaultOpenIdService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/openid/DefaultOpenIdService.kt index b90a2435f7d..afff0f35152 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/openid/DefaultOpenIdService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/openid/DefaultOpenIdService.kt @@ -20,7 +20,7 @@ import org.matrix.android.sdk.api.session.openid.OpenIdService import org.matrix.android.sdk.api.session.openid.OpenIdToken import javax.inject.Inject -internal class DefaultOpenIdService @Inject constructor(private val getOpenIdTokenTask: GetOpenIdTokenTask): OpenIdService { +internal class DefaultOpenIdService @Inject constructor(private val getOpenIdTokenTask: GetOpenIdTokenTask) : OpenIdService { override suspend fun getOpenIdToken(): OpenIdToken { return getOpenIdTokenTask.execute(Unit) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/permalinks/DefaultPermalinkService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/permalinks/DefaultPermalinkService.kt index 134da4ce51b..144ebb54042 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/permalinks/DefaultPermalinkService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/permalinks/DefaultPermalinkService.kt @@ -18,33 +18,29 @@ package org.matrix.android.sdk.internal.session.permalinks import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.permalinks.PermalinkService -import org.matrix.android.sdk.api.session.permalinks.PermalinkService.Companion.MATRIX_TO_URL_BASE import javax.inject.Inject internal class DefaultPermalinkService @Inject constructor( private val permalinkFactory: PermalinkFactory ) : PermalinkService { - override fun createPermalink(event: Event): String? { - return permalinkFactory.createPermalink(event) + override fun createPermalink(event: Event, forceMatrixTo: Boolean): String? { + return permalinkFactory.createPermalink(event, forceMatrixTo) } - override fun createPermalink(id: String): String? { - return permalinkFactory.createPermalink(id) + override fun createPermalink(id: String, forceMatrixTo: Boolean): String? { + return permalinkFactory.createPermalink(id, forceMatrixTo) } - override fun createRoomPermalink(roomId: String, viaServers: List?): String? { - return permalinkFactory.createRoomPermalink(roomId, viaServers) + override fun createRoomPermalink(roomId: String, viaServers: List?, forceMatrixTo: Boolean): String? { + return permalinkFactory.createRoomPermalink(roomId, viaServers, forceMatrixTo) } - override fun createPermalink(roomId: String, eventId: String): String { - return permalinkFactory.createPermalink(roomId, eventId) + override fun createPermalink(roomId: String, eventId: String, forceMatrixTo: Boolean): String { + return permalinkFactory.createPermalink(roomId, eventId, forceMatrixTo) } override fun getLinkedId(url: String): String? { - return url - .takeIf { it.startsWith(MATRIX_TO_URL_BASE) } - ?.substring(MATRIX_TO_URL_BASE.length) - ?.substringBeforeLast("?") + return permalinkFactory.getLinkedId(url) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/permalinks/PermalinkFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/permalinks/PermalinkFactory.kt index 639e45582a0..39c1ddfdce3 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/permalinks/PermalinkFactory.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/permalinks/PermalinkFactory.kt @@ -16,7 +16,11 @@ package org.matrix.android.sdk.internal.session.permalinks +import org.matrix.android.sdk.api.MatrixConfiguration +import org.matrix.android.sdk.api.MatrixPatterns import org.matrix.android.sdk.api.session.events.model.Event +import org.matrix.android.sdk.api.session.permalinks.PermalinkData +import org.matrix.android.sdk.api.session.permalinks.PermalinkParser import org.matrix.android.sdk.api.session.permalinks.PermalinkService.Companion.MATRIX_TO_URL_BASE import org.matrix.android.sdk.internal.di.UserId import javax.inject.Inject @@ -24,28 +28,44 @@ import javax.inject.Inject internal class PermalinkFactory @Inject constructor( @UserId private val userId: String, - private val viaParameterFinder: ViaParameterFinder + private val viaParameterFinder: ViaParameterFinder, + private val matrixConfiguration: MatrixConfiguration ) { - fun createPermalink(event: Event): String? { + fun createPermalink(event: Event, forceMatrixTo: Boolean): String? { if (event.roomId.isNullOrEmpty() || event.eventId.isNullOrEmpty()) { return null } - return createPermalink(event.roomId, event.eventId) + return createPermalink(event.roomId, event.eventId, forceMatrixTo) } - fun createPermalink(id: String): String? { - return if (id.isEmpty()) { - null - } else MATRIX_TO_URL_BASE + escape(id) + fun createPermalink(id: String, forceMatrixTo: Boolean): String? { + return when { + id.isEmpty() -> null + !useClientFormat(forceMatrixTo) -> MATRIX_TO_URL_BASE + escape(id) + else -> { + buildString { + append(matrixConfiguration.clientPermalinkBaseUrl) + when { + MatrixPatterns.isRoomId(id) || MatrixPatterns.isRoomAlias(id) -> append(ROOM_PATH) + MatrixPatterns.isUserId(id) -> append(USER_PATH) + MatrixPatterns.isGroupId(id) -> append(GROUP_PATH) + } + append(escape(id)) + } + } + } } - fun createRoomPermalink(roomId: String, via: List? = null): String? { + fun createRoomPermalink(roomId: String, via: List? = null, forceMatrixTo: Boolean): String? { return if (roomId.isEmpty()) { null } else { buildString { - append(MATRIX_TO_URL_BASE) + append(baseUrl(forceMatrixTo)) + if (useClientFormat(forceMatrixTo)) { + append(ROOM_PATH) + } append(escape(roomId)) append( via?.takeIf { it.isNotEmpty() }?.let { viaParameterFinder.asUrlViaParameters(it) } @@ -55,16 +75,34 @@ internal class PermalinkFactory @Inject constructor( } } - fun createPermalink(roomId: String, eventId: String): String { - return MATRIX_TO_URL_BASE + escape(roomId) + "/" + escape(eventId) + viaParameterFinder.computeViaParams(userId, roomId) + fun createPermalink(roomId: String, eventId: String, forceMatrixTo: Boolean): String { + return buildString { + append(baseUrl(forceMatrixTo)) + if (useClientFormat(forceMatrixTo)) { + append(ROOM_PATH) + } + append(escape(roomId)) + append("/") + append(escape(eventId)) + append(viaParameterFinder.computeViaParams(userId, roomId)) + } } fun getLinkedId(url: String): String? { - val isSupported = url.startsWith(MATRIX_TO_URL_BASE) - - return if (isSupported) { - url.substring(MATRIX_TO_URL_BASE.length) - } else null + val clientBaseUrl = matrixConfiguration.clientPermalinkBaseUrl + return when { + url.startsWith(MATRIX_TO_URL_BASE) -> url.substring(MATRIX_TO_URL_BASE.length) + clientBaseUrl != null && url.startsWith(clientBaseUrl) -> { + when (PermalinkParser.parse(url)) { + is PermalinkData.GroupLink -> url.substring(clientBaseUrl.length + GROUP_PATH.length) + is PermalinkData.RoomLink -> url.substring(clientBaseUrl.length + ROOM_PATH.length) + is PermalinkData.UserLink -> url.substring(clientBaseUrl.length + USER_PATH.length) + else -> null + } + } + else -> null + } + ?.substringBeforeLast("?") } /** @@ -86,4 +124,28 @@ internal class PermalinkFactory @Inject constructor( private fun unescape(id: String): String { return id.replace("%2F", "/") } + + /** + * Get the permalink base URL according to the potential one in [MatrixConfiguration.clientPermalinkBaseUrl] + * and the [forceMatrixTo] parameter. + * + * @param forceMatrixTo whether we should force using matrix.to base URL. + * + * @return the permalink base URL. + */ + private fun baseUrl(forceMatrixTo: Boolean): String { + return matrixConfiguration.clientPermalinkBaseUrl + ?.takeUnless { forceMatrixTo } + ?: MATRIX_TO_URL_BASE + } + + private fun useClientFormat(forceMatrixTo: Boolean): Boolean { + return !forceMatrixTo && matrixConfiguration.clientPermalinkBaseUrl != null + } + + companion object { + private const val ROOM_PATH = "room/" + private const val USER_PATH = "user/" + private const val GROUP_PATH = "group/" + } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/FinalizeAddingThreePidTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/FinalizeAddingThreePidTask.kt index 5f063365e00..6ff4efaf113 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/FinalizeAddingThreePidTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/profile/FinalizeAddingThreePidTask.kt @@ -17,12 +17,12 @@ package org.matrix.android.sdk.internal.session.profile import com.zhuinden.monarchy.Monarchy +import org.matrix.android.sdk.api.auth.UIABaseAuth import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.failure.toRegistrationFlowResponse import org.matrix.android.sdk.api.session.identity.ThreePid import org.matrix.android.sdk.internal.auth.registration.handleUIA -import org.matrix.android.sdk.api.auth.UIABaseAuth import org.matrix.android.sdk.internal.database.model.PendingThreePidEntity import org.matrix.android.sdk.internal.database.model.PendingThreePidEntityFields import org.matrix.android.sdk.internal.di.SessionDatabase @@ -71,8 +71,8 @@ internal class DefaultFinalizeAddingThreePidTask @Inject constructor( } true } catch (throwable: Throwable) { - if (params.userInteractiveAuthInterceptor == null - || !handleUIA( + if (params.userInteractiveAuthInterceptor == null || + !handleUIA( failure = throwable, interceptor = params.userInteractiveAuthInterceptor, retryBlock = { authUpdate -> diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddHttpPusherWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherWorker.kt similarity index 95% rename from matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddHttpPusherWorker.kt rename to matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherWorker.kt index c9d7ad21938..63fd855c08a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddHttpPusherWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/AddPusherWorker.kt @@ -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(context, params, Params::class.java) { +internal class AddPusherWorker(context: Context, params: WorkerParameters) : + SessionSafeCoroutineWorker(context, params, Params::class.java) { @JsonClass(generateAdapter = true) internal data class Params( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/DefaultPushersService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/DefaultPushersService.kt index a772cf5ebb1..9a50abfe357 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/DefaultPushersService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/DefaultPushersService.kt @@ -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() + private fun addPusher(pusher: JsonPusher): UUID { + pusher.validateParameters() + val params = AddPusherWorker.Params(sessionId, pusher) + val request = workManagerProvider.matrixOneTimeWorkRequestBuilder() .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) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/JsonPusherData.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/JsonPusherData.kt index c8d4d77fb1a..42a8fa6ff39 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/JsonPusherData.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/JsonPusherData.kt @@ -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 ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/RemovePusherTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/RemovePusherTask.kt index 3a2ebf40c2f..057c3090785 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/RemovePusherTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/RemovePusherTask.kt @@ -17,16 +17,16 @@ package org.matrix.android.sdk.internal.session.pushers import com.zhuinden.monarchy.Monarchy +import io.realm.Realm import org.matrix.android.sdk.api.session.pushers.PusherState import org.matrix.android.sdk.internal.database.mapper.asDomain import org.matrix.android.sdk.internal.database.model.PusherEntity import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.di.SessionDatabase +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 org.matrix.android.sdk.internal.util.awaitTransaction -import io.realm.Realm -import org.matrix.android.sdk.internal.network.GlobalErrorReceiver import javax.inject.Inject internal interface RemovePusherTask : Task { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt index f69949cbb6c..5b2499c1300 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt @@ -93,13 +93,15 @@ internal class DefaultRoomService @Inject constructor( return roomSummaryDataSource.getRoomSummariesLive(queryParams) } - override fun getPagedRoomSummariesLive(queryParams: RoomSummaryQueryParams, pagedListConfig: PagedList.Config, sortOrder: RoomSortOrder) - : LiveData> { + override fun getPagedRoomSummariesLive(queryParams: RoomSummaryQueryParams, + pagedListConfig: PagedList.Config, + sortOrder: RoomSortOrder): LiveData> { return roomSummaryDataSource.getSortedPagedRoomSummariesLive(queryParams, pagedListConfig, sortOrder) } - override fun getFilteredPagedRoomSummariesLive(queryParams: RoomSummaryQueryParams, pagedListConfig: PagedList.Config, sortOrder: RoomSortOrder) - : UpdatableLivePageResult { + override fun getFilteredPagedRoomSummariesLive(queryParams: RoomSummaryQueryParams, + pagedListConfig: PagedList.Config, + sortOrder: RoomSortOrder): UpdatableLivePageResult { return roomSummaryDataSource.getUpdatablePagedRoomSummariesLive(queryParams, pagedListConfig, sortOrder) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt index c7e09e59545..5a1eb190a8d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt @@ -131,8 +131,8 @@ internal class EventRelationsAggregationProcessor @Inject constructor( EventType.ENCRYPTED -> { // Relation type is in clear val encryptedEventContent = event.content.toModel() - if (encryptedEventContent?.relatesTo?.type == RelationType.REPLACE - || encryptedEventContent?.relatesTo?.type == RelationType.RESPONSE + if (encryptedEventContent?.relatesTo?.type == RelationType.REPLACE || + encryptedEventContent?.relatesTo?.type == RelationType.RESPONSE ) { event.getClearContent().toModel()?.let { if (encryptedEventContent.relatesTo.type == RelationType.REPLACE) { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/GetRoomSummaryTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/GetRoomSummaryTask.kt new file mode 100644 index 00000000000..d9547d9e3a7 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/GetRoomSummaryTask.kt @@ -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 { + data class Params( + val roomId: String, + val viaServers: List? + ) +} + +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) + } + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt index 535fa9df716..efc5166a0cf 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomAPI.kt @@ -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 @@ -215,22 +216,6 @@ internal interface RoomAPI { @GET(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/state") suspend fun getRoomState(@Path("roomId") roomId: String): List - /** - * Send a relation event to a room. - * - * @param txId the transaction Id - * @param roomId the room id - * @param eventType the event type - * @param content the event content - */ - @POST(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/send_relation/{parent_id}/{relation_type}/{event_type}") - suspend fun sendRelation(@Path("roomId") roomId: String, - @Path("parent_id") parentId: String, - @Path("relation_type") relationType: String, - @Path("event_type") eventType: String, - @Body content: Content? - ): SendResponse - /** * Paginate relations for event based in normal topological order * @@ -254,7 +239,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, - @Body params: JsonDict): JoinRoomResponse + @Body params: JsonDict): JoinRoomResponse /** * Leave the given room. @@ -381,4 +366,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?): RoomStrippedState } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt index 794970705cd..dbd0ae6f060 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomModule.kt @@ -253,4 +253,7 @@ internal abstract class RoomModule { @Binds abstract fun bindSign3pidInvitationTask(task: DefaultSign3pidInvitationTask): Sign3pidInvitationTask + + @Binds + abstract fun bindGetRoomSummaryTask(task: DefaultGetRoomSummaryTask): GetRoomSummaryTask } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/DefaultAliasService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/DefaultAliasService.kt index 8f58094a2af..73a3b285e94 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/DefaultAliasService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/alias/DefaultAliasService.kt @@ -17,8 +17,8 @@ package org.matrix.android.sdk.internal.session.room.alias import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import org.matrix.android.sdk.api.session.room.alias.AliasService internal class DefaultAliasService @AssistedInject constructor( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/call/DefaultRoomCallService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/call/DefaultRoomCallService.kt index 9bde5054f6c..675034531d2 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/call/DefaultRoomCallService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/call/DefaultRoomCallService.kt @@ -17,8 +17,8 @@ package org.matrix.android.sdk.internal.session.room.call import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.room.call.RoomCallService import org.matrix.android.sdk.internal.session.room.RoomGetter diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomBodyBuilder.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomBodyBuilder.kt index 9bb3899f2fd..84261e6ebff 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomBodyBuilder.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomBodyBuilder.kt @@ -77,9 +77,9 @@ internal class CreateRoomBodyBuilder @Inject constructor( buildHistoryVisibilityEvent(params), buildAvatarEvent(params), buildGuestAccess(params) - ) - + params.featurePreset?.setupInitialStates().orEmpty() - + buildCustomInitialStates(params) + ) + + params.featurePreset?.setupInitialStates().orEmpty() + + buildCustomInitialStates(params) ) .takeIf { it.isNotEmpty() } @@ -154,8 +154,8 @@ internal class CreateRoomBodyBuilder @Inject constructor( * Add the crypto algorithm to the room creation parameters. */ private suspend fun buildEncryptionWithAlgorithmEvent(params: CreateRoomParams): Event? { - if (params.algorithm == null - && canEnableEncryption(params)) { + if (params.algorithm == null && + canEnableEncryption(params)) { // Enable the encryption params.enableEncryption() } @@ -173,13 +173,13 @@ internal class CreateRoomBodyBuilder @Inject constructor( } private suspend fun canEnableEncryption(params: CreateRoomParams): Boolean { - return params.enableEncryptionIfInvitedUsersSupportIt + return params.enableEncryptionIfInvitedUsersSupportIt && // Parity with web, enable if users have encryption ready devices // for now remove checks on cross signing and 3pid invites // && crossSigningService.isCrossSigningVerified() - && params.invite3pids.isEmpty() - && params.invitedUserIds.isNotEmpty() - && params.invitedUserIds.let { userIds -> + params.invite3pids.isEmpty() && + params.invitedUserIds.isNotEmpty() && + params.invitedUserIds.let { userIds -> val keys = deviceListManager.downloadKeys(userIds, forceDownload = false) userIds.all { userId -> diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomTask.kt index 518f0a0a6df..ac6e0562b0a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/CreateRoomTask.kt @@ -81,13 +81,13 @@ internal class DefaultCreateRoomTask @Inject constructor( } } catch (throwable: Throwable) { if (throwable is Failure.ServerError) { - if (throwable.httpCode == 403 - && throwable.error.code == MatrixError.M_FORBIDDEN - && throwable.error.message.startsWith("Federation denied with")) { + if (throwable.httpCode == 403 && + throwable.error.code == MatrixError.M_FORBIDDEN && + throwable.error.message.startsWith("Federation denied with")) { throw CreateRoomFailure.CreatedWithFederationFailure(throwable.error) - } else if (throwable.httpCode == 400 - && throwable.error.code == MatrixError.M_UNKNOWN - && throwable.error.message == "Invalid characters in room alias") { + } else if (throwable.httpCode == 400 && + throwable.error.code == MatrixError.M_UNKNOWN && + throwable.error.message == "Invalid characters in room alias") { throw CreateRoomFailure.AliasError(RoomAliasError.AliasInvalid) } } @@ -138,8 +138,8 @@ internal class DefaultCreateRoomTask @Inject constructor( * @return true if it is a direct chat */ private fun CreateRoomParams.isDirect(): Boolean { - return preset == CreateRoomPreset.PRESET_TRUSTED_PRIVATE_CHAT - && isDirect == true + return preset == CreateRoomPreset.PRESET_TRUSTED_PRIVATE_CHAT && + isDirect == true } /** diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/RoomCreateEventProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/RoomCreateEventProcessor.kt index cc66a0a2d2a..eb966b684c8 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/RoomCreateEventProcessor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/create/RoomCreateEventProcessor.kt @@ -16,6 +16,7 @@ package org.matrix.android.sdk.internal.session.room.create +import io.realm.Realm import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toModel @@ -25,7 +26,6 @@ import org.matrix.android.sdk.internal.database.model.EventInsertType import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.session.EventInsertLiveProcessor -import io.realm.Realm import javax.inject.Inject internal class RoomCreateEventProcessor @Inject constructor() : EventInsertLiveProcessor { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/draft/DefaultDraftService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/draft/DefaultDraftService.kt index 1d4ab6d516c..046f8ba8ba5 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/draft/DefaultDraftService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/draft/DefaultDraftService.kt @@ -18,8 +18,8 @@ package org.matrix.android.sdk.internal.session.room.draft import androidx.lifecycle.LiveData import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import kotlinx.coroutines.withContext import org.matrix.android.sdk.api.session.room.send.DraftService import org.matrix.android.sdk.api.session.room.send.UserDraft diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/DefaultMembershipService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/DefaultMembershipService.kt index 41e891f78e6..204deb72b4b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/DefaultMembershipService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/DefaultMembershipService.kt @@ -17,10 +17,12 @@ package org.matrix.android.sdk.internal.session.room.membership import androidx.lifecycle.LiveData +import com.zhuinden.monarchy.Monarchy import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory -import com.zhuinden.monarchy.Monarchy +import dagger.assisted.AssistedInject +import io.realm.Realm +import io.realm.RealmQuery import org.matrix.android.sdk.api.session.identity.ThreePid import org.matrix.android.sdk.api.session.room.members.MembershipService import org.matrix.android.sdk.api.session.room.members.RoomMemberQueryParams @@ -38,8 +40,6 @@ import org.matrix.android.sdk.internal.session.room.membership.joining.JoinRoomT import org.matrix.android.sdk.internal.session.room.membership.leaving.LeaveRoomTask import org.matrix.android.sdk.internal.session.room.membership.threepid.InviteThreePidTask import org.matrix.android.sdk.internal.util.fetchCopied -import io.realm.Realm -import io.realm.RealmQuery internal class DefaultMembershipService @AssistedInject constructor( @Assisted private val roomId: String, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomDisplayNameResolver.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomDisplayNameResolver.kt index 3aa812d93db..5e77dd157ab 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomDisplayNameResolver.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/RoomDisplayNameResolver.kt @@ -33,6 +33,7 @@ import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity import org.matrix.android.sdk.internal.database.query.getOrNull import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.di.UserId +import org.matrix.android.sdk.internal.session.displayname.DisplayNameResolver import javax.inject.Inject /** @@ -40,6 +41,7 @@ import javax.inject.Inject */ internal class RoomDisplayNameResolver @Inject constructor( matrixConfiguration: MatrixConfiguration, + private val displayNameResolver: DisplayNameResolver, @UserId private val userId: String ) { @@ -83,7 +85,8 @@ internal class RoomDisplayNameResolver @Inject constructor( activeMembers.where() .equalTo(RoomMemberSummaryEntityFields.USER_ID, it) .findFirst() - ?.getBestName() + ?.toMatrixItem() + ?.let { matrixItem -> displayNameResolver.getBestName(matrixItem) } } ?: roomDisplayNameFallbackProvider.getNameForRoomInvite() } else if (roomEntity?.membership == Membership.JOIN) { @@ -109,7 +112,7 @@ internal class RoomDisplayNameResolver @Inject constructor( // Get left members if any val leftMembersNames = roomMembers.queryLeftRoomMembersEvent() .findAll() - .map { it.getBestName() } + .map { displayNameResolver.getBestName(it.toMatrixItem()) } roomDisplayNameFallbackProvider.getNameForEmptyRoom(roomSummary?.isDirect.orFalse(), leftMembersNames) } 1 -> { @@ -157,7 +160,7 @@ internal class RoomDisplayNameResolver @Inject constructor( roomMemberHelper: RoomMemberHelper): String { val isUnique = roomMemberHelper.isUniqueDisplayName(roomMemberSummary.displayName) return if (isUnique) { - roomMemberSummary.getBestName() + displayNameResolver.getBestName(roomMemberSummary.toMatrixItem()) } else { "${roomMemberSummary.displayName} (${roomMemberSummary.userId})" } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/joining/JoinRoomTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/joining/JoinRoomTask.kt index 209a904fad8..82fea237dbd 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/joining/JoinRoomTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/joining/JoinRoomTask.kt @@ -43,7 +43,7 @@ internal interface JoinRoomTask : Task { val roomIdOrAlias: String, val reason: String?, val viaServers: List = emptyList(), - val thirdPartySigned : SignInvitationResult? = null + val thirdPartySigned: SignInvitationResult? = null ) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/notification/DefaultRoomPushRuleService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/notification/DefaultRoomPushRuleService.kt index 5486d96e289..8f1aefb731e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/notification/DefaultRoomPushRuleService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/notification/DefaultRoomPushRuleService.kt @@ -18,10 +18,10 @@ package org.matrix.android.sdk.internal.session.room.notification import androidx.lifecycle.LiveData import androidx.lifecycle.Transformations +import com.zhuinden.monarchy.Monarchy import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory -import com.zhuinden.monarchy.Monarchy +import dagger.assisted.AssistedInject import org.matrix.android.sdk.api.pushrules.RuleScope import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState import org.matrix.android.sdk.api.session.room.notification.RoomPushRuleService @@ -31,8 +31,8 @@ import org.matrix.android.sdk.internal.di.SessionDatabase internal class DefaultRoomPushRuleService @AssistedInject constructor(@Assisted private val roomId: String, private val setRoomNotificationStateTask: SetRoomNotificationStateTask, - @SessionDatabase private val monarchy: Monarchy) - : RoomPushRuleService { + @SessionDatabase private val monarchy: Monarchy) : + RoomPushRuleService { @AssistedFactory interface Factory { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/notification/SetRoomNotificationStateTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/notification/SetRoomNotificationStateTask.kt index 9cea1fe425f..feb8c27b09e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/notification/SetRoomNotificationStateTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/notification/SetRoomNotificationStateTask.kt @@ -17,6 +17,7 @@ package org.matrix.android.sdk.internal.session.room.notification import com.zhuinden.monarchy.Monarchy +import io.realm.Realm import org.matrix.android.sdk.api.pushrules.RuleScope import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState import org.matrix.android.sdk.internal.database.model.PushRuleEntity @@ -25,7 +26,6 @@ import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.session.pushers.AddPushRuleTask import org.matrix.android.sdk.internal.session.pushers.RemovePushRuleTask import org.matrix.android.sdk.internal.task.Task -import io.realm.Realm import javax.inject.Inject internal interface SetRoomNotificationStateTask : Task { @@ -37,8 +37,8 @@ internal interface SetRoomNotificationStateTask : Task { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/SendRelationWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/SendRelationWorker.kt deleted file mode 100644 index 5d0879d706e..00000000000 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/SendRelationWorker.kt +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2020 The Matrix.org Foundation C.I.C. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.matrix.android.sdk.internal.session.room.relation - -import android.content.Context -import androidx.work.WorkerParameters -import com.squareup.moshi.JsonClass -import org.matrix.android.sdk.api.failure.Failure -import org.matrix.android.sdk.api.session.events.model.Event -import org.matrix.android.sdk.api.session.events.model.toModel -import org.matrix.android.sdk.api.session.room.model.relation.ReactionContent -import org.matrix.android.sdk.api.session.room.model.relation.ReactionInfo -import org.matrix.android.sdk.internal.network.GlobalErrorReceiver -import org.matrix.android.sdk.internal.network.executeRequest -import org.matrix.android.sdk.internal.session.SessionComponent -import org.matrix.android.sdk.internal.session.room.RoomAPI -import org.matrix.android.sdk.internal.session.room.send.LocalEchoRepository -import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker -import org.matrix.android.sdk.internal.worker.SessionWorkerParams -import javax.inject.Inject - -// TODO This is not used. Delete? -internal class SendRelationWorker(context: Context, params: WorkerParameters) - : SessionSafeCoroutineWorker(context, params, Params::class.java) { - - @JsonClass(generateAdapter = true) - internal data class Params( - override val sessionId: String, - val roomId: String, - val eventId: String, - val relationType: String? = null, - override val lastFailureMessage: String? = null - ) : SessionWorkerParams - - @Inject lateinit var roomAPI: RoomAPI - @Inject lateinit var globalErrorReceiver: GlobalErrorReceiver - @Inject lateinit var localEchoRepository: LocalEchoRepository - - override fun injectWith(injector: SessionComponent) { - injector.inject(this) - } - - override suspend fun doSafeWork(params: Params): Result { - val localEvent = localEchoRepository.getUpToDateEcho(params.eventId) - if (localEvent?.eventId == null) { - return Result.failure() - } - val relationContent = localEvent.content.toModel() - ?: return Result.failure() - val relatedEventId = relationContent.relatesTo?.eventId ?: return Result.failure() - val relationType = (relationContent.relatesTo as? ReactionInfo)?.type ?: params.relationType - ?: return Result.failure() - return try { - sendRelation(params.roomId, relationType, relatedEventId, localEvent) - Result.success() - } catch (exception: Throwable) { - when (exception) { - is Failure.NetworkConnection -> Result.retry() - else -> { - // TODO mark as failed to send? - // always return success, or the chain will be stuck for ever! - Result.success() - } - } - } - } - - override fun buildErrorParams(params: Params, message: String): Params { - return params.copy(lastFailureMessage = params.lastFailureMessage ?: message) - } - - private suspend fun sendRelation(roomId: String, relationType: String, relatedEventId: String, localEvent: Event) { - executeRequest(globalErrorReceiver) { - roomAPI.sendRelation( - roomId = roomId, - parentId = relatedEventId, - relationType = relationType, - eventType = localEvent.type!!, - content = localEvent.content - ) - } - } -} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/UpdateQuickReactionTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/UpdateQuickReactionTask.kt index 32d6c5aa7ee..d0ab430dad7 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/UpdateQuickReactionTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/UpdateQuickReactionTask.kt @@ -16,6 +16,7 @@ package org.matrix.android.sdk.internal.session.room.relation import com.zhuinden.monarchy.Monarchy +import io.realm.Realm import org.matrix.android.sdk.internal.database.model.EventAnnotationsSummaryEntity import org.matrix.android.sdk.internal.database.model.EventEntity import org.matrix.android.sdk.internal.database.model.ReactionAggregatedSummaryEntityFields @@ -23,7 +24,6 @@ import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.task.Task -import io.realm.Realm import javax.inject.Inject internal interface UpdateQuickReactionTask : Task { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/reporting/DefaultReportingService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/reporting/DefaultReportingService.kt index add17a9fa58..c961f718ef6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/reporting/DefaultReportingService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/reporting/DefaultReportingService.kt @@ -17,8 +17,8 @@ package org.matrix.android.sdk.internal.session.room.reporting import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import org.matrix.android.sdk.api.session.room.reporting.ReportingService internal class DefaultReportingService @AssistedInject constructor(@Assisted private val roomId: String, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt index a64b9039474..177c98541c8 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/DefaultSendService.kt @@ -22,9 +22,10 @@ import androidx.work.ExistingWorkPolicy import androidx.work.OneTimeWorkRequest import androidx.work.Operation import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.MatrixUrls.isMxcUrl import org.matrix.android.sdk.api.session.content.ContentAttachmentData import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.isAttachmentMessage @@ -130,7 +131,7 @@ internal class DefaultSendService @AssistedInject constructor( val messageContent = clearContent?.toModel() as? MessageWithAttachmentContent ?: return NoOpCancellable val url = messageContent.getFileUrl() ?: return NoOpCancellable - if (url.startsWith("mxc://")) { + if (url.isMxcUrl()) { // We need to resend only the message as the attachment is ok localEchoRepository.updateSendState(localEcho.eventId, roomId, SendState.UNSENT) return sendEvent(localEcho.root) @@ -185,7 +186,7 @@ internal class DefaultSendService @AssistedInject constructor( name = messageContent.body, queryUri = Uri.parse(messageContent.url), type = ContentAttachmentData.Type.AUDIO, - waveform = messageContent.audioWaveformInfo?.waveform + waveform = messageContent.audioWaveformInfo?.waveform?.filterNotNull() ) localEchoRepository.updateSendState(localEcho.eventId, roomId, SendState.UNSENT) internalSendMedia(listOf(localEcho.root), attachmentData, true) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt index c610326a940..8dd0c593872 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/LocalEchoEventFactory.kt @@ -165,8 +165,8 @@ internal class LocalEchoEventFactory @Inject constructor( newBodyAutoMarkdown: Boolean, msgType: String, compatibilityText: String): Event { - val permalink = permalinkFactory.createPermalink(roomId, originalEvent.root.eventId ?: "") - val userLink = originalEvent.root.senderId?.let { permalinkFactory.createPermalink(it) } ?: "" + val permalink = permalinkFactory.createPermalink(roomId, originalEvent.root.eventId ?: "", false) + val userLink = originalEvent.root.senderId?.let { permalinkFactory.createPermalink(it, false) } ?: "" val body = bodyForReply(originalEvent.getLastMessageContent(), originalEvent.isReply()) val replyFormatted = REPLY_PATTERN.format( @@ -350,9 +350,9 @@ internal class LocalEchoEventFactory @Inject constructor( autoMarkdown: Boolean): Event? { // Fallbacks and event representation // TODO Add error/warning logs when any of this is null - val permalink = permalinkFactory.createPermalink(eventReplied.root) ?: return null + val permalink = permalinkFactory.createPermalink(eventReplied.root, false) ?: return null val userId = eventReplied.root.senderId ?: return null - val userLink = permalinkFactory.createPermalink(userId) ?: return null + val userLink = permalinkFactory.createPermalink(userId, false) ?: return null val body = bodyForReply(eventReplied.getLastMessageContent(), eventReplied.isReply()) val replyFormatted = REPLY_PATTERN.format( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/MultipleEventSendingDispatcherWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/MultipleEventSendingDispatcherWorker.kt index e889f1a61ba..16a9eba3636 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/MultipleEventSendingDispatcherWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/MultipleEventSendingDispatcherWorker.kt @@ -38,8 +38,8 @@ import javax.inject.Inject * Possible previous worker: Always [UploadContentWorker] * Possible next worker : None, but it will post new work to send events, encrypted or not */ -internal class MultipleEventSendingDispatcherWorker(context: Context, params: WorkerParameters) - : SessionSafeCoroutineWorker(context, params, Params::class.java) { +internal class MultipleEventSendingDispatcherWorker(context: Context, params: WorkerParameters) : + SessionSafeCoroutineWorker(context, params, Params::class.java) { @JsonClass(generateAdapter = true) internal data class Params( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/RedactEventWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/RedactEventWorker.kt index 306f8654083..b4436bfcbf6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/RedactEventWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/RedactEventWorker.kt @@ -32,8 +32,8 @@ import javax.inject.Inject * Possible previous worker: None * Possible next worker : None */ -internal class RedactEventWorker(context: Context, params: WorkerParameters) - : SessionSafeCoroutineWorker(context, params, Params::class.java) { +internal class RedactEventWorker(context: Context, params: WorkerParameters) : + SessionSafeCoroutineWorker(context, params, Params::class.java) { @JsonClass(generateAdapter = true) internal data class Params( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/SendEventWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/SendEventWorker.kt index cd7911910dd..8b7fe4b9072 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/SendEventWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/SendEventWorker.kt @@ -39,8 +39,8 @@ import javax.inject.Inject * Possible next worker : None */ internal class SendEventWorker(context: Context, - params: WorkerParameters) - : SessionSafeCoroutineWorker(context, params, Params::class.java) { + params: WorkerParameters) : + SessionSafeCoroutineWorker(context, params, Params::class.java) { @JsonClass(generateAdapter = true) internal data class Params( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/pills/TextPillsUtils.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/pills/TextPillsUtils.kt index faf966edf4c..33cb0db243e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/pills/TextPillsUtils.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/pills/TextPillsUtils.kt @@ -17,6 +17,7 @@ package org.matrix.android.sdk.internal.session.room.send.pills import android.text.SpannableString import org.matrix.android.sdk.api.session.room.send.MatrixItemSpan +import org.matrix.android.sdk.internal.session.displayname.DisplayNameResolver import java.util.Collections import javax.inject.Inject @@ -25,7 +26,8 @@ import javax.inject.Inject * formatted text to send them as a Matrix messages. */ internal class TextPillsUtils @Inject constructor( - private val mentionLinkSpecComparator: MentionLinkSpecComparator + private val mentionLinkSpecComparator: MentionLinkSpecComparator, + private val displayNameResolver: DisplayNameResolver ) { /** @@ -63,7 +65,7 @@ internal class TextPillsUtils @Inject constructor( // append text before pill append(text, currIndex, start) // append the pill - append(String.format(template, urlSpan.matrixItem.id, urlSpan.matrixItem.getBestName())) + append(String.format(template, urlSpan.matrixItem.id, displayNameResolver.getBestName(urlSpan.matrixItem))) currIndex = end } // append text after the last pill diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessor.kt index cd5bf575dba..050e321b9c7 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/send/queue/EventSenderProcessor.kt @@ -16,11 +16,11 @@ package org.matrix.android.sdk.internal.session.room.send.queue +import org.matrix.android.sdk.api.session.SessionLifecycleObserver import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.util.Cancelable -import org.matrix.android.sdk.api.session.SessionLifecycleObserver -internal interface EventSenderProcessor: SessionLifecycleObserver { +internal interface EventSenderProcessor : SessionLifecycleObserver { fun postEvent(event: Event): Cancelable diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt index 89a3533946d..30014f4539f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/summary/RoomSummaryUpdater.kt @@ -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,7 +32,10 @@ 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.api.session.sync.model.RoomSyncSummary +import org.matrix.android.sdk.api.session.sync.model.RoomSyncUnreadNotifications import org.matrix.android.sdk.internal.crypto.EventDecryptor import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM import org.matrix.android.sdk.internal.crypto.crosssigning.DefaultCrossSigningService @@ -61,8 +65,6 @@ import org.matrix.android.sdk.internal.session.room.accountdata.RoomAccountDataD import org.matrix.android.sdk.internal.session.room.membership.RoomDisplayNameResolver import org.matrix.android.sdk.internal.session.room.membership.RoomMemberHelper import org.matrix.android.sdk.internal.session.room.relationship.RoomChildRelationInfo -import org.matrix.android.sdk.internal.session.sync.model.RoomSyncSummary -import org.matrix.android.sdk.internal.session.sync.model.RoomSyncUnreadNotifications import timber.log.Timber import javax.inject.Inject import kotlin.system.measureTimeMillis @@ -103,8 +105,8 @@ internal class RoomSummaryUpdater @Inject constructor( } // Hard to filter from the app now we use PagedList... - roomSummaryEntity.isHiddenFromUser = roomSummaryEntity.versioningState == VersioningState.UPGRADED_ROOM_JOINED - || roomAccountDataDataSource.getAccountDataEvent(roomId, RoomAccountDataTypes.EVENT_TYPE_VIRTUAL_ROOM) != null + roomSummaryEntity.isHiddenFromUser = roomSummaryEntity.versioningState == VersioningState.UPGRADED_ROOM_JOINED || + roomAccountDataDataSource.getAccountDataEvent(roomId, RoomAccountDataTypes.EVENT_TYPE_VIRTUAL_ROOM) != null val lastNameEvent = CurrentStateEventEntity.getOrNull(realm, roomId, type = EventType.STATE_ROOM_NAME, stateKey = "")?.root val lastTopicEvent = CurrentStateEventEntity.getOrNull(realm, roomId, type = EventType.STATE_ROOM_TOPIC, stateKey = "")?.root @@ -130,9 +132,9 @@ internal class RoomSummaryUpdater @Inject constructor( roomSummaryEntity.lastActivityTime = lastActivityFromEvent } - roomSummaryEntity.hasUnreadMessages = roomSummaryEntity.notificationCount > 0 + roomSummaryEntity.hasUnreadMessages = roomSummaryEntity.notificationCount > 0 || // avoid this call if we are sure there are unread events - || !isEventRead(realm.configuration, userId, roomId, latestPreviewableEvent?.eventId) + !isEventRead(realm.configuration, userId, roomId, latestPreviewableEvent?.eventId) roomSummaryEntity.displayName = roomDisplayNameResolver.resolve(realm, roomId) roomSummaryEntity.avatarUrl = roomAvatarResolver.resolve(realm, roomId) @@ -207,63 +209,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().apply { - this.childRoomId = child.roomId - this.childSummaryEntity = RoomSummaryEntity.where(realm, child.roomId).findFirst() - this.order = child.order + lookedUp.children.add( + realm.createObject().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) - } - ) - - 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) + 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) + } } } + } + } + + // 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() } + ?.let { PowerLevelsHelper(it) } + + isValidRelation = powerLevelsHelper?.isUserAllowedToSend(senderId, true, EventType.STATE_SPACE_CHILD) ?: false + } + + if (isValidRelation) { + lookedUp.parents.add( + realm.createObject().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().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 diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/tags/DefaultTagsService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/tags/DefaultTagsService.kt index 02acaa05704..131bd40f1ec 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/tags/DefaultTagsService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/tags/DefaultTagsService.kt @@ -17,8 +17,8 @@ package org.matrix.android.sdk.internal.session.room.tags import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import org.matrix.android.sdk.api.session.room.tags.TagsService internal class DefaultTagsService @AssistedInject constructor( diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt index 8cc5d943b7e..c0e428ec851 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt @@ -475,8 +475,8 @@ internal class DefaultTimeline( val currentChunk = getLiveChunk() val token = if (direction == Timeline.Direction.BACKWARDS) currentChunk?.prevToken else currentChunk?.nextToken if (token == null) { - if (direction == Timeline.Direction.BACKWARDS - || (direction == Timeline.Direction.FORWARDS && currentChunk?.hasBeenALastForwardChunk().orFalse())) { + if (direction == Timeline.Direction.BACKWARDS || + (direction == Timeline.Direction.FORWARDS && currentChunk?.hasBeenALastForwardChunk().orFalse())) { // We are in the case where event exists, but we do not know the token. // Fetch (again) the last event to get a token val lastKnownEventId = if (direction == Timeline.Direction.FORWARDS) { @@ -583,8 +583,8 @@ internal class DefaultTimeline( val transactionId = timelineEvent.root.unsignedData?.transactionId uiEchoManager.onSyncedEvent(transactionId) - if (timelineEvent.isEncrypted() - && timelineEvent.root.mxDecryptionResult == null) { + if (timelineEvent.isEncrypted() && + timelineEvent.root.mxDecryptionResult == null) { timelineEvent.root.eventId?.also { eventDecryptor.requestDecryption(TimelineEventDecryptor.DecryptionRequest(timelineEvent.root, timelineID)) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/LiveTimelineEvent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/LiveTimelineEvent.kt index eb4900553b8..64b1a4ff1d5 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/LiveTimelineEvent.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/LiveTimelineEvent.kt @@ -40,8 +40,8 @@ internal class LiveTimelineEvent(private val monarchy: Monarchy, private val coroutineScope: CoroutineScope, private val timelineEventMapper: TimelineEventMapper, private val roomId: String, - private val eventId: String) - : MediatorLiveData>() { + private val eventId: String) : + MediatorLiveData>() { init { buildAndObserveQuery() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEventPersistor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEventPersistor.kt index a7cba2fe99c..dbcc37a918a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEventPersistor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEventPersistor.kt @@ -238,8 +238,8 @@ internal class TokenChunkEventPersistor @Inject constructor(@SessionDatabase pri it.deleteOnCascade(deleteStateEvents = false, canDeleteRoot = false) } val roomSummaryEntity = RoomSummaryEntity.getOrCreate(realm, roomId) - val shouldUpdateSummary = roomSummaryEntity.latestPreviewableEvent == null - || (chunksToDelete.isNotEmpty() && currentChunk.isLastForward && direction == PaginationDirection.FORWARDS) + val shouldUpdateSummary = roomSummaryEntity.latestPreviewableEvent == null || + (chunksToDelete.isNotEmpty() && currentChunk.isLastForward && direction == PaginationDirection.FORWARDS) if (shouldUpdateSummary) { roomSummaryEntity.latestPreviewableEvent = RoomSummaryEventsHelper.getLatestPreviewableEvent(realm, roomId) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/tombstone/RoomTombstoneEventProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/tombstone/RoomTombstoneEventProcessor.kt index 8022d989751..2b404775f0b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/tombstone/RoomTombstoneEventProcessor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/tombstone/RoomTombstoneEventProcessor.kt @@ -16,6 +16,7 @@ package org.matrix.android.sdk.internal.session.room.tombstone +import io.realm.Realm import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toModel @@ -25,7 +26,6 @@ import org.matrix.android.sdk.internal.database.model.EventInsertType import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.session.EventInsertLiveProcessor -import io.realm.Realm import javax.inject.Inject internal class RoomTombstoneEventProcessor @Inject constructor() : EventInsertLiveProcessor { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/uploads/DefaultUploadsService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/uploads/DefaultUploadsService.kt index 6d841644dc8..99cf36faec1 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/uploads/DefaultUploadsService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/uploads/DefaultUploadsService.kt @@ -17,8 +17,8 @@ package org.matrix.android.sdk.internal.session.room.uploads import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import org.matrix.android.sdk.api.session.crypto.CryptoService import org.matrix.android.sdk.api.session.room.uploads.GetUploadsResult import org.matrix.android.sdk.api.session.room.uploads.UploadsService diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/signout/SignOutTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/signout/SignOutTask.kt index 9c25eccb3a5..19f34746ab2 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/signout/SignOutTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/signout/SignOutTask.kt @@ -50,9 +50,9 @@ internal class DefaultSignOutTask @Inject constructor( } } catch (throwable: Throwable) { // Maybe due to https://github.com/matrix-org/synapse/issues/5756 - if (throwable is Failure.ServerError - && throwable.httpCode == HttpURLConnection.HTTP_UNAUTHORIZED /* 401 */ - && throwable.error.code == MatrixError.M_UNKNOWN_TOKEN) { + if (throwable is Failure.ServerError && + throwable.httpCode == HttpURLConnection.HTTP_UNAUTHORIZED && /* 401 */ + throwable.error.code == MatrixError.M_UNKNOWN_TOKEN) { // Also throwable.error.isSoftLogout should be true // Ignore Timber.w("Ignore error due to https://github.com/matrix-org/synapse/issues/5755") diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpace.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpace.kt index 8a6bbc18fd1..8589db27b15 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpace.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpace.kt @@ -89,7 +89,6 @@ internal class DefaultSpace( body = SpaceChildContent( order = null, via = null, -// autoJoin = null, suggested = null ).toContent() ) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpaceService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpaceService.kt index 7be4cdcda95..ac20c790587 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpaceService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/DefaultSpaceService.kt @@ -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() + ) + } + } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/peeking/PeekSpaceTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/peeking/PeekSpaceTask.kt index 5cbaaa45c40..c45d4420ae4 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/peeking/PeekSpaceTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/peeking/PeekSpaceTask.kt @@ -76,9 +76,9 @@ internal class DefaultPeekSpaceTask @Inject constructor( if (depth >= maxDepth) return emptyList() val childRoomsIds = stateEvents .filter { - it.type == EventType.STATE_SPACE_CHILD && !it.stateKey.isNullOrEmpty() + it.type == EventType.STATE_SPACE_CHILD && !it.stateKey.isNullOrEmpty() && // Children where via is not present are ignored. - && it.content?.toModel()?.via != null + it.content?.toModel()?.via != null } .map { it.stateKey to it.content?.toModel() } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/peeking/SpacePeekResult.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/peeking/SpacePeekResult.kt index 44d879f05d4..a2ffd8221ac 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/peeking/SpacePeekResult.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/space/peeking/SpacePeekResult.kt @@ -28,6 +28,7 @@ data class SpacePeekSummary( interface ISpaceChild { val id: String val roomPeekResult: PeekResult + // val default: Boolean? val order: String? } @@ -52,5 +53,5 @@ sealed class SpacePeekResult { data class FailedToResolve(val spaceId: String, val roomPeekResult: PeekResult) : SpacePeekError() data class NotSpaceType(val spaceId: String) : SpacePeekError() - data class Success(val summary: SpacePeekSummary): SpacePeekResult() + data class Success(val summary: SpacePeekSummary) : SpacePeekResult() } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/CryptoSyncHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/CryptoSyncHandler.kt index 411a9c5c065..cec5689a828 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/CryptoSyncHandler.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/CryptoSyncHandler.kt @@ -21,14 +21,14 @@ import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.model.message.MessageContent +import org.matrix.android.sdk.api.session.sync.model.SyncResponse +import org.matrix.android.sdk.api.session.sync.model.ToDeviceSyncResponse import org.matrix.android.sdk.internal.crypto.DefaultCryptoService import org.matrix.android.sdk.internal.crypto.MXEventDecryptionResult import org.matrix.android.sdk.internal.crypto.algorithms.olm.OlmDecryptionResult import org.matrix.android.sdk.internal.crypto.model.event.OlmEventContent import org.matrix.android.sdk.internal.crypto.verification.DefaultVerificationService import org.matrix.android.sdk.internal.session.initsync.ProgressReporter -import org.matrix.android.sdk.internal.session.sync.model.SyncResponse -import org.matrix.android.sdk.internal.session.sync.model.ToDeviceSyncResponse import timber.log.Timber import javax.inject.Inject @@ -42,8 +42,8 @@ internal class CryptoSyncHandler @Inject constructor(private val cryptoService: // Decrypt event if necessary Timber.i("## CRYPTO | To device event from ${event.senderId} of type:${event.type}") decryptToDeviceEvent(event, null) - if (event.getClearType() == EventType.MESSAGE - && event.getClearContent()?.toModel()?.msgType == "m.bad.encrypted") { + if (event.getClearType() == EventType.MESSAGE && + event.getClearContent()?.toModel()?.msgType == "m.bad.encrypted") { Timber.e("## CRYPTO | handleToDeviceEvent() : Warning: Unable to decrypt to-device event : ${event.content}") } else { verificationService.onToDeviceEvent(event) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/GroupSyncHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/GroupSyncHandler.kt index 02362bf050e..2b054e578f0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/GroupSyncHandler.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/GroupSyncHandler.kt @@ -19,14 +19,14 @@ package org.matrix.android.sdk.internal.session.sync import io.realm.Realm import org.matrix.android.sdk.api.session.initsync.InitSyncStep import org.matrix.android.sdk.api.session.room.model.Membership +import org.matrix.android.sdk.api.session.sync.model.GroupsSyncResponse +import org.matrix.android.sdk.api.session.sync.model.InvitedGroupSync import org.matrix.android.sdk.internal.database.model.GroupEntity import org.matrix.android.sdk.internal.database.model.GroupSummaryEntity import org.matrix.android.sdk.internal.database.query.getOrCreate import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.session.initsync.ProgressReporter import org.matrix.android.sdk.internal.session.initsync.mapWithProgress -import org.matrix.android.sdk.internal.session.sync.model.GroupsSyncResponse -import org.matrix.android.sdk.internal.session.sync.model.InvitedGroupSync import javax.inject.Inject internal class GroupSyncHandler @Inject constructor() { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/InitialSyncStatusRepository.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/InitialSyncStatusRepository.kt index cf67bbd805d..bd20ada28b0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/InitialSyncStatusRepository.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/InitialSyncStatusRepository.kt @@ -63,8 +63,8 @@ internal class FileInitialSyncStatusRepository(directory: File) : InitialSyncSta override fun getStep(): Int { ensureCache() val state = cache?.step ?: InitialSyncStatus.STEP_INIT - return if (state >= InitialSyncStatus.STEP_DOWNLOADED - && System.currentTimeMillis() > (cache?.downloadedDate ?: 0) + INIT_SYNC_FILE_LIFETIME) { + return if (state >= InitialSyncStatus.STEP_DOWNLOADED && + System.currentTimeMillis() > (cache?.downloadedDate ?: 0) + INIT_SYNC_FILE_LIFETIME) { Timber.d("INIT_SYNC downloaded file is outdated, download it again") // The downloaded file is outdated setStep(InitialSyncStatus.STEP_INIT) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/InitialSyncStrategy.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/InitialSyncStrategy.kt index 7d93e30191c..4bc866b36d4 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/InitialSyncStrategy.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/InitialSyncStrategy.kt @@ -39,7 +39,7 @@ sealed class InitialSyncStrategy { * Limit to reach to decide to split the init sync response into smaller files * Empiric value: 1 megabytes */ - val minSizeToSplit: Long = 1024 * 1024, + val minSizeToSplit: Long = 1_048_576, // 1024 * 1024 /** * Limit per room to reach to decide to store a join room ephemeral Events into a file * Empiric value: 1 kilobytes diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomFullyReadHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomFullyReadHandler.kt index e8934fdf215..3d0db212c25 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomFullyReadHandler.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomFullyReadHandler.kt @@ -16,11 +16,11 @@ package org.matrix.android.sdk.internal.session.sync +import io.realm.Realm import org.matrix.android.sdk.internal.database.model.ReadMarkerEntity import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity import org.matrix.android.sdk.internal.database.query.getOrCreate import org.matrix.android.sdk.internal.session.room.read.FullyReadContent -import io.realm.Realm import timber.log.Timber import javax.inject.Inject diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomSyncEphemeralTemporaryStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomSyncEphemeralTemporaryStore.kt index c6ff71cfcfd..e8f74bbd487 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomSyncEphemeralTemporaryStore.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomSyncEphemeralTemporaryStore.kt @@ -20,8 +20,8 @@ import com.squareup.moshi.JsonReader import com.squareup.moshi.Moshi import okio.buffer import okio.source +import org.matrix.android.sdk.api.session.sync.model.RoomSyncEphemeral import org.matrix.android.sdk.internal.di.SessionFilesDirectory -import org.matrix.android.sdk.internal.session.sync.model.RoomSyncEphemeral import org.matrix.android.sdk.internal.util.md5 import timber.log.Timber import java.io.File diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomSyncHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomSyncHandler.kt index 830e666c950..52e5b6b58d8 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomSyncHandler.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomSyncHandler.kt @@ -26,6 +26,10 @@ import org.matrix.android.sdk.api.session.initsync.InitSyncStep import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.RoomMemberContent import org.matrix.android.sdk.api.session.room.send.SendState +import org.matrix.android.sdk.api.session.sync.model.InvitedRoomSync +import org.matrix.android.sdk.api.session.sync.model.LazyRoomSyncEphemeral +import org.matrix.android.sdk.api.session.sync.model.RoomSync +import org.matrix.android.sdk.api.session.sync.model.RoomsSyncResponse import org.matrix.android.sdk.internal.crypto.DefaultCryptoService import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM import org.matrix.android.sdk.internal.crypto.algorithms.olm.OlmDecryptionResult @@ -58,10 +62,6 @@ import org.matrix.android.sdk.internal.session.room.summary.RoomSummaryUpdater import org.matrix.android.sdk.internal.session.room.timeline.PaginationDirection import org.matrix.android.sdk.internal.session.room.timeline.TimelineInput import org.matrix.android.sdk.internal.session.room.typing.TypingEventContent -import org.matrix.android.sdk.internal.session.sync.model.InvitedRoomSync -import org.matrix.android.sdk.internal.session.sync.model.LazyRoomSyncEphemeral -import org.matrix.android.sdk.internal.session.sync.model.RoomSync -import org.matrix.android.sdk.internal.session.sync.model.RoomsSyncResponse import org.matrix.android.sdk.internal.session.sync.parsing.RoomSyncAccountDataHandler import org.matrix.android.sdk.internal.util.computeBestChunkSize import timber.log.Timber diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomTagHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomTagHandler.kt index add5d841d1b..8997435be4b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomTagHandler.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomTagHandler.kt @@ -16,10 +16,10 @@ package org.matrix.android.sdk.internal.session.sync +import io.realm.Realm import org.matrix.android.sdk.api.session.room.model.tag.RoomTagContent import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity import org.matrix.android.sdk.internal.database.model.RoomTagEntity -import io.realm.Realm import org.matrix.android.sdk.internal.database.query.getOrCreate import javax.inject.Inject diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomTypingUsersHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomTypingUsersHandler.kt index b7851031adc..1433d891434 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomTypingUsersHandler.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomTypingUsersHandler.kt @@ -16,11 +16,11 @@ package org.matrix.android.sdk.internal.session.sync +import io.realm.Realm import org.matrix.android.sdk.api.session.room.sender.SenderInfo import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.session.room.membership.RoomMemberHelper import org.matrix.android.sdk.internal.session.typing.DefaultTypingUsersTracker -import io.realm.Realm import javax.inject.Inject internal class RoomTypingUsersHandler @Inject constructor(@UserId private val userId: String, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncAPI.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncAPI.kt index 2616803463a..73ec0aa7ef1 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncAPI.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncAPI.kt @@ -17,9 +17,9 @@ package org.matrix.android.sdk.internal.session.sync import okhttp3.ResponseBody +import org.matrix.android.sdk.api.session.sync.model.SyncResponse import org.matrix.android.sdk.internal.network.NetworkConstants import org.matrix.android.sdk.internal.network.TimeOutInterceptor -import org.matrix.android.sdk.internal.session.sync.model.SyncResponse import retrofit2.Call import retrofit2.http.GET import retrofit2.http.Header diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncResponseHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncResponseHandler.kt index a4468a96c97..8c7401ab475 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncResponseHandler.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncResponseHandler.kt @@ -21,6 +21,9 @@ import com.zhuinden.monarchy.Monarchy import org.matrix.android.sdk.api.pushrules.PushRuleService import org.matrix.android.sdk.api.pushrules.RuleScope import org.matrix.android.sdk.api.session.initsync.InitSyncStep +import org.matrix.android.sdk.api.session.sync.model.GroupsSyncResponse +import org.matrix.android.sdk.api.session.sync.model.RoomsSyncResponse +import org.matrix.android.sdk.api.session.sync.model.SyncResponse import org.matrix.android.sdk.internal.crypto.DefaultCryptoService import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.di.SessionId @@ -30,9 +33,6 @@ import org.matrix.android.sdk.internal.session.group.GetGroupDataWorker import org.matrix.android.sdk.internal.session.initsync.ProgressReporter import org.matrix.android.sdk.internal.session.initsync.reportSubtask import org.matrix.android.sdk.internal.session.notification.ProcessEventForPushTask -import org.matrix.android.sdk.internal.session.sync.model.GroupsSyncResponse -import org.matrix.android.sdk.internal.session.sync.model.RoomsSyncResponse -import org.matrix.android.sdk.internal.session.sync.model.SyncResponse import org.matrix.android.sdk.internal.util.awaitTransaction import org.matrix.android.sdk.internal.worker.WorkerParamsFactory import timber.log.Timber diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncResponsePostTreatmentAggregator.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncResponsePostTreatmentAggregator.kt index 9bb2bfc9b17..cc4ccc2e464 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncResponsePostTreatmentAggregator.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncResponsePostTreatmentAggregator.kt @@ -19,6 +19,7 @@ package org.matrix.android.sdk.internal.session.sync internal class SyncResponsePostTreatmentAggregator { // List of RoomId val ephemeralFilesToDelete = mutableListOf() + // Map of roomId to directUserId val directChatsToCheck = mutableMapOf() } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTask.kt index c80fbe60c1b..621a08a414e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTask.kt @@ -17,7 +17,11 @@ 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.api.session.sync.model.LazyRoomSyncEphemeral +import org.matrix.android.sdk.api.session.sync.model.SyncResponse 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,9 +30,8 @@ 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 import org.matrix.android.sdk.internal.session.user.UserStore import org.matrix.android.sdk.internal.task.Task @@ -40,7 +43,9 @@ import java.io.File import java.net.SocketTimeoutException import javax.inject.Inject -internal interface SyncTask : Task { +private val loggerTag = LoggerTag("SyncTask", LoggerTag.SYNC) + +internal interface SyncTask : Task { data class Params( val timeout: Long, @@ -53,7 +58,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, @@ -68,14 +73,14 @@ internal class DefaultSyncTask @Inject constructor( private val workingDir = File(fileDirectory, "is") private val initialSyncStatusRepository: InitialSyncStatusRepository = FileInitialSyncStatusRepository(workingDir) - override suspend fun execute(params: SyncTask.Params) { - syncTaskSequencer.post { + override suspend fun execute(params: SyncTask.Params): SyncResponse { + return syncTaskSequencer.post { doSync(params) } } - private suspend fun doSync(params: SyncTask.Params) { - Timber.v("Sync task started on Thread: ${Thread.currentThread().name}") + private suspend fun doSync(params: SyncTask.Params): SyncResponse { + Timber.tag(loggerTag.value).d("Sync task started on Thread: ${Thread.currentThread().name}") val requestParams = HashMap() var timeout = 0L @@ -92,28 +97,29 @@ 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)) val readTimeOut = (params.timeout + TIMEOUT_MARGIN).coerceAtLeast(TimeOutInterceptor.DEFAULT_LONG_TIMEOUT) + var syncResponseToReturn: SyncResponse? = null 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) { + syncResponseToReturn = 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, @@ -121,44 +127,64 @@ 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) } + syncResponseToReturn = syncResponse } } - 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) + syncResponseToReturn = syncResponse + 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}") + // Should throw if null as it's a mandatory value. + return syncResponseToReturn!! } private suspend fun downloadInitSyncResponse(requestParams: Map): 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 +194,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,30 +211,31 @@ 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 } } } } - private suspend fun handleSyncFile(workingFile: File, initSyncStrategy: InitialSyncStrategy.Optimized) { - logDuration("INIT_SYNC handleSyncFile()") { - val syncResponse = logDuration("INIT_SYNC Read file and parse") { + private suspend fun handleSyncFile(workingFile: File, initSyncStrategy: InitialSyncStrategy.Optimized): SyncResponse { + return 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) + syncResponse } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTokenStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTokenStore.kt index cf061586b7a..35e561a1060 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTokenStore.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/SyncTokenStore.kt @@ -17,9 +17,9 @@ package org.matrix.android.sdk.internal.session.sync import com.zhuinden.monarchy.Monarchy +import io.realm.Realm import org.matrix.android.sdk.internal.database.model.SyncEntity import org.matrix.android.sdk.internal.di.SessionDatabase -import io.realm.Realm import javax.inject.Inject internal class SyncTokenStore @Inject constructor(@SessionDatabase private val monarchy: Monarchy) { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/UserAccountDataSyncHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/UserAccountDataSyncHandler.kt index 110e77813d1..a9926c70aa9 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/UserAccountDataSyncHandler.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/UserAccountDataSyncHandler.kt @@ -29,6 +29,8 @@ import org.matrix.android.sdk.api.session.events.model.Content import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.model.RoomMemberContent import org.matrix.android.sdk.api.session.room.model.RoomSummary +import org.matrix.android.sdk.api.session.sync.model.InvitedRoomSync +import org.matrix.android.sdk.api.session.sync.model.UserAccountDataSync import org.matrix.android.sdk.internal.database.mapper.ContentMapper import org.matrix.android.sdk.internal.database.mapper.PushRulesMapper import org.matrix.android.sdk.internal.database.mapper.asDomain @@ -48,11 +50,9 @@ import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.session.room.RoomAvatarResolver import org.matrix.android.sdk.internal.session.room.membership.RoomDisplayNameResolver import org.matrix.android.sdk.internal.session.room.membership.RoomMemberHelper -import org.matrix.android.sdk.internal.session.sync.model.InvitedRoomSync import org.matrix.android.sdk.internal.session.sync.model.accountdata.BreadcrumbsContent import org.matrix.android.sdk.internal.session.sync.model.accountdata.DirectMessagesContent import org.matrix.android.sdk.internal.session.sync.model.accountdata.IgnoredUsersContent -import org.matrix.android.sdk.internal.session.sync.model.accountdata.UserAccountDataSync import org.matrix.android.sdk.internal.session.sync.model.accountdata.toMutable import org.matrix.android.sdk.internal.session.user.accountdata.DirectChatsHelper import org.matrix.android.sdk.internal.session.user.accountdata.UpdateUserAccountDataTask diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncThread.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncThread.kt index de8d009892f..e1150f2c474 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncThread.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/job/SyncThread.kt @@ -21,24 +21,28 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.Observer import com.squareup.moshi.JsonEncodingException import kotlinx.coroutines.CancellationException -import org.matrix.android.sdk.api.failure.Failure -import org.matrix.android.sdk.api.failure.isTokenError -import org.matrix.android.sdk.api.session.sync.SyncState -import org.matrix.android.sdk.internal.network.NetworkConnectivityChecker -import org.matrix.android.sdk.internal.session.sync.SyncTask -import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver -import org.matrix.android.sdk.internal.util.Debouncer -import org.matrix.android.sdk.internal.util.createUIHandler import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancelChildren import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking +import org.matrix.android.sdk.api.failure.Failure +import org.matrix.android.sdk.api.failure.isTokenError +import org.matrix.android.sdk.api.logger.LoggerTag import org.matrix.android.sdk.api.session.call.MxCall +import org.matrix.android.sdk.api.session.sync.SyncState +import org.matrix.android.sdk.api.session.sync.model.SyncResponse +import org.matrix.android.sdk.internal.network.NetworkConnectivityChecker import org.matrix.android.sdk.internal.session.call.ActiveCallHandler import org.matrix.android.sdk.internal.session.sync.SyncPresence +import org.matrix.android.sdk.internal.session.sync.SyncTask +import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver +import org.matrix.android.sdk.internal.util.Debouncer +import org.matrix.android.sdk.internal.util.createUIHandler import timber.log.Timber import java.net.SocketTimeoutException import java.util.Timer @@ -49,6 +53,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, @@ -72,6 +78,8 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask, } } + private val _syncFlow = MutableSharedFlow() + init { updateStateTo(SyncState.Idle) } @@ -83,7 +91,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 +102,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 +110,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() @@ -115,6 +123,8 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask, return liveState } + fun syncFlow(): SharedFlow = _syncFlow + override fun onConnectivityChanged() { retryNoNetworkTask?.cancel() synchronized(lock) { @@ -124,21 +134,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 +158,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 +178,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) @@ -192,26 +202,27 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask, private suspend fun doSync(params: SyncTask.Params) { try { - syncTask.execute(params) + val syncResponse = syncTask.execute(params) + _syncFlow.emit(syncResponse) } catch (failure: Throwable) { if (failure is Failure.NetworkConnection) { canReachServer = false } 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 +236,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 } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/parsing/DefaultLazyRoomSyncEphemeralJsonAdapter.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/parsing/DefaultLazyRoomSyncEphemeralJsonAdapter.kt index 940ea219fb6..012470a0768 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/parsing/DefaultLazyRoomSyncEphemeralJsonAdapter.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/parsing/DefaultLazyRoomSyncEphemeralJsonAdapter.kt @@ -21,10 +21,10 @@ import com.squareup.moshi.JsonAdapter import com.squareup.moshi.JsonReader import com.squareup.moshi.JsonWriter import com.squareup.moshi.ToJson +import org.matrix.android.sdk.api.session.sync.model.LazyRoomSyncEphemeral +import org.matrix.android.sdk.api.session.sync.model.RoomSyncEphemeral import org.matrix.android.sdk.internal.session.sync.InitialSyncStrategy import org.matrix.android.sdk.internal.session.sync.RoomSyncEphemeralTemporaryStore -import org.matrix.android.sdk.internal.session.sync.model.LazyRoomSyncEphemeral -import org.matrix.android.sdk.internal.session.sync.model.RoomSyncEphemeral import timber.log.Timber internal class DefaultLazyRoomSyncEphemeralJsonAdapter { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/parsing/InitialSyncResponseParser.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/parsing/InitialSyncResponseParser.kt index 0b44887aedc..f00cce2d5e8 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/parsing/InitialSyncResponseParser.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/parsing/InitialSyncResponseParser.kt @@ -19,9 +19,9 @@ package org.matrix.android.sdk.internal.session.sync.parsing import com.squareup.moshi.Moshi import okio.buffer import okio.source +import org.matrix.android.sdk.api.session.sync.model.SyncResponse import org.matrix.android.sdk.internal.session.sync.InitialSyncStrategy import org.matrix.android.sdk.internal.session.sync.RoomSyncEphemeralTemporaryStore -import org.matrix.android.sdk.internal.session.sync.model.SyncResponse import timber.log.Timber import java.io.File import javax.inject.Inject diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/parsing/RoomSyncAccountDataHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/parsing/RoomSyncAccountDataHandler.kt index 8bf9ad5b908..6ca008c5b18 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/parsing/RoomSyncAccountDataHandler.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/parsing/RoomSyncAccountDataHandler.kt @@ -20,6 +20,7 @@ import io.realm.Realm 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.tag.RoomTagContent +import org.matrix.android.sdk.api.session.sync.model.RoomSyncAccountData import org.matrix.android.sdk.api.util.JsonDict import org.matrix.android.sdk.internal.database.mapper.ContentMapper import org.matrix.android.sdk.internal.database.model.RoomAccountDataEntity @@ -29,7 +30,6 @@ import org.matrix.android.sdk.internal.database.query.getOrCreate import org.matrix.android.sdk.internal.session.room.read.FullyReadContent import org.matrix.android.sdk.internal.session.sync.RoomFullyReadHandler import org.matrix.android.sdk.internal.session.sync.RoomTagHandler -import org.matrix.android.sdk.internal.session.sync.model.RoomSyncAccountData import javax.inject.Inject internal class RoomSyncAccountDataHandler @Inject constructor(private val roomTagHandler: RoomTagHandler, diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/terms/DefaultTermsService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/terms/DefaultTermsService.kt index 2c7dc92dddc..d40fd8d076c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/terms/DefaultTermsService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/terms/DefaultTermsService.kt @@ -30,8 +30,8 @@ import org.matrix.android.sdk.internal.session.identity.IdentityAuthAPI import org.matrix.android.sdk.internal.session.identity.IdentityRegisterTask import org.matrix.android.sdk.internal.session.openid.GetOpenIdTokenTask import org.matrix.android.sdk.internal.session.sync.model.accountdata.AcceptedTermsContent -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.ensureTrailingSlash import javax.inject.Inject diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/terms/TermsModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/terms/TermsModule.kt index b7cd7a4a97a..d7b6f68add0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/terms/TermsModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/terms/TermsModule.kt @@ -20,11 +20,11 @@ import dagger.Binds import dagger.Lazy import dagger.Module import dagger.Provides +import okhttp3.OkHttpClient import org.matrix.android.sdk.api.session.terms.TermsService import org.matrix.android.sdk.internal.di.UnauthenticatedWithCertificate import org.matrix.android.sdk.internal.network.RetrofitFactory import org.matrix.android.sdk.internal.session.SessionScope -import okhttp3.OkHttpClient @Module internal abstract class TermsModule { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/thirdparty/DefaultThirdPartyService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/thirdparty/DefaultThirdPartyService.kt index 13829c400a1..fdd5524fc22 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/thirdparty/DefaultThirdPartyService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/thirdparty/DefaultThirdPartyService.kt @@ -22,8 +22,8 @@ import org.matrix.android.sdk.api.session.thirdparty.model.ThirdPartyUser import javax.inject.Inject internal class DefaultThirdPartyService @Inject constructor(private val getThirdPartyProtocolTask: GetThirdPartyProtocolsTask, - private val getThirdPartyUserTask: GetThirdPartyUserTask) - : ThirdPartyService { + private val getThirdPartyUserTask: GetThirdPartyUserTask) : + ThirdPartyService { override suspend fun getThirdPartyProtocols(): Map { return getThirdPartyProtocolTask.execute(Unit) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/accountdata/DefaultSessionAccountDataService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/accountdata/DefaultSessionAccountDataService.kt index ff1750ce8e4..e5c338d5110 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/accountdata/DefaultSessionAccountDataService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/accountdata/DefaultSessionAccountDataService.kt @@ -19,13 +19,13 @@ package org.matrix.android.sdk.internal.session.user.accountdata import androidx.lifecycle.LiveData import com.zhuinden.monarchy.Monarchy import org.matrix.android.sdk.api.session.accountdata.SessionAccountDataService +import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent import org.matrix.android.sdk.api.session.events.model.Content +import org.matrix.android.sdk.api.session.room.accountdata.RoomAccountDataEvent import org.matrix.android.sdk.api.util.Optional import org.matrix.android.sdk.internal.di.SessionDatabase -import org.matrix.android.sdk.internal.session.sync.UserAccountDataSyncHandler -import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent -import org.matrix.android.sdk.api.session.room.accountdata.RoomAccountDataEvent import org.matrix.android.sdk.internal.session.room.accountdata.RoomAccountDataDataSource +import org.matrix.android.sdk.internal.session.sync.UserAccountDataSyncHandler import org.matrix.android.sdk.internal.task.TaskExecutor import org.matrix.android.sdk.internal.task.configureWith import org.matrix.android.sdk.internal.util.awaitCallback diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/accountdata/DirectChatsHelper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/accountdata/DirectChatsHelper.kt index 37030576439..c7b125b5d6d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/accountdata/DirectChatsHelper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/accountdata/DirectChatsHelper.kt @@ -16,11 +16,11 @@ package org.matrix.android.sdk.internal.session.user.accountdata +import io.realm.Realm +import io.realm.RealmConfiguration import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity import org.matrix.android.sdk.internal.database.query.getDirectRooms import org.matrix.android.sdk.internal.di.SessionDatabase -import io.realm.Realm -import io.realm.RealmConfiguration import org.matrix.android.sdk.internal.session.sync.model.accountdata.DirectMessagesContent import javax.inject.Inject diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/accountdata/SaveBreadcrumbsTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/accountdata/SaveBreadcrumbsTask.kt index d6c95d6b521..22af56a1696 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/accountdata/SaveBreadcrumbsTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/accountdata/SaveBreadcrumbsTask.kt @@ -16,6 +16,7 @@ package org.matrix.android.sdk.internal.session.user.accountdata import com.zhuinden.monarchy.Monarchy +import io.realm.RealmList import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.internal.database.model.BreadcrumbsEntity import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity @@ -25,7 +26,6 @@ import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.task.Task import org.matrix.android.sdk.internal.util.awaitTransaction -import io.realm.RealmList import javax.inject.Inject /** diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/accountdata/UpdateUserAccountDataTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/accountdata/UpdateUserAccountDataTask.kt index 1a588d2245b..88db381852f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/accountdata/UpdateUserAccountDataTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/user/accountdata/UpdateUserAccountDataTask.kt @@ -16,15 +16,15 @@ package org.matrix.android.sdk.internal.session.user.accountdata +import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes import org.matrix.android.sdk.internal.di.UserId +import org.matrix.android.sdk.internal.network.GlobalErrorReceiver import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.session.integrationmanager.AllowedWidgetsContent import org.matrix.android.sdk.internal.session.integrationmanager.IntegrationProvisioningContent import org.matrix.android.sdk.internal.session.sync.model.accountdata.AcceptedTermsContent import org.matrix.android.sdk.internal.session.sync.model.accountdata.BreadcrumbsContent 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.network.GlobalErrorReceiver import org.matrix.android.sdk.internal.task.Task import javax.inject.Inject diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/DefaultWidgetPostAPIMediator.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/DefaultWidgetPostAPIMediator.kt index ba7a2be2a50..07f7c7cb868 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/DefaultWidgetPostAPIMediator.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/DefaultWidgetPostAPIMediator.kt @@ -29,8 +29,8 @@ import java.util.HashMap import javax.inject.Inject internal class DefaultWidgetPostAPIMediator @Inject constructor(private val moshi: Moshi, - private val widgetPostMessageAPIProvider: WidgetPostMessageAPIProvider) - : WidgetPostAPIMediator { + private val widgetPostMessageAPIProvider: WidgetPostMessageAPIProvider) : + WidgetPostAPIMediator { private val jsonAdapter = moshi.adapter(JSON_DICT_PARAMETERIZED_TYPE) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/DefaultWidgetService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/DefaultWidgetService.kt index dfe4b6b8107..89e827aea00 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/DefaultWidgetService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/DefaultWidgetService.kt @@ -28,8 +28,8 @@ import javax.inject.Provider internal class DefaultWidgetService @Inject constructor(private val widgetManager: WidgetManager, private val widgetURLFormatter: WidgetURLFormatter, - private val widgetPostAPIMediator: Provider) - : WidgetService { + private val widgetPostAPIMediator: Provider) : + WidgetService { override fun getWidgetURLFormatter(): WidgetURLFormatter { return widgetURLFormatter diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/DefaultWidgetURLFormatter.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/DefaultWidgetURLFormatter.kt index f7664bf3c2c..5879b624467 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/DefaultWidgetURLFormatter.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/DefaultWidgetURLFormatter.kt @@ -18,12 +18,12 @@ package org.matrix.android.sdk.internal.session.widgets import org.matrix.android.sdk.api.MatrixConfiguration import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.SessionLifecycleObserver import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerConfig import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerService import org.matrix.android.sdk.api.session.widgets.WidgetURLFormatter import org.matrix.android.sdk.api.util.appendParamToUrl import org.matrix.android.sdk.api.util.appendParamsToUrl -import org.matrix.android.sdk.api.session.SessionLifecycleObserver import org.matrix.android.sdk.internal.session.SessionScope import org.matrix.android.sdk.internal.session.integrationmanager.IntegrationManager import org.matrix.android.sdk.internal.session.widgets.token.GetScalarTokenTask diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/WidgetManager.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/WidgetManager.kt index e0f43a11c57..9f5f91d917a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/WidgetManager.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/WidgetManager.kt @@ -23,6 +23,7 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.Transformations import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.SessionLifecycleObserver import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes import org.matrix.android.sdk.api.session.events.model.Content @@ -35,7 +36,6 @@ import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper import org.matrix.android.sdk.api.session.widgets.WidgetManagementFailure import org.matrix.android.sdk.api.session.widgets.model.Widget import org.matrix.android.sdk.internal.di.UserId -import org.matrix.android.sdk.api.session.SessionLifecycleObserver import org.matrix.android.sdk.internal.session.SessionScope import org.matrix.android.sdk.internal.session.integrationmanager.IntegrationManager import org.matrix.android.sdk.internal.session.room.state.StateEventDataSource @@ -51,9 +51,9 @@ internal class WidgetManager @Inject constructor(private val integrationManager: private val stateEventDataSource: StateEventDataSource, private val createWidgetTask: CreateWidgetTask, private val widgetFactory: WidgetFactory, - @UserId private val userId: String) + @UserId private val userId: String) : - : IntegrationManagerService.Listener, SessionLifecycleObserver { + IntegrationManagerService.Listener, SessionLifecycleObserver { private val lifecycleOwner: LifecycleOwner = LifecycleOwner { lifecycleRegistry } private val lifecycleRegistry: LifecycleRegistry = LifecycleRegistry(lifecycleOwner) @@ -155,8 +155,8 @@ internal class WidgetManager @Inject constructor(private val integrationManager: return extractWidgetSequence(widgetFactory) .filter { val widgetType = it.widgetContent.type ?: return@filter false - (widgetTypes == null || widgetTypes.contains(widgetType)) - && (excludedTypes == null || !excludedTypes.contains(widgetType)) + (widgetTypes == null || widgetTypes.contains(widgetType)) && + (excludedTypes == null || !excludedTypes.contains(widgetType)) } .toList() } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/WidgetsAPIProvider.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/WidgetsAPIProvider.kt index 7f79f44767e..48c8fcdb033 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/WidgetsAPIProvider.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/WidgetsAPIProvider.kt @@ -17,10 +17,10 @@ package org.matrix.android.sdk.internal.session.widgets import dagger.Lazy +import okhttp3.OkHttpClient import org.matrix.android.sdk.internal.di.Unauthenticated import org.matrix.android.sdk.internal.network.RetrofitFactory import org.matrix.android.sdk.internal.session.SessionScope -import okhttp3.OkHttpClient import javax.inject.Inject @SessionScope diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/helper/UserAccountWidgets.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/helper/UserAccountWidgets.kt index 6f423b38a09..21fbb77667b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/helper/UserAccountWidgets.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/helper/UserAccountWidgets.kt @@ -16,11 +16,11 @@ package org.matrix.android.sdk.internal.session.widgets.helper +import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.toModel -import org.matrix.android.sdk.api.util.JsonDict -import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent import org.matrix.android.sdk.api.session.widgets.model.Widget +import org.matrix.android.sdk.api.util.JsonDict internal fun UserAccountDataEvent.extractWidgetSequence(widgetFactory: WidgetFactory): Sequence { return content.asSequence() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/helper/WidgetFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/helper/WidgetFactory.kt index a469a9fe976..a5e74a8af0f 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/helper/WidgetFactory.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/helper/WidgetFactory.kt @@ -20,11 +20,14 @@ import org.matrix.android.sdk.api.session.content.ContentUrlResolver import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.sender.SenderInfo +import org.matrix.android.sdk.api.session.user.model.User import org.matrix.android.sdk.api.session.widgets.model.Widget import org.matrix.android.sdk.api.session.widgets.model.WidgetContent import org.matrix.android.sdk.api.session.widgets.model.WidgetType +import org.matrix.android.sdk.api.util.toMatrixItem import org.matrix.android.sdk.internal.database.RealmSessionProvider import org.matrix.android.sdk.internal.di.UserId +import org.matrix.android.sdk.internal.session.displayname.DisplayNameResolver import org.matrix.android.sdk.internal.session.room.membership.RoomMemberHelper import org.matrix.android.sdk.internal.session.user.UserDataSource import java.net.URLEncoder @@ -32,6 +35,7 @@ import javax.inject.Inject internal class WidgetFactory @Inject constructor(private val userDataSource: UserDataSource, private val realmSessionProvider: RealmSessionProvider, + private val displayNameResolver: DisplayNameResolver, private val urlResolver: ContentUrlResolver, @UserId private val userId: String) { @@ -68,13 +72,13 @@ internal class WidgetFactory @Inject constructor(private val userDataSource: Use // Ref: https://github.com/matrix-org/matrix-widget-api/blob/master/src/templating/url-template.ts#L29-L33 fun computeURL(widget: Widget, isLightTheme: Boolean): String? { var computedUrl = widget.widgetContent.url ?: return null - val myUser = userDataSource.getUser(userId) + val myUser = userDataSource.getUser(userId) ?: User(userId) val keyValue = widget.widgetContent.data.mapKeys { "\$${it.key}" }.toMutableMap() keyValue[WIDGET_PATTERN_MATRIX_USER_ID] = userId - keyValue[WIDGET_PATTERN_MATRIX_DISPLAY_NAME] = myUser?.getBestName() ?: userId - keyValue[WIDGET_PATTERN_MATRIX_AVATAR_URL] = urlResolver.resolveFullSize(myUser?.avatarUrl) ?: "" + keyValue[WIDGET_PATTERN_MATRIX_DISPLAY_NAME] = displayNameResolver.getBestName(myUser.toMatrixItem()) + keyValue[WIDGET_PATTERN_MATRIX_AVATAR_URL] = urlResolver.resolveFullSize(myUser.avatarUrl) ?: "" keyValue[WIDGET_PATTERN_MATRIX_WIDGET_ID] = widget.widgetId keyValue[WIDGET_PATTERN_MATRIX_ROOM_ID] = widget.event.roomId ?: "" keyValue[WIDGET_PATTERN_THEME] = getTheme(isLightTheme) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/token/GetScalarTokenTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/token/GetScalarTokenTask.kt index 78a40d19775..17797cad52d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/token/GetScalarTokenTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/widgets/token/GetScalarTokenTask.kt @@ -18,9 +18,9 @@ package org.matrix.android.sdk.internal.session.widgets.token import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.failure.MatrixError +import org.matrix.android.sdk.api.session.widgets.WidgetManagementFailure import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.session.openid.GetOpenIdTokenTask -import org.matrix.android.sdk.api.session.widgets.WidgetManagementFailure import org.matrix.android.sdk.internal.session.widgets.WidgetsAPI import org.matrix.android.sdk.internal.session.widgets.WidgetsAPIProvider import org.matrix.android.sdk.internal.task.Task diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/task/CoroutineToCallback.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/task/CoroutineToCallback.kt index ca4b092e071..de6981ec030 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/task/CoroutineToCallback.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/task/CoroutineToCallback.kt @@ -16,15 +16,15 @@ package org.matrix.android.sdk.internal.task -import org.matrix.android.sdk.api.MatrixCallback -import org.matrix.android.sdk.api.util.Cancelable -import org.matrix.android.sdk.internal.extensions.foldToCallback -import org.matrix.android.sdk.internal.util.toCancelable import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import org.matrix.android.sdk.api.MatrixCallback +import org.matrix.android.sdk.api.util.Cancelable +import org.matrix.android.sdk.internal.extensions.foldToCallback +import org.matrix.android.sdk.internal.util.toCancelable import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/task/Task.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/task/Task.kt index a5d031e02ae..01066a6f7c6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/task/Task.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/task/Task.kt @@ -25,7 +25,7 @@ internal interface Task { suspend fun execute(params: PARAMS): RESULT - suspend fun executeRetry(params: PARAMS, remainingRetry: Int) : RESULT { + suspend fun executeRetry(params: PARAMS, remainingRetry: Int): RESULT { return try { execute(params) } catch (failure: Throwable) { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/task/TaskExecutor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/task/TaskExecutor.kt index 4da16eff226..86848d10188 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/task/TaskExecutor.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/task/TaskExecutor.kt @@ -16,16 +16,16 @@ package org.matrix.android.sdk.internal.task -import org.matrix.android.sdk.api.util.Cancelable -import org.matrix.android.sdk.internal.di.MatrixScope -import org.matrix.android.sdk.internal.extensions.foldToCallback -import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers -import org.matrix.android.sdk.internal.util.toCancelable import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancelChildren import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import org.matrix.android.sdk.api.util.Cancelable +import org.matrix.android.sdk.internal.di.MatrixScope +import org.matrix.android.sdk.internal.extensions.foldToCallback +import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers +import org.matrix.android.sdk.internal.util.toCancelable import timber.log.Timber import javax.inject.Inject import kotlin.coroutines.EmptyCoroutineContext diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/CancelableCoroutine.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/CancelableCoroutine.kt index 860cf66c781..f398ee25d8b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/CancelableCoroutine.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/CancelableCoroutine.kt @@ -16,8 +16,8 @@ package org.matrix.android.sdk.internal.util -import org.matrix.android.sdk.api.util.Cancelable import kotlinx.coroutines.Job +import org.matrix.android.sdk.api.util.Cancelable internal fun Job.toCancelable(): Cancelable { return CancelableCoroutine(this) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/JsonCanonicalizer.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/JsonCanonicalizer.kt index 7b45bab3652..a34b91a70bb 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/JsonCanonicalizer.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/JsonCanonicalizer.kt @@ -17,10 +17,10 @@ package org.matrix.android.sdk.internal.util import androidx.annotation.VisibleForTesting -import org.matrix.android.sdk.internal.di.MoshiProvider import org.json.JSONArray import org.json.JSONException import org.json.JSONObject +import org.matrix.android.sdk.internal.di.MoshiProvider import timber.log.Timber import java.util.TreeSet diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/LogUtil.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/LogUtil.kt index 4656856bf78..6fd907d3972 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/LogUtil.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/LogUtil.kt @@ -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 Collection.logLimit(maxQuantity: Int = 5): String { @@ -32,14 +33,15 @@ internal fun Collection.logLimit(maxQuantity: Int = 5): String { } internal suspend fun 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 } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/StringUtils.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/StringUtils.kt index aa0b92aa458..8a6ec189863 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/StringUtils.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/StringUtils.kt @@ -78,7 +78,7 @@ internal val spaceChars = "[\u00A0\u2000-\u200B\u2800\u3000]".toRegex() /** * Strip all the UTF-8 chars which are actually spaces */ -internal fun String.replaceSpaceChars() = replace(spaceChars, "") +internal fun String.replaceSpaceChars(replacement: String = "") = replace(spaceChars, replacement) // String.capitalize is now deprecated internal fun String.safeCapitalize(): String { @@ -90,3 +90,5 @@ internal fun String.safeCapitalize(): String { } } } + +internal fun String.removeInvalidRoomNameChars() = "[^a-z0-9._%#@=+-]".toRegex().replace(this, "") diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/system/DefaultBuildVersionSdkIntProvider.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/system/DefaultBuildVersionSdkIntProvider.kt index d9f0064f38f..2d5e4b944a5 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/system/DefaultBuildVersionSdkIntProvider.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/system/DefaultBuildVersionSdkIntProvider.kt @@ -19,7 +19,7 @@ package org.matrix.android.sdk.internal.util.system import android.os.Build import javax.inject.Inject -internal class DefaultBuildVersionSdkIntProvider @Inject constructor() - : BuildVersionSdkIntProvider { +internal class DefaultBuildVersionSdkIntProvider @Inject constructor() : + BuildVersionSdkIntProvider { override fun get() = Build.VERSION.SDK_INT } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/wellknown/GetWellknownTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/wellknown/GetWellknownTask.kt index f11e87e1e72..82ff9a321fc 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/wellknown/GetWellknownTask.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/wellknown/GetWellknownTask.kt @@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.wellknown import android.util.MalformedJsonException import dagger.Lazy +import okhttp3.OkHttpClient import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig import org.matrix.android.sdk.api.auth.data.WellKnown import org.matrix.android.sdk.api.auth.wellknown.WellknownResult @@ -31,7 +32,6 @@ import org.matrix.android.sdk.internal.session.homeserver.CapabilitiesAPI import org.matrix.android.sdk.internal.session.identity.IdentityAuthAPI import org.matrix.android.sdk.internal.task.Task import org.matrix.android.sdk.internal.util.isValidUrl -import okhttp3.OkHttpClient import java.io.EOFException import javax.inject.Inject import javax.net.ssl.HttpsURLConnection diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/AlwaysSuccessfulWorker.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/AlwaysSuccessfulWorker.kt index 3506a76f758..856d3debcf3 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/AlwaysSuccessfulWorker.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/worker/AlwaysSuccessfulWorker.kt @@ -19,8 +19,8 @@ import android.content.Context import androidx.work.Worker import androidx.work.WorkerParameters -internal class AlwaysSuccessfulWorker(context: Context, params: WorkerParameters) - : Worker(context, params) { +internal class AlwaysSuccessfulWorker(context: Context, params: WorkerParameters) : + Worker(context, params) { override fun doWork(): Result { return Result.success() diff --git a/matrix-sdk-android/src/release/java/org/matrix/android/sdk/internal/network/interceptors/CurlLoggingInterceptor.kt b/matrix-sdk-android/src/release/java/org/matrix/android/sdk/internal/network/interceptors/CurlLoggingInterceptor.kt index f46e2ca35ba..47f6869479d 100644 --- a/matrix-sdk-android/src/release/java/org/matrix/android/sdk/internal/network/interceptors/CurlLoggingInterceptor.kt +++ b/matrix-sdk-android/src/release/java/org/matrix/android/sdk/internal/network/interceptors/CurlLoggingInterceptor.kt @@ -26,8 +26,8 @@ import javax.inject.Inject * No op interceptor */ @MatrixScope -internal class CurlLoggingInterceptor @Inject constructor() - : Interceptor { +internal class CurlLoggingInterceptor @Inject constructor() : + Interceptor { @Throws(IOException::class) override fun intercept(chain: Interceptor.Chain): Response { diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/MatrixTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/MatrixTest.kt index f6a7f525dbe..84f09eb1849 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/MatrixTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/MatrixTest.kt @@ -16,8 +16,8 @@ package org.matrix.android.sdk -import org.matrix.android.sdk.test.shared.createTimberTestRule import org.junit.Rule +import org.matrix.android.sdk.test.shared.createTimberTestRule interface MatrixTest { diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/api/auth/data/VersionsKtTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/api/auth/data/VersionsKtTest.kt index c413d9ccaeb..96655b849df 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/api/auth/data/VersionsKtTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/api/auth/data/VersionsKtTest.kt @@ -16,10 +16,10 @@ package org.matrix.android.sdk.api.auth.data -import org.matrix.android.sdk.internal.auth.version.Versions -import org.matrix.android.sdk.internal.auth.version.isSupportedBySdk import org.amshove.kluent.shouldBe import org.junit.Test +import org.matrix.android.sdk.internal.auth.version.Versions +import org.matrix.android.sdk.internal.auth.version.isSupportedBySdk class VersionsKtTest { diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/api/pushrules/PushRuleActionsTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/api/pushrules/PushRuleActionsTest.kt index b734444990e..9bfdea54143 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/api/pushrules/PushRuleActionsTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/api/pushrules/PushRuleActionsTest.kt @@ -16,15 +16,15 @@ package org.matrix.android.sdk.api.pushrules -import org.matrix.android.sdk.MatrixTest -import org.matrix.android.sdk.api.pushrules.rest.PushRule -import org.matrix.android.sdk.internal.di.MoshiProvider import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull import org.junit.Assert.assertTrue import org.junit.Test +import org.matrix.android.sdk.MatrixTest +import org.matrix.android.sdk.api.pushrules.rest.PushRule +import org.matrix.android.sdk.internal.di.MoshiProvider -class PushRuleActionsTest: MatrixTest { +class PushRuleActionsTest : MatrixTest { @Test fun test_action_parsing() { diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/keysbackup/util/Base58Test.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/keysbackup/util/Base58Test.kt index 2f01a43a67a..a93883a3440 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/keysbackup/util/Base58Test.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/keysbackup/util/Base58Test.kt @@ -16,15 +16,15 @@ package org.matrix.android.sdk.internal.crypto.keysbackup.util -import org.matrix.android.sdk.MatrixTest import org.junit.Assert.assertArrayEquals import org.junit.Assert.assertEquals import org.junit.FixMethodOrder import org.junit.Test import org.junit.runners.MethodSorters +import org.matrix.android.sdk.MatrixTest @FixMethodOrder(MethodSorters.JVM) -class Base58Test: MatrixTest { +class Base58Test : MatrixTest { @Test fun encode() { diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/keysbackup/util/RecoveryKeyTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/keysbackup/util/RecoveryKeyTest.kt index 64ffe52acdd..4e4548b1972 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/keysbackup/util/RecoveryKeyTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/keysbackup/util/RecoveryKeyTest.kt @@ -16,14 +16,14 @@ package org.matrix.android.sdk.internal.crypto.keysbackup.util -import org.matrix.android.sdk.MatrixTest import org.junit.Assert.assertArrayEquals import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Test +import org.matrix.android.sdk.MatrixTest -class RecoveryKeyTest: MatrixTest { +class RecoveryKeyTest : MatrixTest { private val curve25519Key = byteArrayOf( 0x77.toByte(), 0x07.toByte(), 0x6D.toByte(), 0x0A.toByte(), 0x73.toByte(), 0x18.toByte(), 0xA5.toByte(), 0x7D.toByte(), diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/store/db/HelperTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/store/db/HelperTest.kt index 0bcc7983c52..b50d0581b0c 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/store/db/HelperTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/store/db/HelperTest.kt @@ -16,12 +16,12 @@ package org.matrix.android.sdk.internal.crypto.store.db -import org.matrix.android.sdk.MatrixTest -import org.matrix.android.sdk.internal.util.md5 import org.junit.Assert.assertEquals import org.junit.Test +import org.matrix.android.sdk.MatrixTest +import org.matrix.android.sdk.internal.util.md5 -class HelperTest: MatrixTest { +class HelperTest : MatrixTest { @Test fun testHash_ok() { diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/BinaryStringTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/BinaryStringTest.kt index b04834f9f4d..5a82052d1af 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/BinaryStringTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/BinaryStringTest.kt @@ -16,11 +16,11 @@ package org.matrix.android.sdk.internal.crypto.verification.qrcode -import org.matrix.android.sdk.MatrixTest import org.amshove.kluent.shouldBeEqualTo import org.junit.FixMethodOrder import org.junit.Test import org.junit.runners.MethodSorters +import org.matrix.android.sdk.MatrixTest @FixMethodOrder(MethodSorters.JVM) class BinaryStringTest : MatrixTest { diff --git a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/task/CoroutineSequencersTest.kt b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/task/CoroutineSequencersTest.kt index 3572a1a5468..0abca8bee3d 100644 --- a/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/task/CoroutineSequencersTest.kt +++ b/matrix-sdk-android/src/test/java/org/matrix/android/sdk/internal/task/CoroutineSequencersTest.kt @@ -16,7 +16,6 @@ package org.matrix.android.sdk.internal.task -import org.matrix.android.sdk.MatrixTest import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.delay @@ -25,9 +24,10 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import org.junit.Assert.assertEquals import org.junit.Test +import org.matrix.android.sdk.MatrixTest import java.util.concurrent.Executors -class CoroutineSequencersTest: MatrixTest { +class CoroutineSequencersTest : MatrixTest { private val dispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher() diff --git a/multipicker/build.gradle b/multipicker/build.gradle index b2a5148b4a9..437499df7bd 100644 --- a/multipicker/build.gradle +++ b/multipicker/build.gradle @@ -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 } diff --git a/newsfragment/3207.bugfix b/newsfragment/3207.bugfix deleted file mode 100644 index 162485907b4..00000000000 --- a/newsfragment/3207.bugfix +++ /dev/null @@ -1 +0,0 @@ -Space Explore Rooms no feedback on failed to join \ No newline at end of file diff --git a/newsfragment/3520.misc b/newsfragment/3520.misc deleted file mode 100644 index d015c3e03d8..00000000000 --- a/newsfragment/3520.misc +++ /dev/null @@ -1 +0,0 @@ -VoIP: Merge virtual room timeline in corresponding native room (call events only). \ No newline at end of file diff --git a/newsfragment/3531.feature b/newsfragment/3531.feature deleted file mode 100644 index e8b63584e33..00000000000 --- a/newsfragment/3531.feature +++ /dev/null @@ -1 +0,0 @@ -Introduces AutoAcceptInvites which can be enabled at compile time. \ No newline at end of file diff --git a/tools/benchmark/benchmark.profile b/tools/benchmark/benchmark.profile new file mode 100644 index 00000000000..ae27dc9f59c --- /dev/null +++ b/tools/benchmark/benchmark.profile @@ -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" +} diff --git a/tools/benchmark/run_benchmark.sh b/tools/benchmark/run_benchmark.sh new file mode 100755 index 00000000000..b6c81ee513a --- /dev/null +++ b/tools/benchmark/run_benchmark.sh @@ -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 diff --git a/tools/check/forbidden_strings_in_code.txt b/tools/check/forbidden_strings_in_code.txt index 391140b9f32..8f8625fe1cf 100644 --- a/tools/check/forbidden_strings_in_code.txt +++ b/tools/check/forbidden_strings_in_code.txt @@ -141,8 +141,6 @@ android\.app\.AlertDialog androidx\.appcompat\.app\.AlertDialog===4 ### Put the operator at the beginning of next line -&&$ -\|\|$ ==$ ### Use JsonUtils.getBasicGson() @@ -162,7 +160,7 @@ Formatter\.formatShortFileSize===1 # android\.text\.TextUtils ### This is not a rule, but a warning: the number of "enum class" has changed. For Json classes, it is mandatory that they have `@JsonClass(generateAdapter = false)`. If the enum is not used as a Json class, change the value in file forbidden_strings_in_code.txt -enum class===105 +enum class===106 ### Do not import temporary legacy classes import org.matrix.android.sdk.internal.legacy.riot===3 diff --git a/tools/release/pushPlayStoreMetaData.sh b/tools/release/pushPlayStoreMetaData.sh index ad7288ac36b..fe7cf7eca65 100755 --- a/tools/release/pushPlayStoreMetaData.sh +++ b/tools/release/pushPlayStoreMetaData.sh @@ -29,7 +29,8 @@ mv ./fastlane/metadata/android/ga ./fastlane_tmp mv ./fastlane/metadata/android/kab ./fastlane_tmp mv ./fastlane/metadata/android/nb ./fastlane_tmp -# Fastlane / PlayStore require longDescription and shortDescription file to be set, so copy the default one for +# Fastlane / PlayStore require longDescription and shortDescription file to be set, so copy the default +# one for languages where they are missing echo "Copying default description when missing" if [[ -f "./fastlane/metadata/android/ro/full_description.txt" ]]; then echo "It appears that file ./fastlane/metadata/android/ro/full_description.txt now exists. This can be removed." diff --git a/vector/build.gradle b/vector/build.gradle index 9d3cc7ec3ce..f2cb2831bd1 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -13,8 +13,8 @@ kapt { // Note: 2 digits max for each value ext.versionMajor = 1 -ext.versionMinor = 2 -ext.versionPatch = 1 +ext.versionMinor = 3 +ext.versionPatch = 2 static def getGitTimestamp() { def cmd = 'git show -s --format=%ct' @@ -102,17 +102,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 "im.vector.app" // Set to API 21: see #405 - minSdkVersion 21 - targetSdkVersion 30 + minSdk versions.minSdk + targetSdk versions.targetSdk multiDexEnabled true renderscriptTargetApi 24 @@ -289,8 +292,8 @@ android { } compileOptions { - sourceCompatibility JavaVersion.VERSION_11 - targetCompatibility JavaVersion.VERSION_11 + sourceCompatibility versions.sourceCompat + targetCompatibility versions.targetCompat } kotlinOptions { @@ -311,27 +314,12 @@ android { } } -dependencies { +configurations { + // videocache includes a sl4j logger which causes mockk to attempt to call the static android Log + testImplementation.exclude group: 'org.slf4j', module: 'slf4j-android' +} - 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' +dependencies { implementation project(":matrix-sdk-android") implementation project(":matrix-sdk-android-rx") @@ -341,73 +329,77 @@ 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.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version" - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version" + implementation libs.jetbrains.kotlinStdlibJdk7 + 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 "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 + 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' @@ -420,27 +412,27 @@ dependencies { implementation 'com.nulab-inc:zxcvbn:1.5.2' // To convert voice message on old platforms - implementation 'com.arthenica:ffmpeg-kit-audio:4.4.LTS' + implementation 'com.arthenica:ffmpeg-kit-audio:4.5.LTS' - //Alerter + // Alerter implementation 'com.tapadoo.android:alerter:7.0.1' implementation 'com.otaliastudios:autocomplete:1.1.0' // Shake detection - implementation 'com.squareup:seismic:1.0.2' + implementation 'com.squareup:seismic:1.0.3' // 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' @@ -451,8 +443,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 // gplay flavor only gplayImplementation('com.google.firebase:firebase-messaging:22.0.0') { @@ -486,42 +478,44 @@ dependencies { implementation 'me.dm7.barcodescanner:zxing:1.9.13' // Emoji Keyboard - implementation 'com.vanniktech:emoji-material:0.7.0' - implementation 'com.vanniktech:emoji-google:0.7.0' + implementation libs.vanniktech.emojiMaterial + implementation libs.vanniktech.emojiGoogle 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 + testImplementation libs.mockk.mockk // 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 } diff --git a/vector/lint.xml b/vector/lint.xml index a8eed30160f..dde29af62e1 100644 --- a/vector/lint.xml +++ b/vector/lint.xml @@ -21,12 +21,12 @@ + - @@ -47,6 +47,7 @@ + diff --git a/vector/src/androidTest/java/im/vector/app/EspressoExt.kt b/vector/src/androidTest/java/im/vector/app/EspressoExt.kt index d247d88caa3..823ce830158 100644 --- a/vector/src/androidTest/java/im/vector/app/EspressoExt.kt +++ b/vector/src/androidTest/java/im/vector/app/EspressoExt.kt @@ -129,7 +129,7 @@ fun activityIdlingResource(activityClass: Class<*>): IdlingResource { private var callback: IdlingResource.ResourceCallback? = null var hasResumed = false - private var currentActivity : Activity? = null + private var currentActivity: Activity? = null val uniqTS = System.currentTimeMillis() override fun getName() = "activityIdlingResource_${activityClass.name}_$uniqTS" diff --git a/vector/src/androidTest/java/im/vector/app/VerifySessionInteractiveTest.kt b/vector/src/androidTest/java/im/vector/app/VerifySessionInteractiveTest.kt index 571bcf474ce..982a421425a 100644 --- a/vector/src/androidTest/java/im/vector/app/VerifySessionInteractiveTest.kt +++ b/vector/src/androidTest/java/im/vector/app/VerifySessionInteractiveTest.kt @@ -44,14 +44,14 @@ import org.junit.runner.RunWith import org.matrix.android.sdk.api.Matrix import org.matrix.android.sdk.api.auth.UIABaseAuth import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor +import org.matrix.android.sdk.api.auth.UserPasswordAuth +import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.crypto.verification.SasVerificationTransaction import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod import org.matrix.android.sdk.api.session.crypto.verification.VerificationService import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState -import org.matrix.android.sdk.api.auth.UserPasswordAuth -import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse import kotlin.coroutines.Continuation import kotlin.coroutines.resume diff --git a/vector/src/androidTest/java/im/vector/app/espresso/tools/EspressoPreference.kt b/vector/src/androidTest/java/im/vector/app/espresso/tools/EspressoPreference.kt index bf60ad681f8..178b9fb9f63 100644 --- a/vector/src/androidTest/java/im/vector/app/espresso/tools/EspressoPreference.kt +++ b/vector/src/androidTest/java/im/vector/app/espresso/tools/EspressoPreference.kt @@ -30,9 +30,9 @@ import androidx.test.espresso.matcher.ViewMatchers.withClassName import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withText import im.vector.app.R -import org.hamcrest.Matchers.`is` import org.hamcrest.Matchers.allOf import org.hamcrest.Matchers.instanceOf +import org.hamcrest.Matchers.`is` fun clickOnPreference(@StringRes textResId: Int) { onView(withId(R.id.recycler_view)) diff --git a/vector/src/androidTest/java/im/vector/app/features/reactions/data/EmojiDataSourceTest.kt b/vector/src/androidTest/java/im/vector/app/features/reactions/data/EmojiDataSourceTest.kt index 79090c42dd0..a880b17e0c8 100644 --- a/vector/src/androidTest/java/im/vector/app/features/reactions/data/EmojiDataSourceTest.kt +++ b/vector/src/androidTest/java/im/vector/app/features/reactions/data/EmojiDataSourceTest.kt @@ -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) } diff --git a/vector/src/androidTest/java/im/vector/app/ui/UiAllScreensSanityTest.kt b/vector/src/androidTest/java/im/vector/app/ui/UiAllScreensSanityTest.kt index bad5d29e06d..aef5d3fe495 100644 --- a/vector/src/androidTest/java/im/vector/app/ui/UiAllScreensSanityTest.kt +++ b/vector/src/androidTest/java/im/vector/app/ui/UiAllScreensSanityTest.kt @@ -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() } diff --git a/vector/src/debug/java/im/vector/app/features/debug/DebugMenuActivity.kt b/vector/src/debug/java/im/vector/app/features/debug/DebugMenuActivity.kt index 4b5228d1991..303a3a14ba9 100644 --- a/vector/src/debug/java/im/vector/app/features/debug/DebugMenuActivity.kt +++ b/vector/src/debug/java/im/vector/app/features/debug/DebugMenuActivity.kt @@ -235,8 +235,8 @@ class DebugMenuActivity : VectorBaseActivity() { private val qrStartForActivityResult = registerStartForActivityResult { activityResult -> if (activityResult.resultCode == Activity.RESULT_OK) { - toast("QrCode: " + QrCodeScannerActivity.getResultText(activityResult.data) - + " is QRCode: " + QrCodeScannerActivity.getResultIsQrCode(activityResult.data)) + toast("QrCode: " + QrCodeScannerActivity.getResultText(activityResult.data) + + " is QRCode: " + QrCodeScannerActivity.getResultIsQrCode(activityResult.data)) // Also update the current QR Code (reverse operation) // renderQrCode(QrCodeScannerActivity.getResultText(data) ?: "") diff --git a/vector/src/debug/java/im/vector/app/features/debug/sas/SasEmojiItem.kt b/vector/src/debug/java/im/vector/app/features/debug/sas/SasEmojiItem.kt index 408aebb1867..a7b74f3b593 100644 --- a/vector/src/debug/java/im/vector/app/features/debug/sas/SasEmojiItem.kt +++ b/vector/src/debug/java/im/vector/app/features/debug/sas/SasEmojiItem.kt @@ -23,9 +23,9 @@ import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyModelClass import im.vector.app.core.epoxy.VectorEpoxyHolder import im.vector.app.core.epoxy.VectorEpoxyModel -import org.matrix.android.sdk.api.session.crypto.verification.EmojiRepresentation import me.gujun.android.span.image import me.gujun.android.span.span +import org.matrix.android.sdk.api.session.crypto.verification.EmojiRepresentation @EpoxyModelClass(layout = im.vector.app.R.layout.item_sas_emoji) abstract class SasEmojiItem : VectorEpoxyModel() { diff --git a/vector/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestAutoStartBoot.kt b/vector/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestAutoStartBoot.kt index e46a07f7126..27a3f09ddc9 100644 --- a/vector/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestAutoStartBoot.kt +++ b/vector/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestAutoStartBoot.kt @@ -27,8 +27,8 @@ import javax.inject.Inject * Test that the application is started on boot */ class TestAutoStartBoot @Inject constructor(private val vectorPreferences: VectorPreferences, - private val stringProvider: StringProvider) - : TroubleshootTest(R.string.settings_troubleshoot_test_service_boot_title) { + private val stringProvider: StringProvider) : + TroubleshootTest(R.string.settings_troubleshoot_test_service_boot_title) { override fun perform(activityResultLauncher: ActivityResultLauncher) { if (vectorPreferences.autoStartOnBoot()) { diff --git a/vector/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestBackgroundRestrictions.kt b/vector/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestBackgroundRestrictions.kt index abdd696724d..3725fc828d4 100644 --- a/vector/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestBackgroundRestrictions.kt +++ b/vector/src/fdroid/java/im/vector/app/fdroid/features/settings/troubleshoot/TestBackgroundRestrictions.kt @@ -27,8 +27,8 @@ import im.vector.app.features.settings.troubleshoot.TroubleshootTest import javax.inject.Inject class TestBackgroundRestrictions @Inject constructor(private val context: AppCompatActivity, - private val stringProvider: StringProvider) - : TroubleshootTest(R.string.settings_troubleshoot_test_bg_restricted_title) { + private val stringProvider: StringProvider) : + TroubleshootTest(R.string.settings_troubleshoot_test_bg_restricted_title) { override fun perform(activityResultLauncher: ActivityResultLauncher) { context.getSystemService()!!.apply { diff --git a/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestPlayServices.kt b/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestPlayServices.kt index 92e713de814..1f822d60606 100644 --- a/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestPlayServices.kt +++ b/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestPlayServices.kt @@ -30,8 +30,8 @@ import javax.inject.Inject * Check that the play services APK is available an up-to-date. If needed provide quick fix to install it. */ class TestPlayServices @Inject constructor(private val context: AppCompatActivity, - private val stringProvider: StringProvider) - : TroubleshootTest(R.string.settings_troubleshoot_test_play_services_title) { + private val stringProvider: StringProvider) : + TroubleshootTest(R.string.settings_troubleshoot_test_play_services_title) { override fun perform(activityResultLauncher: ActivityResultLauncher) { val apiAvailability = GoogleApiAvailability.getInstance() diff --git a/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestPushFromPushGateway.kt b/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestPushFromPushGateway.kt index d429b293b2b..e13b648dec9 100644 --- a/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestPushFromPushGateway.kt +++ b/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestPushFromPushGateway.kt @@ -40,8 +40,8 @@ class TestPushFromPushGateway @Inject constructor(private val context: AppCompat private val stringProvider: StringProvider, private val errorFormatter: ErrorFormatter, private val pushersManager: PushersManager, - private val activeSessionHolder: ActiveSessionHolder) - : TroubleshootTest(R.string.settings_troubleshoot_test_push_loop_title) { + private val activeSessionHolder: ActiveSessionHolder) : + TroubleshootTest(R.string.settings_troubleshoot_test_push_loop_title) { private var action: Job? = null private var pushReceived: Boolean = false diff --git a/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestTokenRegistration.kt b/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestTokenRegistration.kt index f400c17d468..966d79c59b2 100644 --- a/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestTokenRegistration.kt +++ b/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestTokenRegistration.kt @@ -21,13 +21,13 @@ import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.Observer import androidx.work.WorkInfo import androidx.work.WorkManager -import org.matrix.android.sdk.api.session.pushers.PusherState import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.pushers.PushersManager import im.vector.app.core.resources.StringProvider import im.vector.app.features.settings.troubleshoot.TroubleshootTest import im.vector.app.push.fcm.FcmHelper +import org.matrix.android.sdk.api.session.pushers.PusherState import javax.inject.Inject /** @@ -36,8 +36,8 @@ import javax.inject.Inject class TestTokenRegistration @Inject constructor(private val context: AppCompatActivity, private val stringProvider: StringProvider, private val pushersManager: PushersManager, - private val activeSessionHolder: ActiveSessionHolder) - : TroubleshootTest(R.string.settings_troubleshoot_test_token_registration_title) { + private val activeSessionHolder: ActiveSessionHolder) : + TroubleshootTest(R.string.settings_troubleshoot_test_token_registration_title) { override fun perform(activityResultLauncher: ActivityResultLauncher) { // Check if we have a registered pusher for this token diff --git a/vector/src/gplay/java/im/vector/app/gplay/push/fcm/VectorFirebaseMessagingService.kt b/vector/src/gplay/java/im/vector/app/gplay/push/fcm/VectorFirebaseMessagingService.kt index 4cefeadb623..ddedfb93e36 100755 --- a/vector/src/gplay/java/im/vector/app/gplay/push/fcm/VectorFirebaseMessagingService.kt +++ b/vector/src/gplay/java/im/vector/app/gplay/push/fcm/VectorFirebaseMessagingService.kt @@ -39,17 +39,22 @@ import im.vector.app.features.notifications.NotifiableMessageEvent import im.vector.app.features.notifications.NotificationDrawerManager import im.vector.app.features.notifications.NotificationUtils import im.vector.app.features.notifications.SimpleNotifiableEvent +import im.vector.app.features.settings.VectorDataStore import im.vector.app.features.settings.VectorPreferences import im.vector.app.push.fcm.FcmHelper 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.pushrules.Action import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.events.model.Event import timber.log.Timber +private val loggerTag = LoggerTag("Push", LoggerTag.SYNC) + /** * Class extending FirebaseMessagingService. */ @@ -60,6 +65,7 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() { 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()) @@ -77,6 +83,7 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() { pusherManager = pusherManager() activeSessionHolder = activeSessionHolder() vectorPreferences = vectorPreferences() + vectorDataStore = vectorDataStore() wifiDetector = wifiDetector() } } @@ -88,9 +95,13 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() { */ override fun onMessageReceived(message: RemoteMessage) { if (BuildConfig.LOW_PRIVACY_LOG_ENABLE) { - Timber.d("## onMessageReceived() %s", message.data.toString()) + Timber.tag(loggerTag.value).d("## onMessageReceived() %s", message.data.toString()) + } + Timber.tag(loggerTag.value).d("## onMessageReceived() from FCM with priority %s", message.priority) + + runBlocking { + vectorDataStore.incrementPushCounter() } - Timber.d("## onMessageReceived() from FCM with priority %s", message.priority) // Diagnostic Push if (message.data["event_id"] == PushersManager.TEST_EVENT_ID) { @@ -100,14 +111,14 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() { } 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(message.data) } @@ -121,7 +132,7 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() { * you retrieve the token. */ override fun onNewToken(refreshedToken: String) { - Timber.i("onNewToken: FCM Token has been updated") + Timber.tag(loggerTag.value).i("onNewToken: FCM Token has been updated") FcmHelper.storeFcmToken(this, refreshedToken) if (vectorPreferences.areNotificationEnabledForDevice() && activeSessionHolder.hasActiveSession()) { pusherManager.registerPusherWithFcmKey(refreshedToken) @@ -138,7 +149,7 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() { * It is recommended that the app do a full sync with the app server after receiving this call. */ override fun onDeletedMessages() { - Timber.v("## onDeletedMessages()") + Timber.tag(loggerTag.value).v("## onDeletedMessages()") } /** @@ -150,9 +161,9 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() { private fun onMessageReceivedInternal(data: Map) { try { if (BuildConfig.LOW_PRIVACY_LOG_ENABLE) { - Timber.d("## onMessageReceivedInternal() : $data") + Timber.tag(loggerTag.value).d("## onMessageReceivedInternal() : $data") } else { - Timber.d("## onMessageReceivedInternal() : $data") + Timber.tag(loggerTag.value).d("## onMessageReceivedInternal()") } // update the badge counter @@ -162,24 +173,24 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() { 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 { val eventId = data["event_id"] val roomId = data["room_id"] if (isEventAlreadyKnown(eventId, 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, roomId, 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") } } @@ -193,18 +204,18 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() { } 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) @@ -222,7 +233,7 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() { 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 @@ -230,7 +241,7 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() { private fun handleNotificationWithoutSyncingMode(data: Map, session: Session?) { if (session == null) { - Timber.e("## handleNotificationWithoutSyncingMode cannot find session") + Timber.tag(loggerTag.value).e("## handleNotificationWithoutSyncingMode cannot find session") return } @@ -263,9 +274,9 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() { val notifiableEvent = notifiableEventResolver.resolveEvent(event, session) if (notifiableEvent == null) { - Timber.e("Unsupported notifiable event $eventId") + Timber.tag(loggerTag.value).e("Unsupported notifiable event $eventId") if (BuildConfig.LOW_PRIVACY_LOG_ENABLE) { - Timber.e("--> $event") + Timber.tag(loggerTag.value).e("--> $event") } } else { if (notifiableEvent is NotifiableMessageEvent) { diff --git a/vector/src/main/AndroidManifest.xml b/vector/src/main/AndroidManifest.xml index 5aec41e46f9..7492da37e8d 100644 --- a/vector/src/main/AndroidManifest.xml +++ b/vector/src/main/AndroidManifest.xml @@ -83,13 +83,19 @@ android:name="android.max_aspect" android:value="9.9" /> + + + @@ -103,10 +109,13 @@ + + @@ -120,10 +129,13 @@ android:scheme="element" /> + + @@ -143,7 +155,6 @@ android:name=".features.media.VectorAttachmentViewerActivity" android:theme="@style/Theme.Vector.Black.Transparent" tools:ignore="Instantiatable" /> - - @@ -180,7 +190,13 @@ - + + + + @@ -196,8 +212,37 @@ + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - + android:supportsPictureInPicture="true" + android:taskAffinity=".features.call.VectorCallActivity" /> @@ -364,7 +386,9 @@ A media button receiver receives and helps translate hardware media playback buttons, such as those found on wired and wireless headsets, into the appropriate callbacks in your app. --> - + @@ -374,9 +398,15 @@ + android:name="androidx.startup.InitializationProvider" + android:authorities="${applicationId}.androidx-startup" + android:exported="false" + tools:node="merge"> + + () { @@ -54,7 +55,7 @@ abstract class RadioButtonItem : VectorEpoxyModel() { } if (selected) { - holder.radioImage.setImageDrawable(ContextCompat.getDrawable(holder.view.context, R.drawable.ic_radio_on)) + holder.radioImage.setAttributeTintedImageResource(R.drawable.ic_radio_on, R.attr.colorPrimary) holder.radioImage.contentDescription = holder.view.context.getString(R.string.a11y_checked) } else { holder.radioImage.setImageDrawable(ContextCompat.getDrawable(holder.view.context, R.drawable.ic_radio_off)) diff --git a/vector/src/main/java/im/vector/app/core/error/ErrorFormatter.kt b/vector/src/main/java/im/vector/app/core/error/ErrorFormatter.kt index f0ba79e31cc..6494f31336a 100644 --- a/vector/src/main/java/im/vector/app/core/error/ErrorFormatter.kt +++ b/vector/src/main/java/im/vector/app/core/error/ErrorFormatter.kt @@ -58,49 +58,49 @@ class DefaultErrorFormatter @Inject constructor( } is Failure.ServerError -> { when { - throwable.error.code == MatrixError.M_CONSENT_NOT_GIVEN -> { + throwable.error.code == MatrixError.M_CONSENT_NOT_GIVEN -> { // Special case for terms and conditions stringProvider.getString(R.string.error_terms_not_accepted) } - throwable.isInvalidPassword() -> { + throwable.isInvalidPassword() -> { stringProvider.getString(R.string.auth_invalid_login_param) } - throwable.error.code == MatrixError.M_USER_IN_USE -> { + throwable.error.code == MatrixError.M_USER_IN_USE -> { stringProvider.getString(R.string.login_signup_error_user_in_use) } - throwable.error.code == MatrixError.M_BAD_JSON -> { + throwable.error.code == MatrixError.M_BAD_JSON -> { stringProvider.getString(R.string.login_error_bad_json) } - throwable.error.code == MatrixError.M_NOT_JSON -> { + throwable.error.code == MatrixError.M_NOT_JSON -> { stringProvider.getString(R.string.login_error_not_json) } - throwable.error.code == MatrixError.M_THREEPID_DENIED -> { + throwable.error.code == MatrixError.M_THREEPID_DENIED -> { stringProvider.getString(R.string.login_error_threepid_denied) } - throwable.error.code == MatrixError.M_LIMIT_EXCEEDED -> { + throwable.error.code == MatrixError.M_LIMIT_EXCEEDED -> { limitExceededError(throwable.error) } - throwable.error.code == MatrixError.M_TOO_LARGE -> { + throwable.error.code == MatrixError.M_TOO_LARGE -> { stringProvider.getString(R.string.error_file_too_big_simple) } - throwable.error.code == MatrixError.M_THREEPID_NOT_FOUND -> { + throwable.error.code == MatrixError.M_THREEPID_NOT_FOUND -> { stringProvider.getString(R.string.login_reset_password_error_not_found) } - throwable.error.code == MatrixError.M_USER_DEACTIVATED -> { + throwable.error.code == MatrixError.M_USER_DEACTIVATED -> { stringProvider.getString(R.string.auth_invalid_login_deactivated_account) } - throwable.error.code == MatrixError.M_THREEPID_IN_USE - && throwable.error.message == "Email is already in use" -> { + throwable.error.code == MatrixError.M_THREEPID_IN_USE && + throwable.error.message == "Email is already in use" -> { stringProvider.getString(R.string.account_email_already_used_error) } - throwable.error.code == MatrixError.M_THREEPID_IN_USE - && throwable.error.message == "MSISDN is already in use" -> { + throwable.error.code == MatrixError.M_THREEPID_IN_USE && + throwable.error.message == "MSISDN is already in use" -> { stringProvider.getString(R.string.account_phone_number_already_used_error) } - throwable.error.code == MatrixError.M_THREEPID_AUTH_FAILED -> { + throwable.error.code == MatrixError.M_THREEPID_AUTH_FAILED -> { stringProvider.getString(R.string.error_threepid_auth_failed) } - else -> { + else -> { throwable.error.message.takeIf { it.isNotEmpty() } ?: throwable.error.code.takeIf { it.isNotEmpty() } } diff --git a/vector/src/main/java/im/vector/app/core/extensions/BasicExtensions.kt b/vector/src/main/java/im/vector/app/core/extensions/BasicExtensions.kt index 07a684abefa..ee3d79d8461 100644 --- a/vector/src/main/java/im/vector/app/core/extensions/BasicExtensions.kt +++ b/vector/src/main/java/im/vector/app/core/extensions/BasicExtensions.kt @@ -57,7 +57,7 @@ fun CharSequence.isMsisdn(): Boolean { * - "fi.le.txt".insertBeforeLast("_foo") will return "fi.le_foo.txt" * - null.insertBeforeLast("_foo") will return "_foo" */ -fun String?.insertBeforeLast(insert: String, delimiter: String = ".") : String { +fun String?.insertBeforeLast(insert: String, delimiter: String = "."): String { if (this == null) return insert val idx = lastIndexOf(delimiter) return if (idx == -1) { diff --git a/vector/src/main/java/im/vector/app/core/extensions/EditText.kt b/vector/src/main/java/im/vector/app/core/extensions/EditText.kt index 05b70def3d5..0eb9dcdaf94 100644 --- a/vector/src/main/java/im/vector/app/core/extensions/EditText.kt +++ b/vector/src/main/java/im/vector/app/core/extensions/EditText.kt @@ -18,6 +18,7 @@ package im.vector.app.core.extensions import android.text.Editable import android.text.InputType +import android.text.Spanned import android.view.MotionEvent import android.view.View import android.view.inputmethod.EditorInfo @@ -57,3 +58,38 @@ fun EditText.setupAsSearch(@DrawableRes searchIconRes: Int = R.drawable.ic_searc return@OnTouchListener false }) } + +fun EditText.setTextIfDifferent(newText: CharSequence?): Boolean { + if (!isTextDifferent(newText, text)) { + // Previous text is the same. No op + return false + } + setText(newText) + // Since the text changed we move the cursor to the end of the new text. + // This allows us to fill in text programmatically with a different value, + // but if the user is typing and the view is rebound we won't lose their cursor position. + setSelection(newText?.length ?: 0) + return true +} + +private fun isTextDifferent(str1: CharSequence?, str2: CharSequence?): Boolean { + if (str1 === str2) { + return false + } + if (str1 == null || str2 == null) { + return true + } + val length = str1.length + if (length != str2.length) { + return true + } + if (str1 is Spanned) { + return str1 != str2 + } + for (i in 0 until length) { + if (str1[i] != str2[i]) { + return true + } + } + return false +} diff --git a/vector/src/main/java/im/vector/app/core/extensions/Session.kt b/vector/src/main/java/im/vector/app/core/extensions/Session.kt index 699247ab6d8..215c4212919 100644 --- a/vector/src/main/java/im/vector/app/core/extensions/Session.kt +++ b/vector/src/main/java/im/vector/app/core/extensions/Session.kt @@ -62,15 +62,15 @@ fun Session.startSyncing(context: Context) { * Tell is the session has unsaved e2e keys in the backup */ fun Session.hasUnsavedKeys(): Boolean { - return cryptoService().inboundGroupSessionsCount(false) > 0 - && cryptoService().keysBackupService().state != KeysBackupState.ReadyToBackUp + return cryptoService().inboundGroupSessionsCount(false) > 0 && + cryptoService().keysBackupService().state != KeysBackupState.ReadyToBackUp } fun Session.cannotLogoutSafely(): Boolean { // has some encrypted chat - return hasUnsavedKeys() + return hasUnsavedKeys() || // has local cross signing keys - || (cryptoService().crossSigningService().allPrivateKeysKnown() + (cryptoService().crossSigningService().allPrivateKeysKnown() && // That are not backed up - && !sharedSecretStorageService.isRecoverySetup()) + !sharedSecretStorageService.isRecoverySetup()) } diff --git a/vector/src/main/java/im/vector/app/core/extensions/TextView.kt b/vector/src/main/java/im/vector/app/core/extensions/TextView.kt index 1c424f7071c..adb655f169e 100644 --- a/vector/src/main/java/im/vector/app/core/extensions/TextView.kt +++ b/vector/src/main/java/im/vector/app/core/extensions/TextView.kt @@ -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) { - if (newText == null - || (newText.isBlank() && hideWhenBlank)) { +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 } } } diff --git a/vector/src/main/java/im/vector/app/core/extensions/TimelineEvent.kt b/vector/src/main/java/im/vector/app/core/extensions/TimelineEvent.kt index 48e3a488edd..43ff186e994 100644 --- a/vector/src/main/java/im/vector/app/core/extensions/TimelineEvent.kt +++ b/vector/src/main/java/im/vector/app/core/extensions/TimelineEvent.kt @@ -22,7 +22,7 @@ import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent fun TimelineEvent.canReact(): Boolean { // Only event of type EventType.MESSAGE or EventType.STICKER are supported for the moment - return root.getClearType() in listOf(EventType.MESSAGE, EventType.STICKER) - && root.sendState == SendState.SYNCED - && !root.isRedacted() + return root.getClearType() in listOf(EventType.MESSAGE, EventType.STICKER) && + root.sendState == SendState.SYNCED && + !root.isRedacted() } diff --git a/vector/src/main/java/im/vector/app/core/extensions/ViewExtensions.kt b/vector/src/main/java/im/vector/app/core/extensions/ViewExtensions.kt index 92dc76670fb..54fcac42d19 100644 --- a/vector/src/main/java/im/vector/app/core/extensions/ViewExtensions.kt +++ b/vector/src/main/java/im/vector/app/core/extensions/ViewExtensions.kt @@ -16,12 +16,20 @@ 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.annotation.AttrRes +import androidx.annotation.DrawableRes import androidx.appcompat.widget.SearchView +import androidx.core.content.ContextCompat +import androidx.core.graphics.drawable.DrawableCompat +import androidx.core.view.isVisible import im.vector.app.R +import im.vector.app.features.themes.ThemeUtils /** * Remove left margin of a SearchView @@ -50,3 +58,25 @@ fun View.getMeasurements(): Pair { val height = measuredHeight return width to height } + +fun ImageView.setDrawableOrHide(drawableRes: Drawable?) { + setImageDrawable(drawableRes) + isVisible = drawableRes != null +} + +fun View.setAttributeTintedBackground(@DrawableRes drawableRes: Int, @AttrRes tint: Int) { + val drawable = ContextCompat.getDrawable(context, drawableRes)!! + DrawableCompat.setTint(drawable, ThemeUtils.getColor(context, tint)) + background = drawable +} + +fun ImageView.setAttributeTintedImageResource(@DrawableRes drawableRes: Int, @AttrRes tint: Int) { + val drawable = ContextCompat.getDrawable(context, drawableRes)!! + DrawableCompat.setTint(drawable, ThemeUtils.getColor(context, tint)) + setImageDrawable(drawable) +} + +fun View.setAttributeBackground(@AttrRes attributeId: Int) { + val attribute = ThemeUtils.getAttribute(context, attributeId)!! + setBackgroundResource(attribute.resourceId) +} diff --git a/vector/src/main/java/im/vector/app/core/glide/AvatarPlaceholder.kt b/vector/src/main/java/im/vector/app/core/glide/AvatarPlaceholder.kt index 32968c4f80b..9675e30042c 100644 --- a/vector/src/main/java/im/vector/app/core/glide/AvatarPlaceholder.kt +++ b/vector/src/main/java/im/vector/app/core/glide/AvatarPlaceholder.kt @@ -42,8 +42,8 @@ class AvatarPlaceholderModelLoaderFactory(private val context: Context) : ModelL } } -class AvatarPlaceholderModelLoader(private val context: Context) - : ModelLoader { +class AvatarPlaceholderModelLoader(private val context: Context) : + ModelLoader { override fun buildLoadData(model: AvatarPlaceholder, width: Int, height: Int, options: Options): ModelLoader.LoadData? { return ModelLoader.LoadData(ObjectKey(model), AvatarPlaceholderDataFetcher(context, model)) @@ -54,8 +54,8 @@ class AvatarPlaceholderModelLoader(private val context: Context) } } -class AvatarPlaceholderDataFetcher(context: Context, private val data: AvatarPlaceholder) - : DataFetcher { +class AvatarPlaceholderDataFetcher(context: Context, private val data: AvatarPlaceholder) : + DataFetcher { private val avatarRenderer = context.vectorComponent().avatarRenderer() diff --git a/vector/src/main/java/im/vector/app/core/glide/MyAppGlideModule.kt b/vector/src/main/java/im/vector/app/core/glide/MyAppGlideModule.kt index 74c9d4f0f68..59bffd95fdf 100644 --- a/vector/src/main/java/im/vector/app/core/glide/MyAppGlideModule.kt +++ b/vector/src/main/java/im/vector/app/core/glide/MyAppGlideModule.kt @@ -19,7 +19,6 @@ package im.vector.app.core.glide import android.content.Context import android.graphics.drawable.Drawable import android.util.Log - import com.bumptech.glide.Glide import com.bumptech.glide.GlideBuilder import com.bumptech.glide.Registry diff --git a/vector/src/main/java/im/vector/app/core/glide/VectorGlideModelLoader.kt b/vector/src/main/java/im/vector/app/core/glide/VectorGlideModelLoader.kt index 5fae815dfbe..7dfee7d981f 100644 --- a/vector/src/main/java/im/vector/app/core/glide/VectorGlideModelLoader.kt +++ b/vector/src/main/java/im/vector/app/core/glide/VectorGlideModelLoader.kt @@ -48,8 +48,8 @@ class VectorGlideModelLoaderFactory(private val context: Context) : ModelLoaderF } } -class VectorGlideModelLoader(private val context: Context) - : ModelLoader { +class VectorGlideModelLoader(private val context: Context) : + ModelLoader { override fun handles(model: ImageContentRenderer.Data): Boolean { // Always handle return true @@ -63,8 +63,8 @@ class VectorGlideModelLoader(private val context: Context) class VectorGlideDataFetcher(context: Context, private val data: ImageContentRenderer.Data, private val width: Int, - private val height: Int) - : DataFetcher { + private val height: Int) : + DataFetcher { private val localFilesHelper = LocalFilesHelper(context) private val activeSessionHolder = context.vectorComponent().activeSessionHolder() diff --git a/vector/src/main/java/im/vector/app/core/platform/ButtonStateView.kt b/vector/src/main/java/im/vector/app/core/platform/ButtonStateView.kt index ee2933f5426..e7e91dbfd92 100755 --- a/vector/src/main/java/im/vector/app/core/platform/ButtonStateView.kt +++ b/vector/src/main/java/im/vector/app/core/platform/ButtonStateView.kt @@ -28,8 +28,8 @@ import im.vector.app.core.epoxy.ClickListener import im.vector.app.core.epoxy.onClick import im.vector.app.databinding.ViewButtonStateBinding -class ButtonStateView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) - : FrameLayout(context, attrs, defStyle) { +class ButtonStateView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) : + FrameLayout(context, attrs, defStyle) { sealed class State { object Button : State() diff --git a/vector/src/main/java/im/vector/app/core/platform/LifecycleAwareLazy.kt b/vector/src/main/java/im/vector/app/core/platform/LifecycleAwareLazy.kt new file mode 100644 index 00000000000..283106232ef --- /dev/null +++ b/vector/src/main/java/im/vector/app/core/platform/LifecycleAwareLazy.kt @@ -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 LifecycleOwner.lifecycleAwareLazy(initializer: () -> T): Lazy = LifecycleAwareLazy(this, initializer) + +private object UninitializedValue + +class LifecycleAwareLazy( + private val owner: LifecycleOwner, + initializer: () -> T +) : Lazy, 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." +} diff --git a/vector/src/main/java/im/vector/app/core/platform/MaxHeightScrollView.kt b/vector/src/main/java/im/vector/app/core/platform/MaxHeightScrollView.kt index 139ccc8c052..8d4c5d89503 100644 --- a/vector/src/main/java/im/vector/app/core/platform/MaxHeightScrollView.kt +++ b/vector/src/main/java/im/vector/app/core/platform/MaxHeightScrollView.kt @@ -24,8 +24,8 @@ import im.vector.app.R private const val DEFAULT_MAX_HEIGHT = 200 -class MaxHeightScrollView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) - : NestedScrollView(context, attrs, defStyle) { +class MaxHeightScrollView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) : + NestedScrollView(context, attrs, defStyle) { var maxHeight: Int = 0 set(value) { diff --git a/vector/src/main/java/im/vector/app/core/platform/SimpleFragmentActivity.kt b/vector/src/main/java/im/vector/app/core/platform/SimpleFragmentActivity.kt index 066bd165a95..7573bf2e8e9 100644 --- a/vector/src/main/java/im/vector/app/core/platform/SimpleFragmentActivity.kt +++ b/vector/src/main/java/im/vector/app/core/platform/SimpleFragmentActivity.kt @@ -21,7 +21,6 @@ import androidx.core.view.isVisible import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.hideKeyboard import im.vector.app.databinding.ActivityBinding - import org.matrix.android.sdk.api.session.Session /** diff --git a/vector/src/main/java/im/vector/app/core/platform/StateView.kt b/vector/src/main/java/im/vector/app/core/platform/StateView.kt index a935c56c511..b3d42dc38f6 100755 --- a/vector/src/main/java/im/vector/app/core/platform/StateView.kt +++ b/vector/src/main/java/im/vector/app/core/platform/StateView.kt @@ -26,8 +26,8 @@ import im.vector.app.R import im.vector.app.core.extensions.updateConstraintSet import im.vector.app.databinding.ViewStateBinding -class StateView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) - : FrameLayout(context, attrs, defStyle) { +class StateView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) : + FrameLayout(context, attrs, defStyle) { sealed class State { object Content : State() diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorEventViewModel.kt b/vector/src/main/java/im/vector/app/core/platform/VectorEventViewModel.kt index 2d499db5318..9ab46557a5d 100644 --- a/vector/src/main/java/im/vector/app/core/platform/VectorEventViewModel.kt +++ b/vector/src/main/java/im/vector/app/core/platform/VectorEventViewModel.kt @@ -25,5 +25,5 @@ interface VectorSharedAction /** * Parent class to handle navigation events, action events, or other any events */ -open class VectorSharedActionViewModel(private val store: MutableDataSource = PublishDataSource()) - : ViewModel(), MutableDataSource by store +open class VectorSharedActionViewModel(private val store: MutableDataSource = PublishDataSource()) : + ViewModel(), MutableDataSource by store diff --git a/vector/src/main/java/im/vector/app/core/platform/VectorViewModel.kt b/vector/src/main/java/im/vector/app/core/platform/VectorViewModel.kt index d6f43beaf75..bca462a92bc 100644 --- a/vector/src/main/java/im/vector/app/core/platform/VectorViewModel.kt +++ b/vector/src/main/java/im/vector/app/core/platform/VectorViewModel.kt @@ -27,8 +27,8 @@ import im.vector.app.core.utils.PublishDataSource import io.reactivex.Observable import io.reactivex.Single -abstract class VectorViewModel(initialState: S) - : BaseMvRxViewModel(initialState, false) { +abstract class VectorViewModel(initialState: S) : + BaseMvRxViewModel(initialState, false) { interface Factory { fun create(state: S): BaseMvRxViewModel diff --git a/vector/src/main/java/im/vector/app/core/pushers/PushersManager.kt b/vector/src/main/java/im/vector/app/core/pushers/PushersManager.kt index 5896122393f..a27765bf4f8 100644 --- a/vector/src/main/java/im/vector/app/core/pushers/PushersManager.kt +++ b/vector/src/main/java/im/vector/app/core/pushers/PushersManager.kt @@ -61,6 +61,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(pushKey: String) { val currentSession = activeSessionHolder.getSafeActiveSession() ?: return currentSession.removeHttpPusher(pushKey, stringProvider.getString(R.string.pusher_app_id)) diff --git a/vector/src/main/java/im/vector/app/core/services/CallService.kt b/vector/src/main/java/im/vector/app/core/services/CallService.kt index d8cf8cf6b8a..bda32e528b0 100644 --- a/vector/src/main/java/im/vector/app/core/services/CallService.kt +++ b/vector/src/main/java/im/vector/app/core/services/CallService.kt @@ -33,6 +33,7 @@ import im.vector.app.features.call.telecom.CallConnection import im.vector.app.features.call.webrtc.WebRtcCall import im.vector.app.features.call.webrtc.WebRtcCallManager import im.vector.app.features.call.webrtc.getOpponentAsMatrixItem +import im.vector.app.features.displayname.getBestName import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.notifications.NotificationUtils import im.vector.app.features.popup.IncomingCallAlert @@ -50,7 +51,7 @@ private val loggerTag = LoggerTag("CallService", LoggerTag.VOIP) class CallService : VectorService() { private val connections = mutableMapOf() - private val knownCalls = mutableSetOf() + private val knownCalls = mutableMapOf() private val connectedCallIds = mutableSetOf() private lateinit var notificationManager: NotificationManagerCompat @@ -190,7 +191,7 @@ class CallService : VectorService() { } else { notificationManager.notify(callId.hashCode(), notification) } - knownCalls.add(callInformation) + knownCalls[callId] = callInformation } private fun handleCallTerminated(intent: Intent) { @@ -198,20 +199,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 +246,7 @@ class CallService : VectorService() { } else { notificationManager.notify(callId.hashCode(), notification) } - knownCalls.add(callInformation) + knownCalls[callId] = callInformation } /** @@ -267,18 +270,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() + val notification = notificationUtils.buildCallEndedNotification(false) if (callId != null) { - notificationManager.cancel(callId.hashCode()) + startForeground(callId.hashCode(), notification) + } else { + startForeground(DEFAULT_NOTIFICATION_ID, notification) } - val notification = notificationUtils.buildCallEndedNotification(false) - startForeground(DEFAULT_NOTIFICATION_ID, notification) if (knownCalls.isEmpty()) { mediaSession?.isActive = false myStopSelf() @@ -371,7 +375,7 @@ class CallService : VectorService() { putExtra(EXTRA_END_CALL_REASON, endCallReason) putExtra(EXTRA_END_CALL_REJECTED, rejected) } - ContextCompat.startForegroundService(context, intent) + context.startService(intent) } } diff --git a/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGeneric.kt b/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGeneric.kt index e773993b21f..bd7a07c6409 100644 --- a/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGeneric.kt +++ b/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGeneric.kt @@ -26,7 +26,6 @@ import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.configureWith import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.app.databinding.BottomSheetGenericListBinding - import javax.inject.Inject /** diff --git a/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGenericController.kt b/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGenericController.kt index f0715e6bba6..6e92549809c 100644 --- a/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGenericController.kt +++ b/vector/src/main/java/im/vector/app/core/ui/bottomsheet/BottomSheetGenericController.kt @@ -20,8 +20,8 @@ import com.airbnb.epoxy.TypedEpoxyController /** * Epoxy controller for generic bottom sheet actions */ -abstract class BottomSheetGenericController - : TypedEpoxyController() { +abstract class BottomSheetGenericController : + TypedEpoxyController() { var listener: Listener? = null diff --git a/vector/src/main/java/im/vector/app/core/ui/views/BottomSheetActionButton.kt b/vector/src/main/java/im/vector/app/core/ui/views/BottomSheetActionButton.kt index 97194f2c03b..a3e8b3780cd 100644 --- a/vector/src/main/java/im/vector/app/core/ui/views/BottomSheetActionButton.kt +++ b/vector/src/main/java/im/vector/app/core/ui/views/BottomSheetActionButton.kt @@ -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.databinding.ViewBottomSheetActionButtonBinding import im.vector.app.features.themes.ThemeUtils @@ -80,7 +81,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 @@ -95,6 +96,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) @@ -109,6 +116,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) } } } diff --git a/vector/src/main/java/im/vector/app/core/ui/views/CurrentCallsViewPresenter.kt b/vector/src/main/java/im/vector/app/core/ui/views/CurrentCallsViewPresenter.kt index 5aee73ee694..13db5ddcb3b 100644 --- a/vector/src/main/java/im/vector/app/core/ui/views/CurrentCallsViewPresenter.kt +++ b/vector/src/main/java/im/vector/app/core/ui/views/CurrentCallsViewPresenter.kt @@ -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() ?: "") } diff --git a/vector/src/main/java/im/vector/app/core/ui/views/FailedMessagesWarningView.kt b/vector/src/main/java/im/vector/app/core/ui/views/FailedMessagesWarningView.kt index f9518552a34..755230b5bfc 100644 --- a/vector/src/main/java/im/vector/app/core/ui/views/FailedMessagesWarningView.kt +++ b/vector/src/main/java/im/vector/app/core/ui/views/FailedMessagesWarningView.kt @@ -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 - } } diff --git a/vector/src/main/java/im/vector/app/core/ui/views/JoinConferenceView.kt b/vector/src/main/java/im/vector/app/core/ui/views/JoinConferenceView.kt new file mode 100644 index 00000000000..fa1e2c14036 --- /dev/null +++ b/vector/src/main/java/im/vector/app/core/ui/views/JoinConferenceView.kt @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2020 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.ui.views + +import android.animation.Animator +import android.animation.ArgbEvaluator +import android.animation.ValueAnimator +import android.annotation.SuppressLint +import android.content.Context +import android.util.AttributeSet +import android.widget.FrameLayout +import androidx.core.content.ContextCompat +import im.vector.app.R +import im.vector.app.databinding.ViewJoinConferenceBinding + +class JoinConferenceView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : FrameLayout(context, attrs, defStyleAttr) { + + var views: ViewJoinConferenceBinding? = null + var onJoinClicked: (() -> Unit)? = null + var backgroundAnimator: Animator? = null + + init { + inflate(context, R.layout.view_join_conference, this) + } + + @SuppressLint("Recycle") + override fun onAttachedToWindow() { + super.onAttachedToWindow() + views = ViewJoinConferenceBinding.bind(this) + views?.joinConferenceButton?.setOnClickListener { onJoinClicked?.invoke() } + val colorFrom = ContextCompat.getColor(context, R.color.palette_element_green) + val colorTo = ContextCompat.getColor(context, R.color.join_conference_animated_color) + // Animate button color to highlight + backgroundAnimator = ValueAnimator.ofObject(ArgbEvaluator(), colorFrom, colorTo).apply { + repeatMode = ValueAnimator.REVERSE + repeatCount = ValueAnimator.INFINITE + duration = 500 + addUpdateListener { animator -> + val color = animator.animatedValue as Int + views?.joinConferenceButton?.setBackgroundColor(color) + } + } + backgroundAnimator?.start() + } + + override fun onDetachedFromWindow() { + super.onDetachedFromWindow() + views = null + backgroundAnimator?.cancel() + } +} diff --git a/vector/src/main/java/im/vector/app/core/ui/views/KeysBackupBanner.kt b/vector/src/main/java/im/vector/app/core/ui/views/KeysBackupBanner.kt index 153cac2ac7c..94c1ab65767 100755 --- a/vector/src/main/java/im/vector/app/core/ui/views/KeysBackupBanner.kt +++ b/vector/src/main/java/im/vector/app/core/ui/views/KeysBackupBanner.kt @@ -25,7 +25,6 @@ import androidx.core.view.isVisible import im.vector.app.R import im.vector.app.core.di.DefaultSharedPreferences import im.vector.app.databinding.ViewKeysBackupBannerBinding - import timber.log.Timber /** @@ -137,8 +136,8 @@ class KeysBackupBanner @JvmOverloads constructor( } private fun renderSetup(nbOfKeys: Int) { - if (nbOfKeys == 0 - || DefaultSharedPreferences.getInstance(context).getBoolean(BANNER_SETUP_DO_NOT_SHOW_AGAIN, false)) { + if (nbOfKeys == 0 || + DefaultSharedPreferences.getInstance(context).getBoolean(BANNER_SETUP_DO_NOT_SHOW_AGAIN, false)) { // Do not display the setup banner if there is no keys to backup, or if the user has already closed it isVisible = false } else { diff --git a/vector/src/main/java/im/vector/app/core/ui/views/NotificationAreaView.kt b/vector/src/main/java/im/vector/app/core/ui/views/NotificationAreaView.kt index 463d94b2883..94809d29816 100644 --- a/vector/src/main/java/im/vector/app/core/ui/views/NotificationAreaView.kt +++ b/vector/src/main/java/im/vector/app/core/ui/views/NotificationAreaView.kt @@ -30,7 +30,6 @@ import im.vector.app.core.extensions.exhaustive import im.vector.app.core.utils.DimensionConverter import im.vector.app.databinding.ViewNotificationAreaBinding import im.vector.app.features.themes.ThemeUtils - import me.gujun.android.span.span import me.saket.bettermovementmethod.BetterLinkMovementMethod import org.matrix.android.sdk.api.failure.MatrixError @@ -50,7 +49,7 @@ class NotificationAreaView @JvmOverloads constructor( var delegate: Delegate? = null private var state: State = State.Initial - private lateinit var views : ViewNotificationAreaBinding + private lateinit var views: ViewNotificationAreaBinding init { setupView() diff --git a/vector/src/main/java/im/vector/app/core/ui/views/PasswordStrengthBar.kt b/vector/src/main/java/im/vector/app/core/ui/views/PasswordStrengthBar.kt index 2f6c4b45cfe..a984707bf7c 100644 --- a/vector/src/main/java/im/vector/app/core/ui/views/PasswordStrengthBar.kt +++ b/vector/src/main/java/im/vector/app/core/ui/views/PasswordStrengthBar.kt @@ -35,8 +35,8 @@ import im.vector.app.databinding.ViewPasswordStrengthBarBinding class PasswordStrengthBar @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, - defStyleAttr: Int = 0) - : LinearLayout(context, attrs, defStyleAttr) { + defStyleAttr: Int = 0) : + LinearLayout(context, attrs, defStyleAttr) { private val views: ViewPasswordStrengthBarBinding diff --git a/vector/src/main/java/im/vector/app/core/ui/views/ReadReceiptsView.kt b/vector/src/main/java/im/vector/app/core/ui/views/ReadReceiptsView.kt index 02b351737e6..3a79e7e3283 100644 --- a/vector/src/main/java/im/vector/app/core/ui/views/ReadReceiptsView.kt +++ b/vector/src/main/java/im/vector/app/core/ui/views/ReadReceiptsView.kt @@ -37,7 +37,7 @@ class ReadReceiptsView @JvmOverloads constructor( defStyleAttr: Int = 0 ) : LinearLayout(context, attrs, defStyleAttr) { - private val views : ViewReadReceiptsBinding + private val views: ViewReadReceiptsBinding init { setupView() diff --git a/vector/src/main/java/im/vector/app/core/utils/Dialogs.kt b/vector/src/main/java/im/vector/app/core/utils/Dialogs.kt index 7806f2603d1..c73fa70388e 100644 --- a/vector/src/main/java/im/vector/app/core/utils/Dialogs.kt +++ b/vector/src/main/java/im/vector/app/core/utils/Dialogs.kt @@ -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() +} diff --git a/vector/src/main/java/im/vector/app/core/utils/PermissionsTools.kt b/vector/src/main/java/im/vector/app/core/utils/PermissionsTools.kt index 9f1497a40a6..ba396ed2525 100644 --- a/vector/src/main/java/im/vector/app/core/utils/PermissionsTools.kt +++ b/vector/src/main/java/im/vector/app/core/utils/PermissionsTools.kt @@ -52,8 +52,7 @@ private var permissionDialogDisplayed = false * So when the user does not grant the permission and check the box do not ask again, this boolean will be false. * Only useful if the first boolean is false */ -fun ComponentActivity.registerForPermissionsResult(lambda: (allGranted: Boolean, deniedPermanently: Boolean) -> Unit) - : ActivityResultLauncher> { +fun ComponentActivity.registerForPermissionsResult(lambda: (allGranted: Boolean, deniedPermanently: Boolean) -> Unit): ActivityResultLauncher> { return registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { result -> onPermissionResult(result, lambda) } diff --git a/vector/src/main/java/im/vector/app/core/utils/SnapHelperUtils.kt b/vector/src/main/java/im/vector/app/core/utils/SnapHelperUtils.kt index e039e0bde5d..070c953a3f5 100644 --- a/vector/src/main/java/im/vector/app/core/utils/SnapHelperUtils.kt +++ b/vector/src/main/java/im/vector/app/core/utils/SnapHelperUtils.kt @@ -59,8 +59,8 @@ class SnapOnScrollListener( } override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { - if (behavior == Behavior.NOTIFY_ON_SCROLL_STATE_IDLE - && newState == RecyclerView.SCROLL_STATE_IDLE) { + if (behavior == Behavior.NOTIFY_ON_SCROLL_STATE_IDLE && + newState == RecyclerView.SCROLL_STATE_IDLE) { maybeNotifySnapPositionChange(recyclerView) } } diff --git a/vector/src/main/java/im/vector/app/core/utils/SystemUtils.kt b/vector/src/main/java/im/vector/app/core/utils/SystemUtils.kt index b12c1b73694..966b38828ef 100644 --- a/vector/src/main/java/im/vector/app/core/utils/SystemUtils.kt +++ b/vector/src/main/java/im/vector/app/core/utils/SystemUtils.kt @@ -48,8 +48,8 @@ import im.vector.app.features.notifications.NotificationUtils */ fun isIgnoringBatteryOptimizations(context: Context): Boolean { // no issue before Android M, battery optimisations did not exist - return Build.VERSION.SDK_INT < Build.VERSION_CODES.M - || context.getSystemService()?.isIgnoringBatteryOptimizations(context.packageName) == true + return Build.VERSION.SDK_INT < Build.VERSION_CODES.M || + context.getSystemService()?.isIgnoringBatteryOptimizations(context.packageName) == true } fun isAirplaneModeOn(context: Context): Boolean { diff --git a/vector/src/main/java/im/vector/app/features/MainActivity.kt b/vector/src/main/java/im/vector/app/features/MainActivity.kt index 583aae260b6..e124dbdf6ed 100644 --- a/vector/src/main/java/im/vector/app/features/MainActivity.kt +++ b/vector/src/main/java/im/vector/app/features/MainActivity.kt @@ -45,10 +45,10 @@ import im.vector.app.features.signout.soft.SoftLogoutActivity import im.vector.app.features.signout.soft.SoftLogoutActivity2 import im.vector.app.features.themes.ActivityOtherThemes import im.vector.app.features.ui.UiStateRepository -import kotlinx.parcelize.Parcelize import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import kotlinx.parcelize.Parcelize import org.matrix.android.sdk.api.failure.GlobalError import timber.log.Timber import javax.inject.Inject @@ -220,20 +220,20 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity private fun startNextActivityAndFinish(ignoreClearCredentials: Boolean = false) { val intent = when { - args.clearCredentials - && !ignoreClearCredentials - && (!args.isUserLoggedOut || args.isAccountDeactivated) -> { + args.clearCredentials && + !ignoreClearCredentials && + (!args.isUserLoggedOut || args.isAccountDeactivated) -> { // User has explicitly asked to log out or deactivated his account navigator.openLogin(this, null) null } - args.isSoftLogout -> + args.isSoftLogout -> // The homeserver has invalidated the token, with a soft logout getSoftLogoutActivityIntent() - args.isUserLoggedOut -> + args.isUserLoggedOut -> // the homeserver has invalidated the token (password changed, device deleted, other security reasons) SignedOutActivity.newIntent(this) - sessionHolder.hasActiveSession() -> + sessionHolder.hasActiveSession() -> // We have a session. // Check it can be opened if (sessionHolder.getActiveSession().isOpenable) { @@ -242,7 +242,7 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity // The token is still invalid getSoftLogoutActivityIntent() } - else -> { + else -> { // First start, or no active session navigator.openLogin(this, null) null diff --git a/vector/src/main/java/im/vector/app/features/attachments/AttachmentTypeSelectorView.kt b/vector/src/main/java/im/vector/app/features/attachments/AttachmentTypeSelectorView.kt index eba5dadedac..35644e18430 100644 --- a/vector/src/main/java/im/vector/app/features/attachments/AttachmentTypeSelectorView.kt +++ b/vector/src/main/java/im/vector/app/features/attachments/AttachmentTypeSelectorView.kt @@ -53,8 +53,8 @@ private const val ANIMATION_DURATION = 250 */ class AttachmentTypeSelectorView(context: Context, inflater: LayoutInflater, - var callback: Callback?) - : PopupWindow(context) { + var callback: Callback?) : + PopupWindow(context) { interface Callback { fun onTypeSelected(type: Type) diff --git a/vector/src/main/java/im/vector/app/features/attachments/ContentAttachmentData.kt b/vector/src/main/java/im/vector/app/features/attachments/ContentAttachmentData.kt index 0502f2b0ad0..9805b6f7556 100644 --- a/vector/src/main/java/im/vector/app/features/attachments/ContentAttachmentData.kt +++ b/vector/src/main/java/im/vector/app/features/attachments/ContentAttachmentData.kt @@ -27,9 +27,9 @@ private val listOfPreviewableMimeTypes = listOf( fun ContentAttachmentData.isPreviewable(): Boolean { // Preview supports image and video - return (type == ContentAttachmentData.Type.IMAGE - && listOfPreviewableMimeTypes.contains(getSafeMimeType() ?: "")) - || type == ContentAttachmentData.Type.VIDEO + return (type == ContentAttachmentData.Type.IMAGE && + listOfPreviewableMimeTypes.contains(getSafeMimeType() ?: "")) || + type == ContentAttachmentData.Type.VIDEO } data class GroupedContentAttachmentData( diff --git a/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewViewModel.kt b/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewViewModel.kt index 28d617e613c..0a0e700ce93 100644 --- a/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/attachments/preview/AttachmentsPreviewViewModel.kt @@ -20,8 +20,8 @@ package im.vector.app.features.attachments.preview import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel -class AttachmentsPreviewViewModel(initialState: AttachmentsPreviewViewState) - : VectorViewModel(initialState) { +class AttachmentsPreviewViewModel(initialState: AttachmentsPreviewViewState) : + VectorViewModel(initialState) { override fun handle(action: AttachmentsPreviewAction) { when (action) { diff --git a/vector/src/main/java/im/vector/app/features/attachments/preview/Extensions.kt b/vector/src/main/java/im/vector/app/features/attachments/preview/Extensions.kt index 853f9f89974..672cde977dd 100644 --- a/vector/src/main/java/im/vector/app/features/attachments/preview/Extensions.kt +++ b/vector/src/main/java/im/vector/app/features/attachments/preview/Extensions.kt @@ -24,7 +24,7 @@ import org.matrix.android.sdk.api.util.MimeTypes.isMimeTypeImage * All images are editable, expect Gif */ fun ContentAttachmentData.isEditable(): Boolean { - return type == ContentAttachmentData.Type.IMAGE - && getSafeMimeType()?.isMimeTypeImage() == true - && getSafeMimeType() != MimeTypes.Gif + return type == ContentAttachmentData.Type.IMAGE && + getSafeMimeType()?.isMimeTypeImage() == true && + getSafeMimeType() != MimeTypes.Gif } diff --git a/vector/src/main/java/im/vector/app/features/autocomplete/AutocompleteMatrixItem.kt b/vector/src/main/java/im/vector/app/features/autocomplete/AutocompleteMatrixItem.kt index 3d70615abf1..dba2661927b 100644 --- a/vector/src/main/java/im/vector/app/features/autocomplete/AutocompleteMatrixItem.kt +++ b/vector/src/main/java/im/vector/app/features/autocomplete/AutocompleteMatrixItem.kt @@ -26,6 +26,7 @@ import im.vector.app.core.epoxy.VectorEpoxyHolder import im.vector.app.core.epoxy.VectorEpoxyModel import im.vector.app.core.epoxy.onClick import im.vector.app.core.extensions.setTextOrHide +import im.vector.app.features.displayname.getBestName import im.vector.app.features.home.AvatarRenderer import org.matrix.android.sdk.api.util.MatrixItem diff --git a/vector/src/main/java/im/vector/app/features/autocomplete/command/CommandAutocompletePolicy.kt b/vector/src/main/java/im/vector/app/features/autocomplete/command/CommandAutocompletePolicy.kt index 603ec64a7c4..08f61be0f8b 100644 --- a/vector/src/main/java/im/vector/app/features/autocomplete/command/CommandAutocompletePolicy.kt +++ b/vector/src/main/java/im/vector/app/features/autocomplete/command/CommandAutocompletePolicy.kt @@ -37,8 +37,8 @@ class CommandAutocompletePolicy @Inject constructor() : AutocompletePolicy { // Only if text which starts with '/' and without space override fun shouldShowPopup(text: Spannable?, cursorPos: Int): Boolean { - return enabled && text?.startsWith("/") == true - && !text.contains(" ") + return enabled && text?.startsWith("/") == true && + !text.contains(" ") } override fun shouldDismissPopup(text: Spannable?, cursorPos: Int): Boolean { diff --git a/vector/src/main/java/im/vector/app/features/autocomplete/emoji/AutocompleteEmojiPresenter.kt b/vector/src/main/java/im/vector/app/features/autocomplete/emoji/AutocompleteEmojiPresenter.kt index bf180746de7..4f272c7a24e 100644 --- a/vector/src/main/java/im/vector/app/features/autocomplete/emoji/AutocompleteEmojiPresenter.kt +++ b/vector/src/main/java/im/vector/app/features/autocomplete/emoji/AutocompleteEmojiPresenter.kt @@ -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(context), AutocompleteClickListener { + 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) } } diff --git a/vector/src/main/java/im/vector/app/features/autocomplete/member/AutocompleteMemberPresenter.kt b/vector/src/main/java/im/vector/app/features/autocomplete/member/AutocompleteMemberPresenter.kt index aa0c10e0a26..4976cb39b9e 100644 --- a/vector/src/main/java/im/vector/app/features/autocomplete/member/AutocompleteMemberPresenter.kt +++ b/vector/src/main/java/im/vector/app/features/autocomplete/member/AutocompleteMemberPresenter.kt @@ -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(context), AutocompleteClickListener { - private val room = session.getRoom(roomId)!! + private val room by lazy { session.getRoom(roomId)!! } init { controller.listener = this diff --git a/vector/src/main/java/im/vector/app/features/call/SharedKnownCallsViewModel.kt b/vector/src/main/java/im/vector/app/features/call/SharedKnownCallsViewModel.kt index fb5e48af98a..4b0ea412f38 100644 --- a/vector/src/main/java/im/vector/app/features/call/SharedKnownCallsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/call/SharedKnownCallsViewModel.kt @@ -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() } } diff --git a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt index f71dcc0635c..42e14efad32 100644 --- a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt +++ b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt @@ -55,6 +55,7 @@ import im.vector.app.features.call.dialpad.DialPadFragment import im.vector.app.features.call.utils.EglUtils import im.vector.app.features.call.webrtc.WebRtcCall import im.vector.app.features.call.webrtc.WebRtcCallManager +import im.vector.app.features.displayname.getBestName import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.room.detail.RoomDetailActivity import im.vector.app.features.home.room.detail.RoomDetailArgs diff --git a/vector/src/main/java/im/vector/app/features/call/VectorCallViewActions.kt b/vector/src/main/java/im/vector/app/features/call/VectorCallViewActions.kt index 1834c05e417..67aa7bede25 100644 --- a/vector/src/main/java/im/vector/app/features/call/VectorCallViewActions.kt +++ b/vector/src/main/java/im/vector/app/features/call/VectorCallViewActions.kt @@ -25,9 +25,9 @@ sealed class VectorCallViewActions : VectorViewModelAction { object DeclineCall : VectorCallViewActions() object ToggleMute : VectorCallViewActions() object ToggleVideo : VectorCallViewActions() - object ToggleHoldResume: VectorCallViewActions() + object ToggleHoldResume : VectorCallViewActions() data class ChangeAudioDevice(val device: CallAudioManager.Device) : VectorCallViewActions() - object OpenDialPad: VectorCallViewActions() + object OpenDialPad : VectorCallViewActions() data class SendDtmfDigit(val digit: String) : VectorCallViewActions() data class SwitchCall(val callArgs: CallArgs) : VectorCallViewActions() @@ -36,5 +36,5 @@ sealed class VectorCallViewActions : VectorViewModelAction { object ToggleCamera : VectorCallViewActions() object ToggleHDSD : VectorCallViewActions() object InitiateCallTransfer : VectorCallViewActions() - object TransferCall: VectorCallViewActions() + object TransferCall : VectorCallViewActions() } diff --git a/vector/src/main/java/im/vector/app/features/call/VectorCallViewEvents.kt b/vector/src/main/java/im/vector/app/features/call/VectorCallViewEvents.kt index 9f19429c00c..5a0a2f127c9 100644 --- a/vector/src/main/java/im/vector/app/features/call/VectorCallViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/call/VectorCallViewEvents.kt @@ -27,8 +27,8 @@ sealed class VectorCallViewEvents : VectorViewEvents { val available: Set, val current: CallAudioManager.Device ) : VectorCallViewEvents() - object ShowDialPad: VectorCallViewEvents() - object ShowCallTransferScreen: VectorCallViewEvents() + object ShowDialPad : VectorCallViewEvents() + object ShowCallTransferScreen : VectorCallViewEvents() // data class CallAnswered(val content: CallAnswerContent) : VectorCallViewEvents() // data class CallHangup(val content: CallHangupContent) : VectorCallViewEvents() // object CallAccepted : VectorCallViewEvents() diff --git a/vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt b/vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt index 63ba83bdbc5..90df595f8ff 100644 --- a/vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt @@ -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() } diff --git a/vector/src/main/java/im/vector/app/features/call/conference/JitsiBroadcastEvent.kt b/vector/src/main/java/im/vector/app/features/call/conference/ConferenceEvent.kt similarity index 63% rename from vector/src/main/java/im/vector/app/features/call/conference/JitsiBroadcastEvent.kt rename to vector/src/main/java/im/vector/app/features/call/conference/ConferenceEvent.kt index 00ad7c540e8..0a63ad69079 100644 --- a/vector/src/main/java/im/vector/app/features/call/conference/JitsiBroadcastEvent.kt +++ b/vector/src/main/java/im/vector/app/features/call/conference/ConferenceEvent.kt @@ -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) { + data class Terminated(override val data: Map) : ConferenceEvent(data) + data class WillJoin(override val data: Map) : ConferenceEvent(data) + data class Joined(override val data: Map) : 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) + } } } diff --git a/vector/src/main/java/im/vector/app/features/call/conference/JitsiActiveConferenceHolder.kt b/vector/src/main/java/im/vector/app/features/call/conference/JitsiActiveConferenceHolder.kt index 1a9fc5ea100..179956612d2 100644 --- a/vector/src/main/java/im/vector/app/features/call/conference/JitsiActiveConferenceHolder.kt +++ b/vector/src/main/java/im/vector/app/features/call/conference/JitsiActiveConferenceHolder.kt @@ -18,7 +18,6 @@ package im.vector.app.features.call.conference import android.content.Context import androidx.lifecycle.ProcessLifecycleOwner -import org.jitsi.meet.sdk.BroadcastEvent import org.matrix.android.sdk.api.extensions.orFalse import javax.inject.Inject import javax.inject.Singleton @@ -29,18 +28,18 @@ class JitsiActiveConferenceHolder @Inject constructor(context: Context) { private var activeConference: String? = null init { - ProcessLifecycleOwner.get().lifecycle.addObserver(JitsiBroadcastEventObserver(context, this::onBroadcastEvent)) + ProcessLifecycleOwner.get().lifecycle.addObserver(ConferenceEventObserver(context, this::onBroadcastEvent)) } fun isJoined(confId: String?): Boolean { return confId != null && activeConference?.endsWith(confId).orFalse() } - private fun onBroadcastEvent(broadcastEvent: BroadcastEvent) { - when (broadcastEvent.type) { - BroadcastEvent.Type.CONFERENCE_JOINED -> activeConference = broadcastEvent.extractConferenceUrl() - BroadcastEvent.Type.CONFERENCE_TERMINATED -> activeConference = null - else -> Unit + private fun onBroadcastEvent(conferenceEvent: ConferenceEvent) { + when (conferenceEvent) { + is ConferenceEvent.Joined -> activeConference = conferenceEvent.extractConferenceUrl() + is ConferenceEvent.Terminated -> activeConference = null + else -> Unit } } } diff --git a/vector/src/main/java/im/vector/app/features/call/conference/JitsiCallViewActions.kt b/vector/src/main/java/im/vector/app/features/call/conference/JitsiCallViewActions.kt index 830af7de010..7c9b9385f9d 100644 --- a/vector/src/main/java/im/vector/app/features/call/conference/JitsiCallViewActions.kt +++ b/vector/src/main/java/im/vector/app/features/call/conference/JitsiCallViewActions.kt @@ -25,5 +25,5 @@ sealed class JitsiCallViewActions : VectorViewModelAction { /** * The ViewModel will either ask the View to finish, or to join another conf. */ - object OnConferenceLeft: JitsiCallViewActions() + object OnConferenceLeft : JitsiCallViewActions() } diff --git a/vector/src/main/java/im/vector/app/features/call/conference/JitsiCallViewEvents.kt b/vector/src/main/java/im/vector/app/features/call/conference/JitsiCallViewEvents.kt index c8d570a73f4..25062ffe152 100644 --- a/vector/src/main/java/im/vector/app/features/call/conference/JitsiCallViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/call/conference/JitsiCallViewEvents.kt @@ -34,6 +34,6 @@ sealed class JitsiCallViewEvents : VectorViewEvents { ) : JitsiCallViewEvents() object LeaveConference : JitsiCallViewEvents() - object FailJoiningConference: JitsiCallViewEvents() + object FailJoiningConference : JitsiCallViewEvents() object Finish : JitsiCallViewEvents() } diff --git a/vector/src/main/java/im/vector/app/features/call/conference/JitsiCallViewModel.kt b/vector/src/main/java/im/vector/app/features/call/conference/JitsiCallViewModel.kt index 0fc85cb58cd..0b86114f6c0 100644 --- a/vector/src/main/java/im/vector/app/features/call/conference/JitsiCallViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/call/conference/JitsiCallViewModel.kt @@ -104,8 +104,8 @@ class JitsiCallViewModel @AssistedInject constructor( private fun handleSwitchTo(action: JitsiCallViewActions.SwitchTo) = withState { state -> // Check if it is the same conf - if (action.args.roomId != state.roomId - || action.args.widgetId != state.widgetId) { + if (action.args.roomId != state.roomId || + action.args.widgetId != state.widgetId) { if (action.withConfirmation) { // Ask confirmation to switch, but wait a bit for the Activity to quit the PiP mode viewModelScope.launch { diff --git a/vector/src/main/java/im/vector/app/features/call/conference/JitsiService.kt b/vector/src/main/java/im/vector/app/features/call/conference/JitsiService.kt index b8b6d83dd1c..b691296ba32 100644 --- a/vector/src/main/java/im/vector/app/features/call/conference/JitsiService.kt +++ b/vector/src/main/java/im/vector/app/features/call/conference/JitsiService.kt @@ -22,6 +22,7 @@ import im.vector.app.core.resources.StringProvider import im.vector.app.core.utils.ensureProtocol import im.vector.app.core.utils.toBase32String import im.vector.app.features.call.conference.jwt.JitsiJWTFactory +import im.vector.app.features.displayname.getBestName import im.vector.app.features.raw.wellknown.getElementWellknown import im.vector.app.features.settings.VectorLocale import im.vector.app.features.themes.ThemeProvider diff --git a/vector/src/main/java/im/vector/app/features/call/conference/RemoveJitsiWidgetView.kt b/vector/src/main/java/im/vector/app/features/call/conference/RemoveJitsiWidgetView.kt index 391471d2f26..fd7fc31e6da 100644 --- a/vector/src/main/java/im/vector/app/features/call/conference/RemoveJitsiWidgetView.kt +++ b/vector/src/main/java/im/vector/app/features/call/conference/RemoveJitsiWidgetView.kt @@ -87,10 +87,10 @@ import org.matrix.android.sdk.api.session.room.model.Membership fun render(roomDetailViewState: RoomDetailViewState) { val summary = roomDetailViewState.asyncRoomSummary() - val newState = if (summary?.membership != Membership.JOIN - || roomDetailViewState.isWebRTCCallOptionAvailable() - || !roomDetailViewState.isAllowedToManageWidgets - || roomDetailViewState.jitsiState.widgetId == null) { + val newState = if (summary?.membership != Membership.JOIN || + roomDetailViewState.isWebRTCCallOptionAvailable() || + !roomDetailViewState.isAllowedToManageWidgets || + roomDetailViewState.jitsiState.widgetId == null) { State.Unmount } else if (roomDetailViewState.jitsiState.deleteWidgetInProgress) { State.Progress diff --git a/vector/src/main/java/im/vector/app/features/call/conference/VectorJitsiActivity.kt b/vector/src/main/java/im/vector/app/features/call/conference/VectorJitsiActivity.kt index a7a6f99cfc3..e7fd541f3d3 100644 --- a/vector/src/main/java/im/vector/app/features/call/conference/VectorJitsiActivity.kt +++ b/vector/src/main/java/im/vector/app/features/call/conference/VectorJitsiActivity.kt @@ -39,13 +39,13 @@ import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.databinding.ActivityJitsiBinding import kotlinx.parcelize.Parcelize -import org.jitsi.meet.sdk.BroadcastEvent import org.jitsi.meet.sdk.JitsiMeet import org.jitsi.meet.sdk.JitsiMeetActivityDelegate import org.jitsi.meet.sdk.JitsiMeetActivityInterface import org.jitsi.meet.sdk.JitsiMeetConferenceOptions import org.jitsi.meet.sdk.JitsiMeetView import org.matrix.android.sdk.api.extensions.tryOrNull +import org.matrix.android.sdk.api.util.JsonDict import timber.log.Timber import java.net.URL import javax.inject.Inject @@ -87,7 +87,7 @@ class VectorJitsiActivity : VectorBaseActivity(), JitsiMee JitsiCallViewEvents.LeaveConference -> handleLeaveConference() }.exhaustive } - lifecycle.addObserver(JitsiBroadcastEventObserver(this, this::onBroadcastEvent)) + lifecycle.addObserver(ConferenceEventObserver(this, this::onBroadcastEvent)) } override fun onResume() { @@ -113,7 +113,7 @@ class VectorJitsiActivity : VectorBaseActivity(), JitsiMee jitsiMeetView?.dispose() // Fake emitting CONFERENCE_TERMINATED event when currentConf is not null (probably when closing the PiP screen). if (currentConf != null) { - JitsiBroadcastEmitter(this).emitConferenceEnded() + ConferenceEventEmitter(this).emitConferenceEnded() } JitsiMeetActivityDelegate.onHostDestroy(this) super.onDestroy() @@ -223,15 +223,15 @@ class VectorJitsiActivity : VectorBaseActivity(), JitsiMee JitsiMeetActivityDelegate.onRequestPermissionsResult(requestCode, permissions, grantResults) } - private fun onBroadcastEvent(event: BroadcastEvent) { - Timber.v("Broadcast received: ${event.type}") - when (event.type) { - BroadcastEvent.Type.CONFERENCE_TERMINATED -> onConferenceTerminated(event.data) - else -> Unit + private fun onBroadcastEvent(event: ConferenceEvent) { + Timber.v("Broadcast received: $event") + when (event) { + is ConferenceEvent.Terminated -> onConferenceTerminated(event.data) + else -> Unit } } - private fun onConferenceTerminated(data: Map) { + private fun onConferenceTerminated(data: JsonDict) { Timber.v("JitsiMeetViewListener.onConferenceTerminated()") // Do not finish if there is an error if (data["error"] == null) { diff --git a/vector/src/main/java/im/vector/app/features/call/dialpad/CallDialPadBottomSheet.kt b/vector/src/main/java/im/vector/app/features/call/dialpad/CallDialPadBottomSheet.kt index e7c86026982..8bf2ce47bd6 100644 --- a/vector/src/main/java/im/vector/app/features/call/dialpad/CallDialPadBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/call/dialpad/CallDialPadBottomSheet.kt @@ -87,7 +87,11 @@ class CallDialPadBottomSheet : VectorBaseBottomSheetDialogFragment(initialState) { + private val callManager: WebRtcCallManager) : + VectorViewModel(initialState) { @AssistedFactory interface Factory { diff --git a/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCall.kt b/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCall.kt index 2d39fda2e30..e632d00790e 100644 --- a/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCall.kt +++ b/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCall.kt @@ -636,8 +636,8 @@ class WebRtcCall( // We consider a call to be on hold only if *all* the tracks are on hold // (is this the right thing to do?) for (transceiver in peerConnection?.transceivers ?: emptyList()) { - val trackOnHold = transceiver.currentDirection == RtpTransceiver.RtpTransceiverDirection.INACTIVE - || transceiver.currentDirection == RtpTransceiver.RtpTransceiverDirection.RECV_ONLY + val trackOnHold = transceiver.currentDirection == RtpTransceiver.RtpTransceiverDirection.INACTIVE || + transceiver.currentDirection == RtpTransceiver.RtpTransceiverDirection.RECV_ONLY if (!trackOnHold) callOnHold = false } return callOnHold @@ -810,17 +810,19 @@ class WebRtcCall( } } - fun endCall(reason: EndCallReason = EndCallReason.USER_HANGUP) { + fun endCall(reason: EndCallReason = EndCallReason.USER_HANGUP, sendSignaling: Boolean = true) { sessionScope?.launch(dispatcher) { if (mxCall.state is CallState.Ended) { return@launch } val reject = mxCall.state is CallState.LocalRinging terminate(reason, reject) - if (reject) { - mxCall.reject() - } else { - mxCall.hangUp(reason) + if (sendSignaling) { + if (reject) { + mxCall.reject() + } else { + mxCall.hangUp(reason) + } } } } @@ -889,8 +891,8 @@ class WebRtcCall( val polite = !mxCall.isOutgoing // Here we follow the perfect negotiation logic from // https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API/Perfect_negotiation - val offerCollision = description.type == SdpType.OFFER - && (makingOffer || peerConnection.signalingState() != PeerConnection.SignalingState.STABLE) + val offerCollision = description.type == SdpType.OFFER && + (makingOffer || peerConnection.signalingState() != PeerConnection.SignalingState.STABLE) ignoreOffer = !polite && offerCollision if (ignoreOffer) { diff --git a/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCallManager.kt b/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCallManager.kt index 73a6c07d6af..9e620174f30 100644 --- a/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCallManager.kt +++ b/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCallManager.kt @@ -84,9 +84,10 @@ class WebRtcCallManager @Inject constructor( private val sessionScope: CoroutineScope? get() = currentSession?.coroutineScope - interface CurrentCallListener { - fun onCurrentCallChange(call: WebRtcCall?) {} - fun onAudioDevicesChange() {} + interface Listener { + fun onCallEnded(callId: String) = Unit + fun onCurrentCallChange(call: WebRtcCall?) = Unit + fun onAudioDevicesChange() = Unit } val supportedPSTNProtocol: String? @@ -106,13 +107,13 @@ class WebRtcCallManager @Inject constructor( protocolsChecker?.removeListener(listener) } - private val currentCallsListeners = CopyOnWriteArrayList() + private val currentCallsListeners = CopyOnWriteArrayList() - fun addCurrentCallListener(listener: CurrentCallListener) { + fun addListener(listener: Listener) { currentCallsListeners.add(listener) } - fun removeCurrentCallListener(listener: CurrentCallListener) { + fun removeListener(listener: Listener) { currentCallsListeners.remove(listener) } @@ -250,10 +251,13 @@ class WebRtcCallManager @Inject constructor( callsByRoomId[webRtcCall.signalingRoomId]?.remove(webRtcCall) callsByRoomId[webRtcCall.nativeRoomId]?.remove(webRtcCall) transferees.remove(callId) - if (getCurrentCall()?.callId == callId) { + if (currentCall.get()?.callId == callId) { val otherCall = getCalls().lastOrNull() currentCall.setAndNotify(otherCall) } + tryOrNull { + currentCallsListeners.forEach { it.onCallEnded(callId) } + } // There is no active calls if (getCurrentCall() == null) { Timber.tag(loggerTag.value).v("Dispose peerConnectionFactory as there is no need to keep one") @@ -424,7 +428,11 @@ class WebRtcCallManager @Inject constructor( override fun onCallManagedByOtherSession(callId: String) { Timber.tag(loggerTag.value).v("onCallManagedByOtherSession: $callId") - onCallEnded(callId, EndCallReason.ANSWERED_ELSEWHERE, false) + val call = callsByCallId[callId] + ?: return Unit.also { + Timber.tag(loggerTag.value).w("onCallManagedByOtherSession for non active call? $callId") + } + call.endCall(EndCallReason.ANSWERED_ELSEWHERE, sendSignaling = false) } override fun onCallAssertedIdentityReceived(callAssertedIdentityContent: CallAssertedIdentityContent) { diff --git a/vector/src/main/java/im/vector/app/features/command/Command.kt b/vector/src/main/java/im/vector/app/features/command/Command.kt index 3719618d313..33ccd08d222 100644 --- a/vector/src/main/java/im/vector/app/features/command/Command.kt +++ b/vector/src/main/java/im/vector/app/features/command/Command.kt @@ -28,14 +28,20 @@ enum class Command(val command: String, val parameters: String, @StringRes val d EMOTE("/me", "", R.string.command_description_emote, false), BAN_USER("/ban", " [reason]", R.string.command_description_ban_user, false), UNBAN_USER("/unban", " [reason]", R.string.command_description_unban_user, false), + IGNORE_USER("/ignore", " [reason]", R.string.command_description_ignore_user, false), + UNIGNORE_USER("/unignore", "", R.string.command_description_unignore_user, false), SET_USER_POWER_LEVEL("/op", " []", R.string.command_description_op_user, false), RESET_USER_POWER_LEVEL("/deop", "", R.string.command_description_deop_user, false), + ROOM_NAME("/roomname", "", R.string.command_description_room_name, false), INVITE("/invite", " [reason]", R.string.command_description_invite_user, false), JOIN_ROOM("/join", " [reason]", R.string.command_description_join_room, false), PART("/part", " [reason]", R.string.command_description_part_room, false), TOPIC("/topic", "", R.string.command_description_topic, false), KICK_USER("/kick", " [reason]", R.string.command_description_kick_user, false), CHANGE_DISPLAY_NAME("/nick", "", R.string.command_description_nick, false), + CHANGE_DISPLAY_NAME_FOR_ROOM("/myroomnick", "", R.string.command_description_nick_for_room, false), + ROOM_AVATAR("/roomavatar", "", R.string.command_description_room_avatar, true /* Since user has to know the mxc url */), + CHANGE_AVATAR_FOR_ROOM("/myroomavatar", "", R.string.command_description_avatar_for_room, true /* Since user has to know the mxc url */), MARKDOWN("/markdown", "", R.string.command_description_markdown, false), RAINBOW("/rainbow", "", R.string.command_description_rainbow, false), RAINBOW_EMOTE("/rainbowme", "", R.string.command_description_rainbow_emote, false), @@ -43,12 +49,14 @@ enum class Command(val command: String, val parameters: String, @StringRes val d SPOILER("/spoiler", "", R.string.command_description_spoiler, false), POLL("/poll", "Question | Option 1 | Option 2 ...", R.string.command_description_poll, false), SHRUG("/shrug", "", R.string.command_description_shrug, false), + LENNY("/lenny", "", R.string.command_description_lenny, false), PLAIN("/plain", "", R.string.command_description_plain, false), + WHOIS("/whois", "", R.string.command_description_whois, false), DISCARD_SESSION("/discardsession", "", R.string.command_description_discard_session, false), CONFETTI("/confetti", "", R.string.command_confetti, false), SNOWFALL("/snowfall", "", R.string.command_snow, false), - CREATE_SPACE("/createspace", " *", R.string.command_description_create_space, true), - ADD_TO_SPACE("/addToSpace", "spaceId", R.string.command_description_create_space, true), + CREATE_SPACE("/createspace", " *", R.string.command_description_create_space, true), + ADD_TO_SPACE("/addToSpace", "spaceId", R.string.command_description_add_to_space, true), JOIN_SPACE("/joinSpace", "spaceId", R.string.command_description_join_space, true), LEAVE_ROOM("/leave", "", R.string.command_description_leave_room, true), UPGRADE_ROOM("/upgraderoom", "newVersion", R.string.command_description_upgrade_room, true); diff --git a/vector/src/main/java/im/vector/app/features/command/CommandParser.kt b/vector/src/main/java/im/vector/app/features/command/CommandParser.kt index adba6e4a185..e570033d35a 100644 --- a/vector/src/main/java/im/vector/app/features/command/CommandParser.kt +++ b/vector/src/main/java/im/vector/app/features/command/CommandParser.kt @@ -20,6 +20,7 @@ import im.vector.app.core.extensions.isEmail import im.vector.app.core.extensions.isMsisdn import im.vector.app.features.home.room.detail.ChatEffect import org.matrix.android.sdk.api.MatrixPatterns +import org.matrix.android.sdk.api.MatrixUrls.isMxcUrl import org.matrix.android.sdk.api.session.identity.ThreePid import timber.log.Timber @@ -61,7 +62,7 @@ object CommandParser { } return when (val slashCommand = messageParts.first()) { - Command.PLAIN.command -> { + Command.PLAIN.command -> { val text = textMessage.substring(Command.PLAIN.command.length).trim() if (text.isNotEmpty()) { @@ -70,7 +71,7 @@ object CommandParser { ParsedCommand.ErrorSyntax(Command.PLAIN) } } - Command.CHANGE_DISPLAY_NAME.command -> { + Command.CHANGE_DISPLAY_NAME.command -> { val newDisplayName = textMessage.substring(Command.CHANGE_DISPLAY_NAME.command.length).trim() if (newDisplayName.isNotEmpty()) { @@ -79,7 +80,42 @@ object CommandParser { ParsedCommand.ErrorSyntax(Command.CHANGE_DISPLAY_NAME) } } - Command.TOPIC.command -> { + Command.CHANGE_DISPLAY_NAME_FOR_ROOM.command -> { + val newDisplayName = textMessage.substring(Command.CHANGE_DISPLAY_NAME_FOR_ROOM.command.length).trim() + + if (newDisplayName.isNotEmpty()) { + ParsedCommand.ChangeDisplayNameForRoom(newDisplayName) + } else { + ParsedCommand.ErrorSyntax(Command.CHANGE_DISPLAY_NAME_FOR_ROOM) + } + } + Command.ROOM_AVATAR.command -> { + if (messageParts.size == 2) { + val url = messageParts[1] + + if (url.isMxcUrl()) { + ParsedCommand.ChangeRoomAvatar(url) + } else { + ParsedCommand.ErrorSyntax(Command.ROOM_AVATAR) + } + } else { + ParsedCommand.ErrorSyntax(Command.ROOM_AVATAR) + } + } + Command.CHANGE_AVATAR_FOR_ROOM.command -> { + if (messageParts.size == 2) { + val url = messageParts[1] + + if (url.isMxcUrl()) { + ParsedCommand.ChangeAvatarForRoom(url) + } else { + ParsedCommand.ErrorSyntax(Command.CHANGE_AVATAR_FOR_ROOM) + } + } else { + ParsedCommand.ErrorSyntax(Command.CHANGE_AVATAR_FOR_ROOM) + } + } + Command.TOPIC.command -> { val newTopic = textMessage.substring(Command.TOPIC.command.length).trim() if (newTopic.isNotEmpty()) { @@ -88,22 +124,22 @@ object CommandParser { ParsedCommand.ErrorSyntax(Command.TOPIC) } } - Command.EMOTE.command -> { + Command.EMOTE.command -> { val message = textMessage.subSequence(Command.EMOTE.command.length, textMessage.length).trim() ParsedCommand.SendEmote(message) } - Command.RAINBOW.command -> { + Command.RAINBOW.command -> { val message = textMessage.subSequence(Command.RAINBOW.command.length, textMessage.length).trim() ParsedCommand.SendRainbow(message) } - Command.RAINBOW_EMOTE.command -> { + Command.RAINBOW_EMOTE.command -> { val message = textMessage.subSequence(Command.RAINBOW_EMOTE.command.length, textMessage.length).trim() ParsedCommand.SendRainbowEmote(message) } - Command.JOIN_ROOM.command -> { + Command.JOIN_ROOM.command -> { if (messageParts.size >= 2) { val roomAlias = messageParts[1] @@ -121,7 +157,7 @@ object CommandParser { ParsedCommand.ErrorSyntax(Command.JOIN_ROOM) } } - Command.PART.command -> { + Command.PART.command -> { if (messageParts.size >= 2) { val roomAlias = messageParts[1] @@ -139,7 +175,16 @@ object CommandParser { ParsedCommand.ErrorSyntax(Command.PART) } } - Command.INVITE.command -> { + Command.ROOM_NAME.command -> { + val newRoomName = textMessage.substring(Command.ROOM_NAME.command.length).trim() + + if (newRoomName.isNotEmpty()) { + ParsedCommand.ChangeRoomName(newRoomName) + } else { + ParsedCommand.ErrorSyntax(Command.ROOM_NAME) + } + } + Command.INVITE.command -> { if (messageParts.size >= 2) { val userId = messageParts[1] @@ -166,7 +211,7 @@ object CommandParser { ParsedCommand.ErrorSyntax(Command.INVITE) } } - Command.KICK_USER.command -> { + Command.KICK_USER.command -> { if (messageParts.size >= 2) { val userId = messageParts[1] @@ -184,7 +229,7 @@ object CommandParser { ParsedCommand.ErrorSyntax(Command.KICK_USER) } } - Command.BAN_USER.command -> { + Command.BAN_USER.command -> { if (messageParts.size >= 2) { val userId = messageParts[1] @@ -202,7 +247,7 @@ object CommandParser { ParsedCommand.ErrorSyntax(Command.BAN_USER) } } - Command.UNBAN_USER.command -> { + Command.UNBAN_USER.command -> { if (messageParts.size >= 2) { val userId = messageParts[1] @@ -220,7 +265,33 @@ object CommandParser { ParsedCommand.ErrorSyntax(Command.UNBAN_USER) } } - Command.SET_USER_POWER_LEVEL.command -> { + Command.IGNORE_USER.command -> { + if (messageParts.size == 2) { + val userId = messageParts[1] + + if (MatrixPatterns.isUserId(userId)) { + ParsedCommand.IgnoreUser(userId) + } else { + ParsedCommand.ErrorSyntax(Command.IGNORE_USER) + } + } else { + ParsedCommand.ErrorSyntax(Command.IGNORE_USER) + } + } + Command.UNIGNORE_USER.command -> { + if (messageParts.size == 2) { + val userId = messageParts[1] + + if (MatrixPatterns.isUserId(userId)) { + ParsedCommand.UnignoreUser(userId) + } else { + ParsedCommand.ErrorSyntax(Command.UNIGNORE_USER) + } + } else { + ParsedCommand.ErrorSyntax(Command.UNIGNORE_USER) + } + } + Command.SET_USER_POWER_LEVEL.command -> { if (messageParts.size == 3) { val userId = messageParts[1] if (MatrixPatterns.isUserId(userId)) { @@ -240,7 +311,7 @@ object CommandParser { ParsedCommand.ErrorSyntax(Command.SET_USER_POWER_LEVEL) } } - Command.RESET_USER_POWER_LEVEL.command -> { + Command.RESET_USER_POWER_LEVEL.command -> { if (messageParts.size == 2) { val userId = messageParts[1] @@ -253,7 +324,7 @@ object CommandParser { ParsedCommand.ErrorSyntax(Command.SET_USER_POWER_LEVEL) } } - Command.MARKDOWN.command -> { + Command.MARKDOWN.command -> { if (messageParts.size == 2) { when { "on".equals(messageParts[1], true) -> ParsedCommand.SetMarkdown(true) @@ -264,23 +335,28 @@ object CommandParser { ParsedCommand.ErrorSyntax(Command.MARKDOWN) } } - Command.CLEAR_SCALAR_TOKEN.command -> { + Command.CLEAR_SCALAR_TOKEN.command -> { if (messageParts.size == 1) { ParsedCommand.ClearScalarToken } else { ParsedCommand.ErrorSyntax(Command.CLEAR_SCALAR_TOKEN) } } - Command.SPOILER.command -> { + Command.SPOILER.command -> { val message = textMessage.substring(Command.SPOILER.command.length).trim() ParsedCommand.SendSpoiler(message) } - Command.SHRUG.command -> { + Command.SHRUG.command -> { val message = textMessage.substring(Command.SHRUG.command.length).trim() ParsedCommand.SendShrug(message) } - Command.POLL.command -> { + Command.LENNY.command -> { + val message = textMessage.substring(Command.LENNY.command.length).trim() + + ParsedCommand.SendLenny(message) + } + Command.POLL.command -> { val rawCommand = textMessage.substring(Command.POLL.command.length).trim() val split = rawCommand.split("|").map { it.trim() } if (split.size > 2) { @@ -289,18 +365,31 @@ object CommandParser { ParsedCommand.ErrorSyntax(Command.POLL) } } - Command.DISCARD_SESSION.command -> { + Command.DISCARD_SESSION.command -> { ParsedCommand.DiscardSession } - Command.CONFETTI.command -> { + Command.WHOIS.command -> { + if (messageParts.size == 2) { + val userId = messageParts[1] + + if (MatrixPatterns.isUserId(userId)) { + ParsedCommand.ShowUser(userId) + } else { + ParsedCommand.ErrorSyntax(Command.WHOIS) + } + } else { + ParsedCommand.ErrorSyntax(Command.WHOIS) + } + } + Command.CONFETTI.command -> { val message = textMessage.substring(Command.CONFETTI.command.length).trim() ParsedCommand.SendChatEffect(ChatEffect.CONFETTI, message) } - Command.SNOWFALL.command -> { + Command.SNOWFALL.command -> { val message = textMessage.substring(Command.SNOWFALL.command.length).trim() ParsedCommand.SendChatEffect(ChatEffect.SNOWFALL, message) } - Command.CREATE_SPACE.command -> { + Command.CREATE_SPACE.command -> { val rawCommand = textMessage.substring(Command.CREATE_SPACE.command.length).trim() val split = rawCommand.split(" ").map { it.trim() } if (split.isEmpty()) { @@ -312,25 +401,25 @@ object CommandParser { ) } } - Command.ADD_TO_SPACE.command -> { + Command.ADD_TO_SPACE.command -> { val rawCommand = textMessage.substring(Command.ADD_TO_SPACE.command.length).trim() ParsedCommand.AddToSpace( rawCommand ) } - Command.JOIN_SPACE.command -> { + Command.JOIN_SPACE.command -> { val spaceIdOrAlias = textMessage.substring(Command.JOIN_SPACE.command.length).trim() ParsedCommand.JoinSpace( spaceIdOrAlias ) } - Command.LEAVE_ROOM.command -> { + Command.LEAVE_ROOM.command -> { val spaceIdOrAlias = textMessage.substring(Command.LEAVE_ROOM.command.length).trim() ParsedCommand.LeaveRoom( spaceIdOrAlias ) } - Command.UPGRADE_ROOM.command -> { + Command.UPGRADE_ROOM.command -> { val newVersion = textMessage.substring(Command.UPGRADE_ROOM.command.length).trim() if (newVersion.isEmpty()) { ParsedCommand.ErrorSyntax(Command.UPGRADE_ROOM) @@ -338,7 +427,7 @@ object CommandParser { ParsedCommand.UpgradeRoom(newVersion) } } - else -> { + else -> { // Unknown command ParsedCommand.ErrorUnknownSlashCommand(slashCommand) } diff --git a/vector/src/main/java/im/vector/app/features/command/ParsedCommand.kt b/vector/src/main/java/im/vector/app/features/command/ParsedCommand.kt index 123f1d3a36c..bafb9153e6a 100644 --- a/vector/src/main/java/im/vector/app/features/command/ParsedCommand.kt +++ b/vector/src/main/java/im/vector/app/features/command/ParsedCommand.kt @@ -42,7 +42,10 @@ sealed class ParsedCommand { class SendRainbowEmote(val message: CharSequence) : ParsedCommand() class BanUser(val userId: String, val reason: String?) : ParsedCommand() class UnbanUser(val userId: String, val reason: String?) : ParsedCommand() + class IgnoreUser(val userId: String) : ParsedCommand() + class UnignoreUser(val userId: String) : ParsedCommand() class SetUserPowerLevel(val userId: String, val powerLevel: Int?) : ParsedCommand() + class ChangeRoomName(val name: String) : ParsedCommand() class Invite(val userId: String, val reason: String?) : ParsedCommand() class Invite3Pid(val threePid: ThreePid) : ParsedCommand() class JoinRoom(val roomAlias: String, val reason: String?) : ParsedCommand() @@ -50,12 +53,17 @@ sealed class ParsedCommand { class ChangeTopic(val topic: String) : ParsedCommand() class KickUser(val userId: String, val reason: String?) : ParsedCommand() class ChangeDisplayName(val displayName: String) : ParsedCommand() + class ChangeDisplayNameForRoom(val displayName: String) : ParsedCommand() + class ChangeRoomAvatar(val url: String) : ParsedCommand() + class ChangeAvatarForRoom(val url: String) : ParsedCommand() class SetMarkdown(val enable: Boolean) : ParsedCommand() object ClearScalarToken : ParsedCommand() class SendSpoiler(val message: String) : ParsedCommand() class SendShrug(val message: CharSequence) : ParsedCommand() + class SendLenny(val message: CharSequence) : ParsedCommand() class SendPoll(val question: String, val options: List) : ParsedCommand() object DiscardSession : ParsedCommand() + class ShowUser(val userId: String) : ParsedCommand() class SendChatEffect(val chatEffect: ChatEffect, val message: String) : ParsedCommand() class CreateSpace(val name: String, val invitees: List) : ParsedCommand() class AddToSpace(val spaceId: String) : ParsedCommand() diff --git a/vector/src/main/java/im/vector/app/features/configuration/VectorConfiguration.kt b/vector/src/main/java/im/vector/app/features/configuration/VectorConfiguration.kt index a2d190bd693..2c19e807723 100644 --- a/vector/src/main/java/im/vector/app/features/configuration/VectorConfiguration.kt +++ b/vector/src/main/java/im/vector/app/features/configuration/VectorConfiguration.kt @@ -104,8 +104,8 @@ class VectorConfiguration @Inject constructor(private val context: Context) { * @return the local status value */ fun getHash(): String { - return (VectorLocale.applicationLocale.toString() - + "_" + FontScale.getFontScaleValue(context).preferenceValue - + "_" + ThemeUtils.getApplicationTheme(context)) + return (VectorLocale.applicationLocale.toString() + + "_" + FontScale.getFontScaleValue(context).preferenceValue + + "_" + ThemeUtils.getApplicationTheme(context)) } } diff --git a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookFragment.kt b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookFragment.kt index 9291352821d..fc913ff8354 100644 --- a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookFragment.kt +++ b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookFragment.kt @@ -23,21 +23,19 @@ import android.view.ViewGroup import androidx.core.view.isVisible import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.withState -import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.jakewharton.rxbinding3.widget.checkedChanges import com.jakewharton.rxbinding3.widget.textChanges -import im.vector.app.R import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.configureWith import im.vector.app.core.extensions.hideKeyboard import im.vector.app.core.platform.VectorBaseFragment +import im.vector.app.core.utils.showIdentityServerConsentDialog import im.vector.app.databinding.FragmentContactsBookBinding import im.vector.app.features.userdirectory.PendingSelection import im.vector.app.features.userdirectory.UserListAction import im.vector.app.features.userdirectory.UserListSharedAction import im.vector.app.features.userdirectory.UserListSharedActionViewModel import im.vector.app.features.userdirectory.UserListViewModel - import org.matrix.android.sdk.api.session.identity.ThreePid import org.matrix.android.sdk.api.session.user.model.User import java.util.concurrent.TimeUnit @@ -76,14 +74,9 @@ class ContactsBookFragment @Inject constructor( private fun setupConsentView() { views.phoneBookSearchForMatrixContacts.setOnClickListener { withState(contactsBookViewModel) { state -> - MaterialAlertDialogBuilder(requireActivity()) - .setTitle(R.string.identity_server_consent_dialog_title) - .setMessage(getString(R.string.identity_server_consent_dialog_content, state.identityServerUrl ?: "")) - .setPositiveButton(R.string.yes) { _, _ -> - contactsBookViewModel.handle(ContactsBookAction.UserConsentGranted) - } - .setNegativeButton(R.string.no, null) - .show() + requireContext().showIdentityServerConsentDialog(state.identityServerUrl) { + contactsBookViewModel.handle(ContactsBookAction.UserConsentGranted) + } } } } diff --git a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewModel.kt b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewModel.kt index cfbdef8ffb6..e3d996c33b0 100644 --- a/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/contactsbook/ContactsBookViewModel.kt @@ -24,8 +24,8 @@ import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.core.contacts.ContactsDataSource import im.vector.app.core.contacts.MappedContact import im.vector.app.core.extensions.exhaustive @@ -41,8 +41,8 @@ import timber.log.Timber class ContactsBookViewModel @AssistedInject constructor(@Assisted initialState: ContactsBookViewState, private val contactsDataSource: ContactsDataSource, - private val session: Session) - : VectorViewModel(initialState) { + private val session: Session) : + VectorViewModel(initialState) { @AssistedFactory interface Factory { @@ -156,8 +156,8 @@ class ContactsBookViewModel @AssistedInject constructor(@Assisted val filteredMappedContacts = mappedContacts .filter { it.displayName.contains(state.searchTerm, true) } .filter { contactModel -> - !state.onlyBoundContacts - || contactModel.emails.any { it.matrixId != null } || contactModel.msisdns.any { it.matrixId != null } + !state.onlyBoundContacts || + contactModel.emails.any { it.matrixId != null } || contactModel.msisdns.any { it.matrixId != null } } setState { diff --git a/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomAction.kt b/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomAction.kt index ffc25210e96..da3425d3268 100644 --- a/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomAction.kt +++ b/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomAction.kt @@ -21,7 +21,6 @@ import im.vector.app.features.userdirectory.PendingSelection sealed class CreateDirectRoomAction : VectorViewModelAction { data class CreateRoomAndInviteSelectedUsers( - val selections: Set, - val existingDmRoomId: String? + val selections: Set ) : CreateDirectRoomAction() } diff --git a/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomActivity.kt b/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomActivity.kt index 68123d5e827..b91ecf0cfe2 100644 --- a/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomActivity.kt +++ b/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomActivity.kt @@ -138,10 +138,7 @@ class CreateDirectRoomActivity : SimpleFragmentActivity(), UserListViewModel.Fac private fun onMenuItemSelected(action: UserListSharedAction.OnMenuItemSelected) { if (action.itemId == R.id.action_create_direct_room) { - viewModel.handle(CreateDirectRoomAction.CreateRoomAndInviteSelectedUsers( - action.selections, - null - )) + viewModel.handle(CreateDirectRoomAction.CreateRoomAndInviteSelectedUsers(action.selections)) } } diff --git a/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomByQrCodeFragment.kt b/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomByQrCodeFragment.kt index 8da0147a430..5f089c64486 100644 --- a/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomByQrCodeFragment.kt +++ b/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomByQrCodeFragment.kt @@ -99,7 +99,6 @@ class CreateDirectRoomByQrCodeFragment @Inject constructor() : VectorBaseFragmen Toast.makeText(requireContext(), R.string.invalid_qr_code_uri, Toast.LENGTH_SHORT).show() requireActivity().finish() } else { - val existingDm = viewModel.session.getExistingDirectRoomWithUser(mxid) // The following assumes MXIDs are case insensitive if (mxid.equals(other = viewModel.session.myUserId, ignoreCase = true)) { Toast.makeText(requireContext(), R.string.cannot_dm_self, Toast.LENGTH_SHORT).show() @@ -109,7 +108,7 @@ class CreateDirectRoomByQrCodeFragment @Inject constructor() : VectorBaseFragmen val qrInvitee = if (viewModel.session.getUser(mxid) != null) viewModel.session.getUser(mxid)!! else User(mxid, null, null) viewModel.handle( - CreateDirectRoomAction.CreateRoomAndInviteSelectedUsers(setOf(PendingSelection.UserPendingSelection(qrInvitee)), existingDm) + CreateDirectRoomAction.CreateRoomAndInviteSelectedUsers(setOf(PendingSelection.UserPendingSelection(qrInvitee))) ) } } diff --git a/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomViewModel.kt b/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomViewModel.kt index 88e8d68155d..f192ea30195 100644 --- a/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/createdirect/CreateDirectRoomViewModel.kt @@ -24,8 +24,8 @@ import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.core.extensions.exhaustive import im.vector.app.core.mvrx.runCatchingToAsync import im.vector.app.core.platform.VectorViewModel @@ -41,8 +41,8 @@ import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams class CreateDirectRoomViewModel @AssistedInject constructor(@Assisted initialState: CreateDirectRoomViewState, private val rawService: RawService, - val session: Session) - : VectorViewModel(initialState) { + val session: Session) : + VectorViewModel(initialState) { @AssistedFactory interface Factory { @@ -71,10 +71,13 @@ class CreateDirectRoomViewModel @AssistedInject constructor(@Assisted * If users already have a DM room then navigate to it instead of creating a new room. */ private fun onSubmitInvitees(action: CreateDirectRoomAction.CreateRoomAndInviteSelectedUsers) { - if (action.existingDmRoomId != null) { + val existingRoomId = action.selections.singleOrNull()?.getMxId()?.let { userId -> + session.getExistingDirectRoomWithUser(userId) + } + if (existingRoomId != null) { // Do not create a new DM, just tell that the creation is successful by passing the existing roomId setState { - copy(createAndInviteState = Success(action.existingDmRoomId)) + copy(createAndInviteState = Success(existingRoomId)) } } else { // Create the DM diff --git a/vector/src/main/java/im/vector/app/features/crypto/keys/KeysExporter.kt b/vector/src/main/java/im/vector/app/features/crypto/keys/KeysExporter.kt index 2c66a14cb43..3db67df8e1b 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keys/KeysExporter.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keys/KeysExporter.kt @@ -18,24 +18,43 @@ package im.vector.app.features.crypto.keys import android.content.Context import android.net.Uri -import kotlinx.coroutines.Dispatchers +import im.vector.app.core.dispatchers.CoroutineDispatchers import kotlinx.coroutines.withContext import org.matrix.android.sdk.api.session.Session import javax.inject.Inject class KeysExporter @Inject constructor( private val session: Session, - private val context: Context + private val context: Context, + private val dispatchers: CoroutineDispatchers ) { /** * Export keys and write them to the provided uri */ suspend fun export(password: String, uri: Uri) { - return withContext(Dispatchers.IO) { + withContext(dispatchers.io) { val data = session.cryptoService().exportRoomKeys(password) context.contentResolver.openOutputStream(uri) ?.use { it.write(data) } - ?: throw IllegalStateException("Unable to open file for writting") + ?: throw IllegalStateException("Unable to open file for writing") + verifyExportedKeysOutputFileSize(uri, expectedSize = data.size.toLong()) + } + } + + private fun verifyExportedKeysOutputFileSize(uri: Uri, expectedSize: Long) { + val output = context.contentResolver.openFileDescriptor(uri, "r", null) + when { + output == null -> throw IllegalStateException("Exported file not found") + output.statSize != expectedSize -> { + throw UnexpectedExportKeysFileSizeException( + expectedFileSize = expectedSize, + actualFileSize = output.statSize + ) + } } } } + +class UnexpectedExportKeysFileSizeException(expectedFileSize: Long, actualFileSize: Long) : IllegalStateException( + "Exported Keys file has unexpected file size, got: $actualFileSize but expected: $expectedFileSize" +) diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyFragment.kt index 1f8cf4d3a05..435ca6e608f 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromKeyFragment.kt @@ -27,12 +27,11 @@ import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.utils.startImportTextFromFileIntent import im.vector.app.databinding.FragmentKeysBackupRestoreFromKeyBinding - import org.matrix.android.sdk.api.extensions.tryOrNull import javax.inject.Inject -class KeysBackupRestoreFromKeyFragment @Inject constructor() - : VectorBaseFragment() { +class KeysBackupRestoreFromKeyFragment @Inject constructor() : + VectorBaseFragment() { override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentKeysBackupRestoreFromKeyBinding { return FragmentKeysBackupRestoreFromKeyBinding.inflate(inflater, container, false) diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseFragment.kt index 96d8a257b78..c023e66e44f 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreFromPassphraseFragment.kt @@ -27,7 +27,6 @@ import androidx.core.widget.doOnTextChanged import im.vector.app.R import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.databinding.FragmentKeysBackupRestoreFromPassphraseBinding - import javax.inject.Inject class KeysBackupRestoreFromPassphraseFragment @Inject constructor() : VectorBaseFragment() { diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreSharedViewModel.kt index c323024bd96..34a333d5886 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreSharedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreSharedViewModel.kt @@ -88,26 +88,26 @@ class KeysBackupRestoreSharedViewModel @Inject constructor( override fun onStepProgress(step: StepProgressListener.Step) { when (step) { is StepProgressListener.Step.ComputingKey -> { - loadingEvent.postValue(WaitingViewData(stringProvider.getString(R.string.keys_backup_restoring_waiting_message) - + "\n" + stringProvider.getString(R.string.keys_backup_restoring_computing_key_waiting_message), + loadingEvent.postValue(WaitingViewData(stringProvider.getString(R.string.keys_backup_restoring_waiting_message) + + "\n" + stringProvider.getString(R.string.keys_backup_restoring_computing_key_waiting_message), step.progress, step.total)) } is StepProgressListener.Step.DownloadingKey -> { - loadingEvent.postValue(WaitingViewData(stringProvider.getString(R.string.keys_backup_restoring_waiting_message) - + "\n" + stringProvider.getString(R.string.keys_backup_restoring_downloading_backup_waiting_message), + loadingEvent.postValue(WaitingViewData(stringProvider.getString(R.string.keys_backup_restoring_waiting_message) + + "\n" + stringProvider.getString(R.string.keys_backup_restoring_downloading_backup_waiting_message), isIndeterminate = true)) } is StepProgressListener.Step.ImportingKey -> { Timber.d("backupKeys.ImportingKey.progress: ${step.progress}") // Progress 0 can take a while, display an indeterminate progress in this case if (step.progress == 0) { - loadingEvent.postValue(WaitingViewData(stringProvider.getString(R.string.keys_backup_restoring_waiting_message) - + "\n" + stringProvider.getString(R.string.keys_backup_restoring_importing_keys_waiting_message), + loadingEvent.postValue(WaitingViewData(stringProvider.getString(R.string.keys_backup_restoring_waiting_message) + + "\n" + stringProvider.getString(R.string.keys_backup_restoring_importing_keys_waiting_message), isIndeterminate = true)) } else { - loadingEvent.postValue(WaitingViewData(stringProvider.getString(R.string.keys_backup_restoring_waiting_message) - + "\n" + stringProvider.getString(R.string.keys_backup_restoring_importing_keys_waiting_message), + loadingEvent.postValue(WaitingViewData(stringProvider.getString(R.string.keys_backup_restoring_waiting_message) + + "\n" + stringProvider.getString(R.string.keys_backup_restoring_importing_keys_waiting_message), step.progress, step.total)) } diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreSuccessFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreSuccessFragment.kt index 8c1124bbbc8..66a7df14c3f 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreSuccessFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/restore/KeysBackupRestoreSuccessFragment.kt @@ -24,7 +24,6 @@ import im.vector.app.R import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.utils.LiveEvent import im.vector.app.databinding.FragmentKeysBackupRestoreSuccessBinding - import javax.inject.Inject class KeysBackupRestoreSuccessFragment @Inject constructor() : VectorBaseFragment() { diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/settings/KeysBackupSettingViewState.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/settings/KeysBackupSettingViewState.kt index 874e3765ca3..fa701f28101 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/settings/KeysBackupSettingViewState.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/settings/KeysBackupSettingViewState.kt @@ -26,5 +26,5 @@ import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersionR data class KeysBackupSettingViewState(val keysBackupVersionTrust: Async = Uninitialized, val keysBackupState: KeysBackupState? = null, val keysBackupVersion: KeysVersionResult? = null, - val deleteBackupRequest: Async = Uninitialized) - : MvRxState + val deleteBackupRequest: Async = Uninitialized) : + MvRxState diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/settings/KeysBackupSettingsFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/settings/KeysBackupSettingsFragment.kt index d2ee1bd6373..cf6bd99f234 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/settings/KeysBackupSettingsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/settings/KeysBackupSettingsFragment.kt @@ -29,11 +29,10 @@ import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.databinding.FragmentKeysBackupSettingsBinding import im.vector.app.features.crypto.keysbackup.restore.KeysBackupRestoreActivity import im.vector.app.features.crypto.keysbackup.setup.KeysBackupSetupActivity - import javax.inject.Inject -class KeysBackupSettingsFragment @Inject constructor(private val keysBackupSettingsRecyclerViewController: KeysBackupSettingsRecyclerViewController) - : VectorBaseFragment(), +class KeysBackupSettingsFragment @Inject constructor(private val keysBackupSettingsRecyclerViewController: KeysBackupSettingsRecyclerViewController) : + VectorBaseFragment(), KeysBackupSettingsRecyclerViewController.Listener { override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentKeysBackupSettingsBinding { diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/settings/KeysBackupSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/settings/KeysBackupSettingsViewModel.kt index cb8a6ce4e99..d47416dd385 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/settings/KeysBackupSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/settings/KeysBackupSettingsViewModel.kt @@ -23,8 +23,8 @@ import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel import org.matrix.android.sdk.api.MatrixCallback @@ -163,7 +163,7 @@ class KeysBackupSettingsViewModel @AssistedInject constructor(@Assisted initialS fun canExit(): Boolean { val currentBackupState = keysBackupService.state - return currentBackupState == KeysBackupState.Unknown - || currentBackupState == KeysBackupState.CheckingBackUpOnHomeserver + return currentBackupState == KeysBackupState.Unknown || + currentBackupState == KeysBackupState.CheckingBackUpOnHomeserver } } diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep1Fragment.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep1Fragment.kt index cf65a9942c7..08496c490f3 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep1Fragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep1Fragment.kt @@ -23,7 +23,6 @@ import android.view.ViewGroup import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.utils.LiveEvent import im.vector.app.databinding.FragmentKeysBackupSetupStep1Binding - import javax.inject.Inject class KeysBackupSetupStep1Fragment @Inject constructor() : VectorBaseFragment() { diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep3Fragment.kt b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep3Fragment.kt index 5739da11bb0..2befc4e79d0 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep3Fragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysbackup/setup/KeysBackupSetupStep3Fragment.kt @@ -36,7 +36,6 @@ import im.vector.app.core.utils.copyToClipboard import im.vector.app.core.utils.selectTxtFileToWrite import im.vector.app.core.utils.startSharePlainTextIntent import im.vector.app.databinding.FragmentKeysBackupSetupStep3Binding - import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext diff --git a/vector/src/main/java/im/vector/app/features/crypto/keysrequest/KeyRequestHandler.kt b/vector/src/main/java/im/vector/app/features/crypto/keysrequest/KeyRequestHandler.kt index a961c312923..85250a94ce7 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/keysrequest/KeyRequestHandler.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/keysrequest/KeyRequestHandler.kt @@ -226,9 +226,9 @@ class KeyRequestHandler @Inject constructor( val alertMgrUniqueKey = alertManagerId(userId, deviceId) alertsToRequests[alertMgrUniqueKey]?.removeAll { - it.deviceId == request.deviceId - && it.userId == request.userId - && it.requestId == request.requestId + it.deviceId == request.deviceId && + it.userId == request.userId && + it.requestId == request.requestId } if (alertsToRequests[alertMgrUniqueKey]?.isEmpty() == true) { popupAlertManager.cancelAlert(alertMgrUniqueKey) diff --git a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt index 9a5fc4ca066..e3258c40ecc 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecureStorageViewModel.kt @@ -27,8 +27,8 @@ import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.R import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel @@ -67,8 +67,8 @@ class SharedSecureStorageViewModel @AssistedInject constructor( @Assisted initialState: SharedSecureStorageViewState, @Assisted val args: SharedSecureStorageActivity.Args, private val stringProvider: StringProvider, - private val session: Session) - : VectorViewModel(initialState) { + private val session: Session) : + VectorViewModel(initialState) { @AssistedFactory interface Factory { @@ -76,7 +76,6 @@ class SharedSecureStorageViewModel @AssistedInject constructor( } init { - setState { copy(userId = session.myUserId) } @@ -167,10 +166,14 @@ class SharedSecureStorageViewModel @AssistedInject constructor( if (state.checkingSSSSAction is Loading) return@withState // ignore when (state.step) { SharedSecureStorageViewState.Step.EnterKey -> { - setState { - copy( - step = SharedSecureStorageViewState.Step.EnterPassphrase - ) + if (state.hasPassphrase) { + setState { + copy( + step = SharedSecureStorageViewState.Step.EnterPassphrase + ) + } + } else { + _viewEvents.post(SharedSecureStorageViewEvent.Dismiss) } } SharedSecureStorageViewState.Step.ResetAll -> { diff --git a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecuredStorageKeyFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecuredStorageKeyFragment.kt index feff326cfdd..1ba0198cb42 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecuredStorageKeyFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecuredStorageKeyFragment.kt @@ -31,7 +31,6 @@ import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.utils.startImportTextFromFileIntent import im.vector.app.databinding.FragmentSsssAccessFromKeyBinding import io.reactivex.android.schedulers.AndroidSchedulers - import org.matrix.android.sdk.api.extensions.tryOrNull import java.util.concurrent.TimeUnit import javax.inject.Inject diff --git a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecuredStorageResetAllFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecuredStorageResetAllFragment.kt index b6405777979..670e5c610a3 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecuredStorageResetAllFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/quads/SharedSecuredStorageResetAllFragment.kt @@ -27,11 +27,10 @@ import im.vector.app.core.extensions.setTextOrHide import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.databinding.FragmentSsssResetAllBinding import im.vector.app.features.roommemberprofile.devices.DeviceListBottomSheet - import javax.inject.Inject -class SharedSecuredStorageResetAllFragment @Inject constructor() - : VectorBaseFragment() { +class SharedSecuredStorageResetAllFragment @Inject constructor() : + VectorBaseFragment() { override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentSsssResetAllBinding { return FragmentSsssResetAllBinding.inflate(inflater, container, false) diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapActions.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapActions.kt index 869f0ed8ecf..363416b7de6 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapActions.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapActions.kt @@ -36,6 +36,7 @@ sealed class BootstrapActions : VectorViewModelAction { object DoInitializeGeneratedKey : BootstrapActions() data class UpdateCandidatePassphrase(val pass: String) : BootstrapActions() data class UpdateConfirmCandidatePassphrase(val pass: String) : BootstrapActions() + // data class ReAuth(val pass: String) : BootstrapActions() object RecoveryKeySaved : BootstrapActions() object Completed : BootstrapActions() @@ -47,7 +48,7 @@ sealed class BootstrapActions : VectorViewModelAction { data class DoMigrateWithPassphrase(val passphrase: String) : BootstrapActions() data class DoMigrateWithRecoveryKey(val recoveryKey: String) : BootstrapActions() - object SsoAuthDone: BootstrapActions() - data class PasswordAuthDone(val password: String): BootstrapActions() - object ReAuthCancelled: BootstrapActions() + object SsoAuthDone : BootstrapActions() + data class PasswordAuthDone(val password: String) : BootstrapActions() + object ReAuthCancelled : BootstrapActions() } diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapConclusionFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapConclusionFragment.kt index 6c5c2597553..555f2d7c1c9 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapConclusionFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapConclusionFragment.kt @@ -28,7 +28,6 @@ import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.resources.ColorProvider import im.vector.app.core.utils.colorizeMatchingText import im.vector.app.databinding.FragmentBootstrapConclusionBinding - import javax.inject.Inject class BootstrapConclusionFragment @Inject constructor( diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapConfirmPassphraseFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapConfirmPassphraseFragment.kt index 602f0bad91f..4aeb822bbdc 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapConfirmPassphraseFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapConfirmPassphraseFragment.kt @@ -31,12 +31,11 @@ import im.vector.app.core.extensions.hideKeyboard import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.databinding.FragmentBootstrapEnterPassphraseBinding import io.reactivex.android.schedulers.AndroidSchedulers - import java.util.concurrent.TimeUnit import javax.inject.Inject -class BootstrapConfirmPassphraseFragment @Inject constructor() - : VectorBaseFragment() { +class BootstrapConfirmPassphraseFragment @Inject constructor() : + VectorBaseFragment() { override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentBootstrapEnterPassphraseBinding { return FragmentBootstrapEnterPassphraseBinding.inflate(inflater, container, false) diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapCrossSigningTask.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapCrossSigningTask.kt index 70cda4bf79f..cc863346aa6 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapCrossSigningTask.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapCrossSigningTask.kt @@ -81,9 +81,9 @@ class BootstrapCrossSigningTask @Inject constructor( Timber.d("## BootstrapCrossSigningTask: mode:${params.setupMode} Starting...") // Ensure cross-signing is initialized. Due to migration it is maybe not always correctly initialized - val shouldSetCrossSigning = !crossSigningService.isCrossSigningInitialized() - || (params.setupMode == SetupMode.PASSPHRASE_AND_NEEDED_SECRETS_RESET && !crossSigningService.allPrivateKeysKnown()) - || (params.setupMode == SetupMode.HARD_RESET) + val shouldSetCrossSigning = !crossSigningService.isCrossSigningInitialized() || + (params.setupMode == SetupMode.PASSPHRASE_AND_NEEDED_SECRETS_RESET && !crossSigningService.allPrivateKeysKnown()) || + (params.setupMode == SetupMode.HARD_RESET) if (shouldSetCrossSigning) { Timber.d("## BootstrapCrossSigningTask: Cross signing not enabled, so initialize") params.progressListener?.onProgress( @@ -227,9 +227,9 @@ class BootstrapCrossSigningTask @Inject constructor( val knownMegolmSecret = session.cryptoService().keysBackupService().getKeyBackupRecoveryKeyInfo() val isMegolmBackupSecretKnown = knownMegolmSecret != null && knownMegolmSecret.version == serverVersion?.version - val shouldCreateKeyBackup = serverVersion == null - || (params.setupMode == SetupMode.PASSPHRASE_AND_NEEDED_SECRETS_RESET && !isMegolmBackupSecretKnown) - || (params.setupMode == SetupMode.HARD_RESET) + val shouldCreateKeyBackup = serverVersion == null || + (params.setupMode == SetupMode.PASSPHRASE_AND_NEEDED_SECRETS_RESET && !isMegolmBackupSecretKnown) || + (params.setupMode == SetupMode.HARD_RESET) if (shouldCreateKeyBackup) { // clear all existing backups while (serverVersion != null) { diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapEnterPassphraseFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapEnterPassphraseFragment.kt index 9ecc7719a54..f43ddb88882 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapEnterPassphraseFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapEnterPassphraseFragment.kt @@ -30,12 +30,11 @@ import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.databinding.FragmentBootstrapEnterPassphraseBinding import im.vector.app.features.settings.VectorLocale import io.reactivex.android.schedulers.AndroidSchedulers - import java.util.concurrent.TimeUnit import javax.inject.Inject -class BootstrapEnterPassphraseFragment @Inject constructor() - : VectorBaseFragment() { +class BootstrapEnterPassphraseFragment @Inject constructor() : + VectorBaseFragment() { override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentBootstrapEnterPassphraseBinding { return FragmentBootstrapEnterPassphraseBinding.inflate(inflater, container, false) diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapMigrateBackupFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapMigrateBackupFragment.kt index 23d3068e341..b40a194a15e 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapMigrateBackupFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapMigrateBackupFragment.kt @@ -40,7 +40,6 @@ import im.vector.app.core.utils.colorizeMatchingText import im.vector.app.core.utils.startImportTextFromFileIntent import im.vector.app.databinding.FragmentBootstrapMigrateBackupBinding import io.reactivex.android.schedulers.AndroidSchedulers - import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.internal.crypto.keysbackup.util.isValidRecoveryKey import java.util.concurrent.TimeUnit diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapReAuthFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapReAuthFragment.kt index 507050c2e8a..c46ccbf08e8 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapReAuthFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapReAuthFragment.kt @@ -27,7 +27,6 @@ import im.vector.app.core.extensions.setTextOrHide import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.resources.ColorProvider import im.vector.app.databinding.FragmentBootstrapReauthBinding - import javax.inject.Inject class BootstrapReAuthFragment @Inject constructor( diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSaveRecoveryKeyFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSaveRecoveryKeyFragment.kt index 0e11c352893..8a41c7ce4de 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSaveRecoveryKeyFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSaveRecoveryKeyFragment.kt @@ -34,7 +34,6 @@ import im.vector.app.core.resources.ColorProvider import im.vector.app.core.utils.startSharePlainTextIntent import im.vector.app.core.utils.toast import im.vector.app.databinding.FragmentBootstrapSaveKeyBinding - import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import javax.inject.Inject diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSetupRecoveryKeyFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSetupRecoveryKeyFragment.kt index 8676f1fb6bd..2765dfefd3b 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSetupRecoveryKeyFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSetupRecoveryKeyFragment.kt @@ -26,11 +26,10 @@ import com.airbnb.mvrx.withState import im.vector.app.R import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.databinding.FragmentBootstrapSetupRecoveryBinding - import javax.inject.Inject -class BootstrapSetupRecoveryKeyFragment @Inject constructor() - : VectorBaseFragment() { +class BootstrapSetupRecoveryKeyFragment @Inject constructor() : + VectorBaseFragment() { override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentBootstrapSetupRecoveryBinding { return FragmentBootstrapSetupRecoveryBinding.inflate(inflater, container, false) diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSharedViewModel.kt index 866a87d7f91..17a1359ea63 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSharedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapSharedViewModel.kt @@ -38,19 +38,19 @@ import im.vector.app.features.auth.ReAuthActivity import im.vector.app.features.login.ReAuthHelper import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.auth.UIABaseAuth import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor +import org.matrix.android.sdk.api.auth.UserPasswordAuth import org.matrix.android.sdk.api.auth.data.LoginFlowTypes +import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse +import org.matrix.android.sdk.api.auth.registration.nextUncompletedStage import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.securestorage.RawBytesKeySpec -import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse -import org.matrix.android.sdk.api.auth.registration.nextUncompletedStage import org.matrix.android.sdk.internal.crypto.crosssigning.fromBase64 import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysVersionResult import org.matrix.android.sdk.internal.crypto.keysbackup.util.extractCurveKeyFromRecoveryKey import org.matrix.android.sdk.internal.crypto.model.rest.DefaultBaseAuth -import org.matrix.android.sdk.api.auth.UIABaseAuth -import org.matrix.android.sdk.api.auth.UserPasswordAuth import org.matrix.android.sdk.internal.util.awaitCallback import java.io.OutputStream import kotlin.coroutines.Continuation @@ -437,9 +437,9 @@ class BootstrapSharedViewModel @AssistedInject constructor( } } is BootstrapResult.Failure -> { - if (bootstrapResult is BootstrapResult.GenericError - && bootstrapResult.failure is Failure.OtherServerError - && bootstrapResult.failure.httpCode == 401) { + if (bootstrapResult is BootstrapResult.GenericError && + bootstrapResult.failure is Failure.OtherServerError && + bootstrapResult.failure.httpCode == 401) { // Ignore this error } else { _viewEvents.post(BootstrapViewEvents.ModalError(bootstrapResult.error ?: stringProvider.getString(R.string.matrix_error))) diff --git a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapWaitingFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapWaitingFragment.kt index 7852ff172c6..8cf48a7c66f 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapWaitingFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/recover/BootstrapWaitingFragment.kt @@ -23,11 +23,10 @@ import com.airbnb.mvrx.parentFragmentViewModel import com.airbnb.mvrx.withState import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.databinding.FragmentBootstrapWaitingBinding - import javax.inject.Inject -class BootstrapWaitingFragment @Inject constructor() - : VectorBaseFragment() { +class BootstrapWaitingFragment @Inject constructor() : + VectorBaseFragment() { override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentBootstrapWaitingBinding { return FragmentBootstrapWaitingBinding.inflate(inflater, container, false) diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/IncomingVerificationRequestHandler.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/IncomingVerificationRequestHandler.kt index 0b93120b529..6c009d37866 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/IncomingVerificationRequestHandler.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/IncomingVerificationRequestHandler.kt @@ -18,6 +18,7 @@ package im.vector.app.features.crypto.verification import android.content.Context import im.vector.app.R import im.vector.app.core.platform.VectorBaseActivity +import im.vector.app.features.displayname.getBestName import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.room.detail.RoomDetailActivity import im.vector.app.features.home.room.detail.RoomDetailArgs @@ -63,7 +64,7 @@ class IncomingVerificationRequestHandler @Inject constructor( is VerificationTxState.OnStarted -> { // Add a notification for every incoming request val user = session?.getUser(tx.otherUserId) - val name = user?.getBestName() ?: tx.otherUserId + val name = user?.toMatrixItem()?.getBestName() ?: tx.otherUserId val alert = VerificationVectorAlert( uid, context.getString(R.string.sas_incoming_request_notif_title), diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheet.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheet.kt index 47033199f42..3b392780ef3 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheet.kt @@ -45,6 +45,7 @@ import im.vector.app.features.crypto.verification.emoji.VerificationEmojiCodeFra import im.vector.app.features.crypto.verification.qrconfirmation.VerificationQRWaitingFragment import im.vector.app.features.crypto.verification.qrconfirmation.VerificationQrScannedByOtherFragment import im.vector.app.features.crypto.verification.request.VerificationRequestFragment +import im.vector.app.features.displayname.getBestName import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.settings.VectorSettingsActivity import kotlinx.parcelize.Parcelize @@ -159,9 +160,9 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment if (state.isMe) { avatarRenderer.render(matrixItem, views.otherUserAvatarImageView) - if (state.sasTransactionState == VerificationTxState.Verified - || state.qrTransactionState == VerificationTxState.Verified - || state.verifiedFromPrivateKeys) { + if (state.sasTransactionState == VerificationTxState.Verified || + state.qrTransactionState == VerificationTxState.Verified || + state.verifiedFromPrivateKeys) { views.otherUserShield.render(RoomEncryptionTrustLevel.Trusted) } else { views.otherUserShield.render(RoomEncryptionTrustLevel.Warning) diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheetViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheetViewModel.kt index 8e214127154..4a29f883c06 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheetViewModel.kt @@ -26,8 +26,8 @@ import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.R import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel @@ -86,8 +86,8 @@ class VerificationBottomSheetViewModel @AssistedInject constructor( @Assisted val args: VerificationBottomSheet.VerificationArgs, private val session: Session, private val supportedVerificationMethodsProvider: SupportedVerificationMethodsProvider, - private val stringProvider: StringProvider) - : VectorViewModel(initialState), + private val stringProvider: StringProvider) : + VectorViewModel(initialState), VerificationService.Listener { init { @@ -172,9 +172,9 @@ class VerificationBottomSheetViewModel @AssistedInject constructor( } } else { // if the verification is already done you can't cancel anymore - if (state.pendingRequest.invoke()?.cancelConclusion != null - || state.sasTransactionState is VerificationTxState.TerminalTxState - || state.verifyingFrom4S) { + if (state.pendingRequest.invoke()?.cancelConclusion != null || + state.sasTransactionState is VerificationTxState.TerminalTxState || + state.verifyingFrom4S) { // you cannot cancel anymore } else { setState { @@ -537,9 +537,9 @@ class VerificationBottomSheetViewModel @AssistedInject constructor( } } - if (pr.localId == state.pendingLocalId - || pr.localId == state.pendingRequest.invoke()?.localId - || state.pendingRequest.invoke()?.transactionId == pr.transactionId) { + if (pr.localId == state.pendingLocalId || + pr.localId == state.pendingRequest.invoke()?.localId || + state.pendingRequest.invoke()?.transactionId == pr.transactionId) { setState { copy( transactionId = args.verificationId ?: pr.transactionId, diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/cancel/VerificationCancelFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/cancel/VerificationCancelFragment.kt index 497617f1add..62bab05e42a 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/cancel/VerificationCancelFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/cancel/VerificationCancelFragment.kt @@ -27,7 +27,6 @@ import im.vector.app.core.extensions.configureWith import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.databinding.BottomSheetVerificationChildFragmentBinding import im.vector.app.features.crypto.verification.VerificationBottomSheetViewModel - import javax.inject.Inject class VerificationCancelFragment @Inject constructor( diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/cancel/VerificationNotMeFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/cancel/VerificationNotMeFragment.kt index 46cb3a97a2d..635a56a6c1d 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/cancel/VerificationNotMeFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/cancel/VerificationNotMeFragment.kt @@ -27,7 +27,6 @@ import im.vector.app.core.extensions.configureWith import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.databinding.BottomSheetVerificationChildFragmentBinding import im.vector.app.features.crypto.verification.VerificationBottomSheetViewModel - import javax.inject.Inject class VerificationNotMeFragment @Inject constructor( diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/choose/VerificationChooseMethodFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/choose/VerificationChooseMethodFragment.kt index d3f24816a54..92faa7a0e7e 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/choose/VerificationChooseMethodFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/choose/VerificationChooseMethodFragment.kt @@ -36,7 +36,6 @@ import im.vector.app.databinding.BottomSheetVerificationChildFragmentBinding import im.vector.app.features.crypto.verification.VerificationAction import im.vector.app.features.crypto.verification.VerificationBottomSheetViewModel import im.vector.app.features.qrcode.QrCodeScannerActivity - import timber.log.Timber import javax.inject.Inject diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/choose/VerificationChooseMethodViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/choose/VerificationChooseMethodViewModel.kt index 7b9acd2f579..a6825348e9b 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/choose/VerificationChooseMethodViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/choose/VerificationChooseMethodViewModel.kt @@ -20,8 +20,8 @@ import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.core.di.HasScreenInjector import im.vector.app.core.platform.EmptyAction import im.vector.app.core.platform.EmptyViewEvents diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/conclusion/VerificationConclusionViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/conclusion/VerificationConclusionViewModel.kt index 50912af1c2f..2708aa0961c 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/conclusion/VerificationConclusionViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/conclusion/VerificationConclusionViewModel.kt @@ -35,8 +35,8 @@ enum class ConclusionState { CANCELLED } -class VerificationConclusionViewModel(initialState: VerificationConclusionViewState) - : VectorViewModel(initialState) { +class VerificationConclusionViewModel(initialState: VerificationConclusionViewState) : + VectorViewModel(initialState) { companion object : MvRxViewModelFactory { diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/emoji/VerificationEmojiCodeController.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/emoji/VerificationEmojiCodeController.kt index 5159a69090d..eab53ea9544 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/emoji/VerificationEmojiCodeController.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/emoji/VerificationEmojiCodeController.kt @@ -30,6 +30,7 @@ import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationD import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationEmojisItem import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationNoticeItem import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationWaitingItem +import im.vector.app.features.displayname.getBestName import javax.inject.Inject class VerificationEmojiCodeController @Inject constructor( diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/emoji/VerificationEmojiCodeFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/emoji/VerificationEmojiCodeFragment.kt index 97f521ee727..566307c05b9 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/emoji/VerificationEmojiCodeFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/emoji/VerificationEmojiCodeFragment.kt @@ -28,7 +28,6 @@ import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.databinding.BottomSheetVerificationChildFragmentBinding import im.vector.app.features.crypto.verification.VerificationAction import im.vector.app.features.crypto.verification.VerificationBottomSheetViewModel - import javax.inject.Inject class VerificationEmojiCodeFragment @Inject constructor( diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/emoji/VerificationEmojiCodeViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/emoji/VerificationEmojiCodeViewModel.kt index 44f0e752c8f..1795d120f36 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/emoji/VerificationEmojiCodeViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/emoji/VerificationEmojiCodeViewModel.kt @@ -25,8 +25,8 @@ import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.core.di.HasScreenInjector import im.vector.app.core.platform.EmptyAction import im.vector.app.core.platform.EmptyViewEvents diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/qrconfirmation/VerificationQrScannedByOtherController.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/qrconfirmation/VerificationQrScannedByOtherController.kt index 01862a5ab48..f3990842f9c 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/qrconfirmation/VerificationQrScannedByOtherController.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/qrconfirmation/VerificationQrScannedByOtherController.kt @@ -25,6 +25,7 @@ import im.vector.app.features.crypto.verification.VerificationBottomSheetViewSta import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationActionItem import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationBigImageItem import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationNoticeItem +import im.vector.app.features.displayname.getBestName import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel import javax.inject.Inject diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/qrconfirmation/VerificationQrScannedByOtherFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/qrconfirmation/VerificationQrScannedByOtherFragment.kt index 2ef015edbc0..9e77506e3bf 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/qrconfirmation/VerificationQrScannedByOtherFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/qrconfirmation/VerificationQrScannedByOtherFragment.kt @@ -27,7 +27,6 @@ import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.databinding.BottomSheetVerificationChildFragmentBinding import im.vector.app.features.crypto.verification.VerificationAction import im.vector.app.features.crypto.verification.VerificationBottomSheetViewModel - import javax.inject.Inject class VerificationQrScannedByOtherFragment @Inject constructor( diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/request/VerificationRequestController.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/request/VerificationRequestController.kt index 260bcf597e7..6440c0032b4 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/request/VerificationRequestController.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/request/VerificationRequestController.kt @@ -31,6 +31,7 @@ import im.vector.app.features.crypto.verification.epoxy.bottomSheetSelfWaitItem import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationActionItem import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationNoticeItem import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationWaitingItem +import im.vector.app.features.displayname.getBestName import javax.inject.Inject class VerificationRequestController @Inject constructor( diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/request/VerificationRequestFragment.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/request/VerificationRequestFragment.kt index 479dc7b3c7d..238249683f3 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/request/VerificationRequestFragment.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/request/VerificationRequestFragment.kt @@ -27,7 +27,6 @@ import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.databinding.BottomSheetVerificationChildFragmentBinding import im.vector.app.features.crypto.verification.VerificationAction import im.vector.app.features.crypto.verification.VerificationBottomSheetViewModel - import javax.inject.Inject class VerificationRequestFragment @Inject constructor( diff --git a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolActivity.kt b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolActivity.kt index 36ccef1fcab..bcdcf5f719c 100644 --- a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolActivity.kt +++ b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolActivity.kt @@ -194,8 +194,8 @@ class RoomDevToolActivity : SimpleFragmentActivity(), RoomDevToolViewModel.Facto state.displayMode is RoomDevToolViewState.Mode.StateEventDetail } R.id.menuItemSend -> { - state.displayMode is RoomDevToolViewState.Mode.EditEventContent - || state.displayMode is RoomDevToolViewState.Mode.SendEventForm + state.displayMode is RoomDevToolViewState.Mode.EditEventContent || + state.displayMode is RoomDevToolViewState.Mode.SendEventForm } else -> true } diff --git a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolEditFragment.kt b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolEditFragment.kt index 98fe19a7658..9af51f67b3f 100644 --- a/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolEditFragment.kt +++ b/vector/src/main/java/im/vector/app/features/devtools/RoomDevToolEditFragment.kt @@ -28,8 +28,8 @@ import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.databinding.FragmentDevtoolsEditorBinding import javax.inject.Inject -class RoomDevToolEditFragment @Inject constructor() - : VectorBaseFragment() { +class RoomDevToolEditFragment @Inject constructor() : + VectorBaseFragment() { private val sharedViewModel: RoomDevToolViewModel by activityViewModel() diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsController.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsController.kt index 1e8db2571d1..440307ae516 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsController.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsController.kt @@ -282,8 +282,8 @@ class DiscoverySettingsController @Inject constructor( val error = pidInfo.finalRequest.error // Deal with error 500 // Ref: https://github.com/matrix-org/sydent/issues/292 - if (error is Failure.ServerError - && error.httpCode == HttpsURLConnection.HTTP_INTERNAL_ERROR /* 500 */) { + if (error is Failure.ServerError && + error.httpCode == HttpsURLConnection.HTTP_INTERNAL_ERROR /* 500 */) { stringProvider.getString(R.string.settings_text_message_sent_wrong_code) } else { errorFormatter.toHumanReadable(error) diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt index 0b8674ec6f8..0e67b03f7cd 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsFragment.kt @@ -32,10 +32,10 @@ import im.vector.app.core.extensions.observeEvent import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.utils.ensureProtocol +import im.vector.app.core.utils.showIdentityServerConsentDialog import im.vector.app.databinding.FragmentGenericRecyclerBinding import im.vector.app.features.discovery.change.SetIdentityServerFragment import im.vector.app.features.settings.VectorSettingsActivity - import org.matrix.android.sdk.api.session.identity.SharedState import org.matrix.android.sdk.api.session.identity.ThreePid import org.matrix.android.sdk.api.session.terms.TermsService @@ -179,14 +179,9 @@ class DiscoverySettingsFragment @Inject constructor( override fun onTapUpdateUserConsent(newValue: Boolean) { if (newValue) { withState(viewModel) { state -> - MaterialAlertDialogBuilder(requireActivity()) - .setTitle(R.string.identity_server_consent_dialog_title) - .setMessage(getString(R.string.identity_server_consent_dialog_content, state.identityServer.invoke())) - .setPositiveButton(R.string.yes) { _, _ -> - viewModel.handle(DiscoverySettingsAction.UpdateUserConsent(true)) - } - .setNegativeButton(R.string.no, null) - .show() + requireContext().showIdentityServerConsentDialog(state.identityServer.invoke()) { + viewModel.handle(DiscoverySettingsAction.UpdateUserConsent(true)) + } } } else { viewModel.handle(DiscoverySettingsAction.UpdateUserConsent(false)) diff --git a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt index 11fd7965348..9d730d29cf4 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/DiscoverySettingsViewModel.kt @@ -25,8 +25,8 @@ import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import kotlinx.coroutines.launch @@ -39,8 +39,8 @@ import org.matrix.android.sdk.rx.rx class DiscoverySettingsViewModel @AssistedInject constructor( @Assisted initialState: DiscoverySettingsState, - private val session: Session) - : VectorViewModel(initialState) { + private val session: Session) : + VectorViewModel(initialState) { @AssistedFactory interface Factory { @@ -65,7 +65,7 @@ class DiscoverySettingsViewModel @AssistedInject constructor( setState { copy( identityServer = Success(identityServerUrl), - userConsent = false + userConsent = identityService.getUserConsent() ) } if (currentIS != identityServerUrl) retrieveBinding() diff --git a/vector/src/main/java/im/vector/app/features/discovery/change/SetIdentityServerFragment.kt b/vector/src/main/java/im/vector/app/features/discovery/change/SetIdentityServerFragment.kt index 36e77fb6a0b..dd4db36387f 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/change/SetIdentityServerFragment.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/change/SetIdentityServerFragment.kt @@ -37,7 +37,6 @@ import im.vector.app.core.resources.ColorProvider import im.vector.app.core.utils.colorizeMatchingText import im.vector.app.databinding.FragmentSetIdentityServerBinding import im.vector.app.features.discovery.DiscoverySharedViewModel - import org.matrix.android.sdk.api.session.terms.TermsService import javax.inject.Inject diff --git a/vector/src/main/java/im/vector/app/features/discovery/change/SetIdentityServerViewModel.kt b/vector/src/main/java/im/vector/app/features/discovery/change/SetIdentityServerViewModel.kt index 08632a2bd18..a63ec22110a 100644 --- a/vector/src/main/java/im/vector/app/features/discovery/change/SetIdentityServerViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/discovery/change/SetIdentityServerViewModel.kt @@ -20,8 +20,8 @@ import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.R import im.vector.app.core.di.HasScreenInjector import im.vector.app.core.extensions.exhaustive @@ -38,8 +38,8 @@ import java.net.UnknownHostException class SetIdentityServerViewModel @AssistedInject constructor( @Assisted initialState: SetIdentityServerState, private val mxSession: Session, - stringProvider: StringProvider) - : VectorViewModel(initialState) { + stringProvider: StringProvider) : + VectorViewModel(initialState) { @AssistedFactory interface Factory { diff --git a/vector/src/main/java/im/vector/app/features/displayname/Extension.kt b/vector/src/main/java/im/vector/app/features/displayname/Extension.kt new file mode 100644 index 00000000000..6ca1d484647 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/displayname/Extension.kt @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.displayname + +import org.matrix.android.sdk.api.util.MatrixItem + +fun MatrixItem.getBestName(): String { + // Note: this code is copied from [DisplayNameResolver] in the SDK + return if (this is MatrixItem.GroupItem || this is MatrixItem.RoomAliasItem) { + // Best name is the id, and we keep the displayName of the room for the case we need the first letter + id + } else { + displayName + ?.takeIf { it.isNotBlank() } + ?: VectorMatrixItemDisplayNameFallbackProvider.getDefaultName(this) + } +} diff --git a/vector/src/main/java/im/vector/app/features/displayname/VectorMatrixItemDisplayNameFallbackProvider.kt b/vector/src/main/java/im/vector/app/features/displayname/VectorMatrixItemDisplayNameFallbackProvider.kt new file mode 100644 index 00000000000..23b55335b80 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/displayname/VectorMatrixItemDisplayNameFallbackProvider.kt @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.displayname + +import org.matrix.android.sdk.api.MatrixItemDisplayNameFallbackProvider +import org.matrix.android.sdk.api.util.MatrixItem + +// Used to provide the fallback to the MatrixSDK, in the MatrixConfiguration +object VectorMatrixItemDisplayNameFallbackProvider : MatrixItemDisplayNameFallbackProvider { + override fun getDefaultName(matrixItem: MatrixItem): String { + // Can customize something from the id if necessary here + return matrixItem.id + } +} diff --git a/vector/src/main/java/im/vector/app/features/form/FormEditTextItem.kt b/vector/src/main/java/im/vector/app/features/form/FormEditTextItem.kt index 2b2fddd0c99..cda1623c88e 100644 --- a/vector/src/main/java/im/vector/app/features/form/FormEditTextItem.kt +++ b/vector/src/main/java/im/vector/app/features/form/FormEditTextItem.kt @@ -17,6 +17,7 @@ package im.vector.app.features.form import android.text.Editable +import android.text.InputFilter import android.view.View import android.view.inputmethod.EditorInfo import android.widget.TextView @@ -77,6 +78,9 @@ abstract class FormEditTextItem : VectorEpoxyModel() { @EpoxyAttribute var suffixText: String? = null + @EpoxyAttribute + var maxLength: Int? = null + private val onTextChangeListener = object : SimpleTextWatcher() { override fun afterTextChanged(s: Editable) { onTextChange?.invoke(s.toString()) @@ -109,6 +113,15 @@ abstract class FormEditTextItem : VectorEpoxyModel() { holder.textInputEditText.addTextChangedListenerOnce(onTextChangeListener) holder.textInputEditText.setOnEditorActionListener(editorActionListener) holder.textInputEditText.onFocusChangeListener = onFocusChangedListener + + if (maxLength != null) { + holder.textInputEditText.filters = arrayOf(InputFilter.LengthFilter(maxLength!!)) + holder.textInputLayout.isCounterEnabled = true + holder.textInputLayout.counterMaxLength = maxLength!! + } else { + holder.textInputEditText.filters = arrayOf() + holder.textInputLayout.isCounterEnabled = false + } } override fun shouldSaveViewState(): Boolean { diff --git a/vector/src/main/java/im/vector/app/features/grouplist/HomeSpaceSummaryItem.kt b/vector/src/main/java/im/vector/app/features/grouplist/HomeSpaceSummaryItem.kt index bbced2e8e1b..bd414c0c3f3 100644 --- a/vector/src/main/java/im/vector/app/features/grouplist/HomeSpaceSummaryItem.kt +++ b/vector/src/main/java/im/vector/app/features/grouplist/HomeSpaceSummaryItem.kt @@ -39,7 +39,7 @@ abstract class HomeSpaceSummaryItem : VectorEpoxyModel) { val placeholder = getPlaceholderDrawable(matrixItem) glideRequests.loadResolvedUrl(matrixItem.avatarUrl) - .apply { + .let { when (matrixItem) { is MatrixItem.SpaceItem -> { - transform(MultiTransformation(CenterCrop(), RoundedCorners(dimensionConverter.dpToPx(8)))) + it.transform(MultiTransformation(CenterCrop(), RoundedCorners(dimensionConverter.dpToPx(8)))) } else -> { - apply(RequestOptions.circleCropTransform()) + it.apply(RequestOptions.circleCropTransform()) } } } @@ -157,13 +157,13 @@ class AvatarRenderer @Inject constructor(private val activeSessionHolder: Active fun shortcutDrawable(glideRequests: GlideRequests, matrixItem: MatrixItem, iconSize: Int): Bitmap { return glideRequests .asBitmap() - .apply { + .let { val resolvedUrl = resolvedUrl(matrixItem.avatarUrl) if (resolvedUrl != null) { - load(resolvedUrl) + it.load(resolvedUrl) } else { val avatarColor = matrixItemColorProvider.getColor(matrixItem) - load(TextDrawable.builder() + it.load(TextDrawable.builder() .beginConfig() .bold() .endConfig() diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt index a302276f45f..3d264104c3f 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivity.kt @@ -27,6 +27,8 @@ import android.view.MenuItem import androidx.core.view.GravityCompat import androidx.core.view.isVisible import androidx.drawerlayout.widget.DrawerLayout +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentManager import com.airbnb.mvrx.MvRx import com.airbnb.mvrx.viewModel import com.google.android.material.appbar.MaterialToolbar @@ -51,6 +53,9 @@ import im.vector.app.features.navigation.Navigator import im.vector.app.features.notifications.NotificationDrawerManager import im.vector.app.features.permalink.NavigationInterceptor import im.vector.app.features.permalink.PermalinkHandler +import im.vector.app.features.permalink.PermalinkHandler.Companion.MATRIX_TO_CUSTOM_SCHEME_URL_BASE +import im.vector.app.features.permalink.PermalinkHandler.Companion.ROOM_LINK_PREFIX +import im.vector.app.features.permalink.PermalinkHandler.Companion.USER_LINK_PREFIX import im.vector.app.features.popup.DefaultVectorAlert import im.vector.app.features.popup.PopupAlertManager import im.vector.app.features.popup.VerificationVectorAlert @@ -70,7 +75,7 @@ import im.vector.app.features.workers.signout.ServerBackupStatusViewState import im.vector.app.push.fcm.FcmHelper import io.reactivex.android.schedulers.AndroidSchedulers import kotlinx.parcelize.Parcelize -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.permalinks.PermalinkService import org.matrix.android.sdk.api.util.MatrixItem import org.matrix.android.sdk.internal.session.sync.InitialSyncStrategy @@ -81,7 +86,8 @@ import javax.inject.Inject @Parcelize data class HomeActivityArgs( val clearNotification: Boolean, - val accountCreation: Boolean + val accountCreation: Boolean, + val inviteNotificationRoomId: String? = null ) : Parcelable class HomeActivity : @@ -92,7 +98,8 @@ class HomeActivity : UnreadMessagesSharedViewModel.Factory, PromoteRestrictedViewModel.Factory, NavigationInterceptor, - SpaceInviteBottomSheet.InteractionListener { + SpaceInviteBottomSheet.InteractionListener, + MatrixToBottomSheet.InteractionListener { private lateinit var sharedActionViewModel: HomeSharedActionViewModel @@ -141,6 +148,22 @@ class HomeActivity : } } + private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() { + override fun onFragmentResumed(fm: FragmentManager, f: Fragment) { + if (f is MatrixToBottomSheet) { + f.interactionListener = this@HomeActivity + } + super.onFragmentResumed(fm, f) + } + + override fun onFragmentPaused(fm: FragmentManager, f: Fragment) { + if (f is MatrixToBottomSheet) { + f.interactionListener = null + } + super.onFragmentPaused(fm, f) + } + } + private val drawerListener = object : DrawerLayout.SimpleDrawerListener() { override fun onDrawerStateChanged(newState: Int) { hideKeyboard() @@ -169,6 +192,7 @@ class HomeActivity : override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, false) FcmHelper.ensureFcmTokenIsRetrieved(this, pushManager, vectorPreferences.areNotificationEnabledForDevice()) sharedActionViewModel = viewModelProvider.get(HomeSharedActionViewModel::class.java) views.drawerLayout.addDrawerListener(drawerListener) @@ -229,6 +253,11 @@ class HomeActivity : if (args?.clearNotification == true) { notificationDrawerManager.clearAllEvents() } + if (args?.inviteNotificationRoomId != null) { + activeSessionHolder.getSafeActiveSession()?.permalinkService()?.createPermalink(args.inviteNotificationRoomId)?.let { + navigator.openMatrixToBottomSheet(this, it) + } + } homeActivityViewModel.observeViewEvents { when (it) { @@ -245,8 +274,8 @@ class HomeActivity : if (!vectorPreferences.didPromoteNewRestrictedFeature()) { promoteRestrictedViewModel.subscribe(this) { - if (it.activeSpaceSummary != null && !it.activeSpaceSummary.isPublic - && it.activeSpaceSummary.otherMemberIds.isNotEmpty()) { + if (it.activeSpaceSummary != null && !it.activeSpaceSummary.isPublic && + it.activeSpaceSummary.otherMemberIds.isNotEmpty()) { // It's a private space with some members show this once if (it.canUserManageSpace && !popupAlertManager.hasAlertsToShow()) { if (!vectorPreferences.didPromoteNewRestrictedFeature()) { @@ -266,20 +295,19 @@ class HomeActivity : private fun handleIntent(intent: Intent?) { intent?.dataString?.let { deepLink -> val resolvedLink = when { - deepLink.startsWith(PermalinkService.MATRIX_TO_URL_BASE) -> deepLink - deepLink.startsWith(MATRIX_TO_CUSTOM_SCHEME_URL_BASE) -> { - // This is a bit ugly, but for now just convert to matrix.to link for compatibility - when { + // Element custom scheme is not handled by the sdk, convert it to matrix.to link for compatibility + deepLink.startsWith(MATRIX_TO_CUSTOM_SCHEME_URL_BASE) -> { + val let = when { deepLink.startsWith(USER_LINK_PREFIX) -> deepLink.substring(USER_LINK_PREFIX.length) deepLink.startsWith(ROOM_LINK_PREFIX) -> deepLink.substring(ROOM_LINK_PREFIX.length) else -> null - }?.let { - activeSessionHolder.getSafeActiveSession()?.permalinkService()?.createPermalink(it) + }?.let { permalinkId -> + activeSessionHolder.getSafeActiveSession()?.permalinkService()?.createPermalink(permalinkId) } + let } - else -> return@let + else -> deepLink } - permalinkHandler.launch( context = this, deepLink = resolvedLink, @@ -290,9 +318,11 @@ class HomeActivity : .observeOn(AndroidSchedulers.mainThread()) .subscribe { isHandled -> if (!isHandled) { + val isMatrixToLink = deepLink.startsWith(PermalinkService.MATRIX_TO_URL_BASE) || + deepLink.startsWith(MATRIX_TO_CUSTOM_SCHEME_URL_BASE) MaterialAlertDialogBuilder(this) .setTitle(R.string.dialog_title_error) - .setMessage(R.string.permalink_malformed) + .setMessage(if (isMatrixToLink) R.string.permalink_malformed else R.string.universal_link_malformed) .setPositiveButton(R.string.ok, null) .show() } @@ -302,11 +332,8 @@ class HomeActivity : } private fun renderState(state: HomeActivityViewState) { - when (val status = state.initialSyncProgressServiceStatus) { - is InitialSyncProgressService.Status.Idle -> { - views.waitingView.root.isVisible = false - } - is InitialSyncProgressService.Status.Progressing -> { + when (val status = state.syncStatusServiceStatus) { + is SyncStatusService.Status.Progressing -> { val initSyncStepStr = initSyncStepFormatter.format(status.initSyncStep) Timber.v("$initSyncStepStr ${status.percentProgress}") views.waitingView.root.setOnClickListener { @@ -324,6 +351,10 @@ class HomeActivity : } views.waitingView.root.isVisible = true } + else -> { + // Idle or Incremental sync status + views.waitingView.root.isVisible = false + } }.exhaustive } @@ -422,14 +453,23 @@ class HomeActivity : override fun onNewIntent(intent: Intent?) { super.onNewIntent(intent) - if (intent?.getParcelableExtra(MvRx.KEY_ARG)?.clearNotification == true) { + val parcelableExtra = intent?.getParcelableExtra(MvRx.KEY_ARG) + if (parcelableExtra?.clearNotification == true) { notificationDrawerManager.clearAllEvents() } + if (parcelableExtra?.inviteNotificationRoomId != null) { + activeSessionHolder.getSafeActiveSession() + ?.permalinkService() + ?.createPermalink(parcelableExtra.inviteNotificationRoomId)?.let { + navigator.openMatrixToBottomSheet(this, it) + } + } handleIntent(intent) } override fun onDestroy() { views.drawerLayout.removeDrawerListener(drawerListener) + supportFragmentManager.unregisterFragmentLifecycleCallbacks(fragmentLifecycleCallbacks) super.onDestroy() } @@ -460,8 +500,8 @@ class HomeActivity : override fun getMenuRes() = R.menu.home override fun onPrepareOptionsMenu(menu: Menu): Boolean { - menu.findItem(R.id.menu_home_init_sync_legacy)?.isVisible = vectorPreferences.developerMode() - menu.findItem(R.id.menu_home_init_sync_optimized)?.isVisible = vectorPreferences.developerMode() + menu.findItem(R.id.menu_home_init_sync_legacy).isVisible = vectorPreferences.developerMode() + menu.findItem(R.id.menu_home_init_sync_optimized).isVisible = vectorPreferences.developerMode() return super.onPrepareOptionsMenu(menu) } @@ -511,30 +551,15 @@ class HomeActivity : } override fun navToMemberProfile(userId: String, deepLink: Uri): Boolean { - val listener = object : MatrixToBottomSheet.InteractionListener { - override fun navigateToRoom(roomId: String) { - navigator.openRoom(this@HomeActivity, roomId) - } - } // TODO check if there is already one?? - MatrixToBottomSheet.withLink(deepLink.toString(), listener) + MatrixToBottomSheet.withLink(deepLink.toString()) .show(supportFragmentManager, "HA#MatrixToBottomSheet") return true } override fun navToRoom(roomId: String?, eventId: String?, deepLink: Uri?): Boolean { if (roomId == null) return false - val listener = object : MatrixToBottomSheet.InteractionListener { - override fun navigateToRoom(roomId: String) { - navigator.openRoom(this@HomeActivity, roomId) - } - - override fun switchToSpace(spaceId: String) { - navigator.switchToSpace(this@HomeActivity, spaceId, Navigator.PostSwitchSpaceAction.None) - } - } - - MatrixToBottomSheet.withLink(deepLink.toString(), listener) + MatrixToBottomSheet.withLink(deepLink.toString()) .show(supportFragmentManager, "HA#MatrixToBottomSheet") return true } @@ -548,10 +573,15 @@ class HomeActivity : } companion object { - fun newIntent(context: Context, clearNotification: Boolean = false, accountCreation: Boolean = false): Intent { + fun newIntent(context: Context, + clearNotification: Boolean = false, + accountCreation: Boolean = false, + inviteNotificationRoomId: String? = null + ): Intent { val args = HomeActivityArgs( clearNotification = clearNotification, - accountCreation = accountCreation + accountCreation = accountCreation, + inviteNotificationRoomId = inviteNotificationRoomId ) return Intent(context, HomeActivity::class.java) @@ -559,11 +589,15 @@ class HomeActivity : putExtra(MvRx.KEY_ARG, args) } } - - private const val MATRIX_TO_CUSTOM_SCHEME_URL_BASE = "element://" - private const val ROOM_LINK_PREFIX = "${MATRIX_TO_CUSTOM_SCHEME_URL_BASE}room/" - private const val USER_LINK_PREFIX = "${MATRIX_TO_CUSTOM_SCHEME_URL_BASE}user/" } override fun create(initialState: ActiveSpaceViewState) = promoteRestrictedViewModelFactory.create(initialState) + + override fun mxToBottomSheetNavigateToRoom(roomId: String) { + navigator.openRoom(this, roomId) + } + + override fun mxToBottomSheetSwitchToSpace(spaceId: String) { + navigator.switchToSpace(this, spaceId, Navigator.PostSwitchSpaceAction.None) + } } diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt index bfedbd6f52e..be8b0cbc619 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewModel.kt @@ -40,7 +40,7 @@ import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse import org.matrix.android.sdk.api.auth.registration.nextUncompletedStage import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.pushrules.RuleIds -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.room.model.Membership import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams import org.matrix.android.sdk.api.util.toMatrixItem @@ -122,25 +122,26 @@ class HomeActivityViewModel @AssistedInject constructor( private fun observeInitialSync() { val session = activeSessionHolder.getSafeActiveSession() ?: return - session.getInitialSyncProgressStatus() + session.getSyncStatusLive() .asObservable() .subscribe { status -> when (status) { - is InitialSyncProgressService.Status.Progressing -> { + is SyncStatusService.Status.Progressing -> { // Schedule a check of the bootstrap when the init sync will be finished checkBootstrap = true } - is InitialSyncProgressService.Status.Idle -> { + is SyncStatusService.Status.Idle -> { if (checkBootstrap) { checkBootstrap = false maybeBootstrapCrossSigningAfterInitialSync() } } + else -> Unit } setState { copy( - initialSyncProgressServiceStatus = status + syncStatusServiceStatus = status ) } } @@ -217,9 +218,9 @@ class HomeActivityViewModel @AssistedInject constructor( object : UserInteractiveAuthInterceptor { override fun performStage(flowResponse: RegistrationFlowResponse, errCode: String?, promise: Continuation) { // We missed server grace period or it's not setup, see if we remember locally password - if (flowResponse.nextUncompletedStage() == LoginFlowTypes.PASSWORD - && errCode == null - && reAuthHelper.data != null) { + if (flowResponse.nextUncompletedStage() == LoginFlowTypes.PASSWORD && + errCode == null && + reAuthHelper.data != null) { promise.resume( UserPasswordAuth( session = flowResponse.session, diff --git a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewState.kt b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewState.kt index d4df7cd073c..f3bddaf0d21 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeActivityViewState.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeActivityViewState.kt @@ -17,8 +17,8 @@ package im.vector.app.features.home import com.airbnb.mvrx.MvRxState -import org.matrix.android.sdk.api.session.initsync.InitialSyncProgressService +import org.matrix.android.sdk.api.session.initsync.SyncStatusService data class HomeActivityViewState( - val initialSyncProgressServiceStatus: InitialSyncProgressService.Status = InitialSyncProgressService.Status.Idle + val syncStatusServiceStatus: SyncStatusService.Status = SyncStatusService.Status.Idle ) : MvRxState diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDetailAction.kt b/vector/src/main/java/im/vector/app/features/home/HomeDetailAction.kt index b466f204ec6..26f0e56f234 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeDetailAction.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeDetailAction.kt @@ -21,5 +21,5 @@ import im.vector.app.core.platform.VectorViewModelAction sealed class HomeDetailAction : VectorViewModelAction { data class SwitchTab(val tab: HomeTab) : HomeDetailAction() object MarkAllRoomsRead : HomeDetailAction() - data class StartCallWithPhoneNumber(val phoneNumber: String): HomeDetailAction() + data class StartCallWithPhoneNumber(val phoneNumber: String) : HomeDetailAction() } diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt index 9b71d1c90c9..627f4b45814 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt @@ -29,6 +29,7 @@ import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState import com.google.android.material.badge.BadgeDrawable +import im.vector.app.AppStateHandler import im.vector.app.R import im.vector.app.RoomGroupingMethod import im.vector.app.core.extensions.commitTransaction @@ -69,7 +70,8 @@ class HomeDetailFragment @Inject constructor( private val colorProvider: ColorProvider, private val alertManager: PopupAlertManager, private val callManager: WebRtcCallManager, - private val vectorPreferences: VectorPreferences + private val vectorPreferences: VectorPreferences, + private val appStateHandler: AppStateHandler ) : VectorBaseFragment(), KeysBackupBanner.Delegate, CurrentCallsView.Callback, @@ -204,6 +206,18 @@ class HomeDetailFragment @Inject constructor( // update notification tab if needed updateTabVisibilitySafely(R.id.bottom_action_notification, vectorPreferences.labAddNotificationTab()) callManager.checkForProtocolsSupportIfNeeded() + + // Current space/group is not live so at least refresh toolbar on resume + appStateHandler.getCurrentRoomGroupingMethod()?.let { roomGroupingMethod -> + when (roomGroupingMethod) { + is RoomGroupingMethod.ByLegacyGroup -> { + onGroupChange(roomGroupingMethod.groupSummary) + } + is RoomGroupingMethod.BySpace -> { + onSpaceChange(roomGroupingMethod.spaceSummary) + } + } + } } private fun promptForNewUnknownDevices(uid: String, state: UnknownDevicesState, newest: DeviceInfo) { @@ -426,7 +440,11 @@ class HomeDetailFragment @Inject constructor( views.bottomNavigationView.getOrCreateBadge(R.id.bottom_action_people).render(it.notificationCountPeople, it.notificationHighlightPeople) views.bottomNavigationView.getOrCreateBadge(R.id.bottom_action_rooms).render(it.notificationCountRooms, it.notificationHighlightRooms) views.bottomNavigationView.getOrCreateBadge(R.id.bottom_action_notification).render(it.notificationCountCatchup, it.notificationHighlightCatchup) - views.syncStateView.render(it.syncState) + views.syncStateView.render( + it.syncState, + it.incrementalSyncStatus, + it.pushCounter, + vectorPreferences.developerShowDebugInfo()) hasUnreadRooms = it.hasUnreadMessages } diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt index b960402f906..494a4148329 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewModel.kt @@ -33,13 +33,16 @@ import im.vector.app.features.call.webrtc.WebRtcCallManager import im.vector.app.features.createdirect.DirectRoomHelper import im.vector.app.features.invite.AutoAcceptInvites import im.vector.app.features.invite.showInvites +import im.vector.app.features.settings.VectorDataStore import im.vector.app.features.ui.UiStateRepository import io.reactivex.schedulers.Schedulers import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import org.matrix.android.sdk.api.query.ActiveSpaceFilter import org.matrix.android.sdk.api.query.RoomCategoryFilter import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.initsync.SyncStatusService import org.matrix.android.sdk.api.session.room.RoomSortOrder import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams @@ -56,11 +59,12 @@ import java.util.concurrent.TimeUnit class HomeDetailViewModel @AssistedInject constructor(@Assisted initialState: HomeDetailViewState, private val session: Session, private val uiStateRepository: UiStateRepository, + private val vectorDataStore: VectorDataStore, private val callManager: WebRtcCallManager, private val directRoomHelper: DirectRoomHelper, private val appStateHandler: AppStateHandler, -private val autoAcceptInvites: AutoAcceptInvites) - : VectorViewModel(initialState), + private val autoAcceptInvites: AutoAcceptInvites) : + VectorViewModel(initialState), CallProtocolsChecker.Listener { @AssistedFactory @@ -89,6 +93,7 @@ private val autoAcceptInvites: AutoAcceptInvites) observeRoomGroupingMethod() observeRoomSummaries() updateShowDialPadTab() + observeDataStore() callManager.addProtocolsCheckerListener(this) session.rx().liveUser(session.myUserId).execute { copy( @@ -97,6 +102,18 @@ private val autoAcceptInvites: AutoAcceptInvites) } } + private fun observeDataStore() { + viewModelScope.launch { + vectorDataStore.pushCounterFlow.collect { nbOfPush -> + setState { + copy( + pushCounter = nbOfPush + ) + } + } + } + } + override fun handle(action: HomeDetailAction) { when (action) { is HomeDetailAction.SwitchTab -> handleSwitchTab(action) @@ -173,6 +190,17 @@ private val autoAcceptInvites: AutoAcceptInvites) } } .disposeOnClear() + + session.getSyncStatusLive() + .asObservable() + .subscribe { + if (it is SyncStatusService.Status.IncrementalSyncStatus) { + setState { + copy(incrementalSyncStatus = it) + } + } + } + .disposeOnClear() } private fun observeRoomGroupingMethod() { diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewState.kt b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewState.kt index 304444abdd5..4022a0d9fb9 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeDetailViewState.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeDetailViewState.kt @@ -22,6 +22,7 @@ import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.Uninitialized import im.vector.app.R import im.vector.app.RoomGroupingMethod +import org.matrix.android.sdk.api.session.initsync.SyncStatusService import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.sync.SyncState import org.matrix.android.sdk.api.util.MatrixItem @@ -39,6 +40,8 @@ data class HomeDetailViewState( val notificationHighlightRooms: Boolean = false, val hasUnreadMessages: Boolean = false, val syncState: SyncState = SyncState.Idle, + val incrementalSyncStatus: SyncStatusService.Status.IncrementalSyncStatus = SyncStatusService.Status.IncrementalSyncIdle, + val pushCounter: Int = 0, val showDialPadTab: Boolean = false ) : MvRxState diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDrawerFragment.kt b/vector/src/main/java/im/vector/app/features/home/HomeDrawerFragment.kt index 9ff865b9d17..3accc247401 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeDrawerFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeDrawerFragment.kt @@ -30,13 +30,11 @@ import im.vector.app.core.extensions.replaceChildFragment import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.utils.startSharePlainTextIntent import im.vector.app.databinding.FragmentHomeDrawerBinding -// import im.vector.app.features.grouplist.GroupListFragment import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.VectorSettingsActivity import im.vector.app.features.spaces.SpaceListFragment import im.vector.app.features.usercode.UserCodeActivity import im.vector.app.features.workers.signout.SignOutUiWorker - import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.util.toMatrixItem import javax.inject.Inject diff --git a/vector/src/main/java/im/vector/app/features/home/LoadingFragment.kt b/vector/src/main/java/im/vector/app/features/home/LoadingFragment.kt index 2de36f2f5e5..47cfd1c28f4 100644 --- a/vector/src/main/java/im/vector/app/features/home/LoadingFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/LoadingFragment.kt @@ -23,7 +23,6 @@ import android.view.View import android.view.ViewGroup import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.databinding.FragmentLoadingBinding - import javax.inject.Inject class LoadingFragment @Inject constructor() : VectorBaseFragment() { diff --git a/vector/src/main/java/im/vector/app/features/home/ShortcutCreator.kt b/vector/src/main/java/im/vector/app/features/home/ShortcutCreator.kt index fc204a0c562..ee7edc021d6 100644 --- a/vector/src/main/java/im/vector/app/features/home/ShortcutCreator.kt +++ b/vector/src/main/java/im/vector/app/features/home/ShortcutCreator.kt @@ -78,7 +78,6 @@ class ShortcutCreator @Inject constructor( .setLongLived(true) .setRank(rank) .setCategories(categories) - .build() } diff --git a/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt index 988b4fbabe3..b88fe141eca 100644 --- a/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/UnknownDeviceDetectorSharedViewModel.kt @@ -25,8 +25,8 @@ import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.VectorViewModelAction @@ -58,8 +58,8 @@ data class DeviceDetectionInfo( class UnknownDeviceDetectorSharedViewModel @AssistedInject constructor(@Assisted initialState: UnknownDevicesState, session: Session, - private val vectorPreferences: VectorPreferences) - : VectorViewModel(initialState) { + private val vectorPreferences: VectorPreferences) : + VectorViewModel(initialState) { sealed class Action : VectorViewModelAction { data class IgnoreDevice(val deviceIds: List) : Action() diff --git a/vector/src/main/java/im/vector/app/features/home/UnreadMessagesSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/home/UnreadMessagesSharedViewModel.kt index f02711690a8..0ee7152cbbb 100644 --- a/vector/src/main/java/im/vector/app/features/home/UnreadMessagesSharedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/UnreadMessagesSharedViewModel.kt @@ -38,6 +38,7 @@ import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.room.RoomSortOrder import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams +import org.matrix.android.sdk.api.session.room.spaceSummaryQueryParams import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotificationCount import org.matrix.android.sdk.rx.asObservable import java.util.concurrent.TimeUnit @@ -56,8 +57,8 @@ class UnreadMessagesSharedViewModel @AssistedInject constructor(@Assisted initia session: Session, private val vectorPreferences: VectorPreferences, appStateHandler: AppStateHandler, - private val autoAcceptInvites: AutoAcceptInvites) - : VectorViewModel(initialState) { + private val autoAcceptInvites: AutoAcceptInvites) : + VectorViewModel(initialState) { @AssistedFactory interface Factory { @@ -143,6 +144,17 @@ class UnreadMessagesSharedViewModel @AssistedInject constructor(@Assisted initia roomSummaryQueryParams { this.memberships = listOf(Membership.INVITE) } ).size } + + val spaceInviteCount = if (autoAcceptInvites.hideInvites) { + 0 + } else { + session.getRoomSummaries( + spaceSummaryQueryParams { + this.memberships = listOf(Membership.INVITE) + } + ).size + } + val totalCount = session.getNotificationCountForRooms( roomSummaryQueryParams { this.memberships = listOf(Membership.JOIN) @@ -161,15 +173,16 @@ class UnreadMessagesSharedViewModel @AssistedInject constructor(@Assisted initia // filter out current selection it.roomId != selectedSpace } + CountInfo( homeCount = counts, otherCount = RoomAggregateNotificationCount( - rootCounts.fold(0, { acc, rs -> - acc + rs.notificationCount - }) + (counts.notificationCount.takeIf { selectedSpace != null } ?: 0), - rootCounts.fold(0, { acc, rs -> - acc + rs.highlightCount - }) + (counts.highlightCount.takeIf { selectedSpace != null } ?: 0) + notificationCount = rootCounts.fold(0, { acc, rs -> acc + rs.notificationCount }) + + (counts.notificationCount.takeIf { selectedSpace != null } ?: 0) + + spaceInviteCount, + highlightCount = rootCounts.fold(0, { acc, rs -> acc + rs.highlightCount }) + + (counts.highlightCount.takeIf { selectedSpace != null } ?: 0) + + spaceInviteCount ) ) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsFragment.kt index d66f3848a5b..47a8256628b 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsFragment.kt @@ -28,7 +28,6 @@ import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.databinding.FragmentBreadcrumbsBinding import im.vector.app.features.home.room.detail.RoomDetailSharedAction import im.vector.app.features.home.room.detail.RoomDetailSharedActionViewModel - import javax.inject.Inject class BreadcrumbsFragment @Inject constructor( diff --git a/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsItem.kt b/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsItem.kt index fd912a4953c..52996c74dee 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsItem.kt @@ -27,6 +27,7 @@ import im.vector.app.core.epoxy.ClickListener import im.vector.app.core.epoxy.VectorEpoxyHolder import im.vector.app.core.epoxy.VectorEpoxyModel import im.vector.app.core.epoxy.onClick +import im.vector.app.features.displayname.getBestName import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.room.list.UnreadCounterBadgeView import org.matrix.android.sdk.api.util.MatrixItem diff --git a/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsViewModel.kt index d3825de4efd..39004f4ed4e 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/breadcrumbs/BreadcrumbsViewModel.kt @@ -20,8 +20,8 @@ import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.core.platform.EmptyAction import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel @@ -33,8 +33,8 @@ import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams import org.matrix.android.sdk.rx.rx class BreadcrumbsViewModel @AssistedInject constructor(@Assisted initialState: BreadcrumbsViewState, - private val session: Session) - : VectorViewModel(initialState) { + private val session: Session) : + VectorViewModel(initialState) { @AssistedFactory interface Factory { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/AutoCompleter.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/AutoCompleter.kt index 8e6343519af..d4430730d71 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/AutoCompleter.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/AutoCompleter.kt @@ -36,6 +36,7 @@ import im.vector.app.features.autocomplete.group.AutocompleteGroupPresenter import im.vector.app.features.autocomplete.member.AutocompleteMemberPresenter import im.vector.app.features.autocomplete.room.AutocompleteRoomPresenter import im.vector.app.features.command.Command +import im.vector.app.features.displayname.getBestName import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.html.PillImageSpan import im.vector.app.features.themes.ThemeUtils diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/ChatEffectManager.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/ChatEffectManager.kt index 24151c5c109..f95baae36b9 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/ChatEffectManager.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/ChatEffectManager.kt @@ -105,8 +105,8 @@ class ChatEffectManager @Inject constructor() { } private fun hasAlreadyPlayed(event: TimelineEvent): Boolean { - return alreadyPlayed.contains(event.eventId) - || (event.root.unsignedData?.transactionId?.let { alreadyPlayed.contains(it) } ?: false) + return alreadyPlayed.contains(event.eventId) || + (event.root.unsignedData?.transactionId?.let { alreadyPlayed.contains(it) } ?: false) } private fun findEffect(content: MessageContent, event: TimelineEvent): ChatEffect? { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt index 94388dcfeb6..9c5f436728d 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt @@ -19,7 +19,7 @@ package im.vector.app.features.home.room.detail import android.net.Uri import android.view.View import im.vector.app.core.platform.VectorViewModelAction -import org.jitsi.meet.sdk.BroadcastEvent +import im.vector.app.features.call.conference.ConferenceEvent import org.matrix.android.sdk.api.session.content.ContentAttachmentData import org.matrix.android.sdk.api.session.room.model.message.MessageAudioContent import org.matrix.android.sdk.api.session.room.model.message.MessageStickerContent @@ -30,10 +30,7 @@ import org.matrix.android.sdk.api.session.widgets.model.Widget import org.matrix.android.sdk.api.util.MatrixItem sealed class RoomDetailAction : VectorViewModelAction { - data class UserIsTyping(val isTyping: Boolean) : RoomDetailAction() - data class SaveDraft(val draft: String) : RoomDetailAction() data class SendSticker(val stickerContent: MessageStickerContent) : RoomDetailAction() - data class SendMessage(val text: CharSequence, val autoMarkdown: Boolean) : RoomDetailAction() data class SendMedia(val attachments: List, val compressBeforeSending: Boolean) : RoomDetailAction() data class TimelineEventTurnsVisible(val event: TimelineEvent) : RoomDetailAction() data class TimelineEventTurnsInvisible(val event: TimelineEvent) : RoomDetailAction() @@ -52,11 +49,6 @@ sealed class RoomDetailAction : VectorViewModelAction { object EnterTrackingUnreadMessagesState : RoomDetailAction() object ExitTrackingUnreadMessagesState : RoomDetailAction() - data class EnterEditMode(val eventId: String, val text: String) : RoomDetailAction() - data class EnterQuoteMode(val eventId: String, val text: String) : RoomDetailAction() - data class EnterReplyMode(val eventId: String, val text: String) : RoomDetailAction() - data class EnterRegularMode(val text: String, val fromSharing: Boolean) : RoomDetailAction() - data class ResendMessage(val eventId: String) : RoomDetailAction() data class RemoveFailedEcho(val eventId: String) : RoomDetailAction() data class CancelSend(val eventId: String, val force: Boolean) : RoomDetailAction() @@ -75,7 +67,7 @@ sealed class RoomDetailAction : VectorViewModelAction { object ResendAll : RoomDetailAction() data class StartCall(val isVideo: Boolean) : RoomDetailAction() - data class AcceptCall(val callId: String): RoomDetailAction() + data class AcceptCall(val callId: String) : RoomDetailAction() object EndCall : RoomDetailAction() data class AcceptVerificationRequest(val transactionId: String, val otherUserId: String) : RoomDetailAction() @@ -91,13 +83,13 @@ sealed class RoomDetailAction : VectorViewModelAction { data class AddJitsiWidget(val withVideo: Boolean) : RoomDetailAction() data class RemoveWidget(val widgetId: String) : RoomDetailAction() - object JoinJitsiCall: RoomDetailAction() - object LeaveJitsiCall: RoomDetailAction() + object JoinJitsiCall : RoomDetailAction() + object LeaveJitsiCall : RoomDetailAction() data class EnsureNativeWidgetAllowed(val widget: Widget, val userJustAccepted: Boolean, val grantedEvents: RoomDetailViewEvents) : RoomDetailAction() - data class UpdateJoinJitsiCallStatus(val jitsiEvent: BroadcastEvent): RoomDetailAction() + data class UpdateJoinJitsiCallStatus(val conferenceEvent: ConferenceEvent) : RoomDetailAction() data class OpenOrCreateDm(val userId: String) : RoomDetailAction() data class JumpToReadReceipt(val userId: String) : RoomDetailAction() @@ -115,7 +107,7 @@ sealed class RoomDetailAction : VectorViewModelAction { // Failed messages object RemoveAllFailedMessages : RoomDetailAction() - data class RoomUpgradeSuccess(val replacementRoomId: String): RoomDetailAction() + data class RoomUpgradeSuccess(val replacementRoomId: String) : RoomDetailAction() // Voice Message object StartRecordingVoiceMessage : RoomDetailAction() diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt index 25428bbfbff..76c3816ce67 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailActivity.kt @@ -20,10 +20,12 @@ import android.content.Context import android.content.Intent import android.os.Bundle import android.widget.Toast -import com.google.android.material.appbar.MaterialToolbar import androidx.core.view.GravityCompat import androidx.drawerlayout.widget.DrawerLayout +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentManager import com.airbnb.mvrx.viewModel +import com.google.android.material.appbar.MaterialToolbar import im.vector.app.R import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.hideKeyboard @@ -32,25 +34,44 @@ import im.vector.app.core.platform.ToolbarConfigurable import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.databinding.ActivityRoomDetailBinding import im.vector.app.features.home.room.breadcrumbs.BreadcrumbsFragment +import im.vector.app.features.matrixto.MatrixToBottomSheet +import im.vector.app.features.navigation.Navigator import im.vector.app.features.room.RequireActiveMembershipAction import im.vector.app.features.room.RequireActiveMembershipViewEvents import im.vector.app.features.room.RequireActiveMembershipViewModel import im.vector.app.features.room.RequireActiveMembershipViewState import im.vector.app.features.widgets.permissions.RoomWidgetPermissionViewModel import im.vector.app.features.widgets.permissions.RoomWidgetPermissionViewState - import javax.inject.Inject class RoomDetailActivity : VectorBaseActivity(), ToolbarConfigurable, RequireActiveMembershipViewModel.Factory, - RoomWidgetPermissionViewModel.Factory { + RoomWidgetPermissionViewModel.Factory, + MatrixToBottomSheet.InteractionListener { override fun getBinding(): ActivityRoomDetailBinding { return ActivityRoomDetailBinding.inflate(layoutInflater) } + private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() { + + override fun onFragmentResumed(fm: FragmentManager, f: Fragment) { + if (f is MatrixToBottomSheet) { + f.interactionListener = this@RoomDetailActivity + } + super.onFragmentResumed(fm, f) + } + + override fun onFragmentPaused(fm: FragmentManager, f: Fragment) { + if (f is MatrixToBottomSheet) { + f.interactionListener = null + } + super.onFragmentPaused(fm, f) + } + } + override fun getCoordinatorLayout() = views.coordinatorLayout private lateinit var sharedActionViewModel: RoomDetailSharedActionViewModel @@ -79,6 +100,7 @@ class RoomDetailActivity : override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, false) waitingView = views.waitingView.waitingView val roomDetailArgs: RoomDetailArgs? = if (intent?.action == ACTION_ROOM_DETAILS_FROM_SHORTCUT) { RoomDetailArgs(roomId = intent?.extras?.getString(EXTRA_ROOM_ID)!!) @@ -130,6 +152,7 @@ class RoomDetailActivity : } override fun onDestroy() { + supportFragmentManager.unregisterFragmentLifecycleCallbacks(fragmentLifecycleCallbacks) views.drawerLayout.removeDrawerListener(drawerListener) super.onDestroy() } @@ -182,4 +205,12 @@ class RoomDetailActivity : } } } + + override fun mxToBottomSheetNavigateToRoom(roomId: String) { + navigator.openRoom(this, roomId) + } + + override fun mxToBottomSheetSwitchToSpace(spaceId: String) { + navigator.switchToSpace(this, spaceId, Navigator.PostSwitchSpaceAction.None) + } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt index 847b06fb456..b3fe6fcbf48 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt @@ -16,7 +16,6 @@ package im.vector.app.features.home.room.detail -import android.animation.ArgbEvaluator import android.annotation.SuppressLint import android.app.Activity import android.content.Intent @@ -66,7 +65,6 @@ import com.airbnb.mvrx.MvRx import com.airbnb.mvrx.args import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState -import com.google.android.material.button.MaterialButton import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.jakewharton.rxbinding3.view.focusChanges import com.jakewharton.rxbinding3.widget.textChanges @@ -88,11 +86,13 @@ import im.vector.app.core.hardware.vibrate import im.vector.app.core.intent.getFilenameFromUri import im.vector.app.core.intent.getMimeTypeFromUri import im.vector.app.core.platform.VectorBaseFragment +import im.vector.app.core.platform.lifecycleAwareLazy import im.vector.app.core.platform.showOptimizedSnackbar import im.vector.app.core.resources.ColorProvider import im.vector.app.core.ui.views.CurrentCallsView import im.vector.app.core.ui.views.CurrentCallsViewPresenter import im.vector.app.core.ui.views.FailedMessagesWarningView +import im.vector.app.core.ui.views.JoinConferenceView import im.vector.app.core.ui.views.NotificationAreaView import im.vector.app.core.utils.Debouncer import im.vector.app.core.utils.DimensionConverter @@ -124,15 +124,21 @@ import im.vector.app.features.attachments.preview.AttachmentsPreviewArgs import im.vector.app.features.attachments.toGroupedContentAttachmentData import im.vector.app.features.call.SharedKnownCallsViewModel import im.vector.app.features.call.VectorCallActivity -import im.vector.app.features.call.conference.JitsiBroadcastEmitter -import im.vector.app.features.call.conference.JitsiBroadcastEventObserver +import im.vector.app.features.call.conference.ConferenceEvent +import im.vector.app.features.call.conference.ConferenceEventEmitter +import im.vector.app.features.call.conference.ConferenceEventObserver import im.vector.app.features.call.conference.JitsiCallViewModel import im.vector.app.features.call.webrtc.WebRtcCallManager import im.vector.app.features.command.Command import im.vector.app.features.crypto.keysbackup.restore.KeysBackupRestoreActivity import im.vector.app.features.crypto.verification.VerificationBottomSheet import im.vector.app.features.home.AvatarRenderer +import im.vector.app.features.home.room.detail.composer.SendMode +import im.vector.app.features.home.room.detail.composer.TextComposerAction import im.vector.app.features.home.room.detail.composer.TextComposerView +import im.vector.app.features.home.room.detail.composer.TextComposerViewEvents +import im.vector.app.features.home.room.detail.composer.TextComposerViewModel +import im.vector.app.features.home.room.detail.composer.TextComposerViewState import im.vector.app.features.home.room.detail.composer.VoiceMessageRecorderView import im.vector.app.features.home.room.detail.readreceipts.DisplayReadReceiptsBottomSheet import im.vector.app.features.home.room.detail.timeline.TimelineEventController @@ -153,6 +159,7 @@ import im.vector.app.features.home.room.detail.timeline.item.ReadReceiptData import im.vector.app.features.home.room.detail.timeline.reactions.ViewReactionsBottomSheet import im.vector.app.features.home.room.detail.timeline.url.PreviewUrlRetriever import im.vector.app.features.home.room.detail.upgrade.MigrateRoomBottomSheet +import im.vector.app.features.home.room.detail.views.RoomDetailLazyLoadedViews import im.vector.app.features.home.room.detail.widget.RoomWidgetsBottomSheet import im.vector.app.features.html.EventHtmlRenderer import im.vector.app.features.html.PillImageSpan @@ -185,8 +192,6 @@ import nl.dionsegijn.konfetti.models.Shape import nl.dionsegijn.konfetti.models.Size import org.billcarsonfr.jsonviewer.JSonViewerDialog import org.commonmark.parser.Parser -import org.jitsi.meet.sdk.BroadcastEvent -import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.content.ContentAttachmentData import org.matrix.android.sdk.api.session.events.model.toModel @@ -217,7 +222,6 @@ import java.net.URL import java.util.UUID import java.util.concurrent.TimeUnit import javax.inject.Inject -import android.animation.ValueAnimator @Parcelize data class RoomDetailArgs( @@ -235,6 +239,7 @@ class RoomDetailFragment @Inject constructor( private val permalinkHandler: PermalinkHandler, private val notificationDrawerManager: NotificationDrawerManager, val roomDetailViewModelFactory: RoomDetailViewModel.Factory, + val textComposerViewModelFactory: TextComposerViewModel.Factory, private val eventHtmlRenderer: EventHtmlRenderer, private val vectorPreferences: VectorPreferences, private val colorProvider: ColorProvider, @@ -287,6 +292,7 @@ class RoomDetailFragment @Inject constructor( autoCompleterFactory.create(roomDetailArgs.roomId) } private val roomDetailViewModel: RoomDetailViewModel by fragmentViewModel() + private val textComposerViewModel: TextComposerViewModel by fragmentViewModel() private val debouncer = Debouncer(createUIHandler()) private lateinit var scrollOnNewMessageCallback: ScrollOnNewMessageCallback @@ -314,7 +320,10 @@ class RoomDetailFragment @Inject constructor( private var lockSendButton = false private val currentCallsViewPresenter = CurrentCallsViewPresenter() - private lateinit var emojiPopup: EmojiPopup + private val lazyLoadedViews = RoomDetailLazyLoadedViews() + private val emojiPopup: EmojiPopup by lifecycleAwareLazy { + createEmojiPopup() + } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -326,7 +335,7 @@ class RoomDetailFragment @Inject constructor( } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - lifecycle.addObserver(JitsiBroadcastEventObserver(vectorBaseActivity, this::onBroadcastJitsiEvent)) + lifecycle.addObserver(ConferenceEventObserver(vectorBaseActivity, this::onBroadcastJitsiEvent)) super.onViewCreated(view, savedInstanceState) sharedActionViewModel = activityViewModelProvider.get(MessageSharedActionViewModel::class.java) knownCallsViewModel = activityViewModelProvider.get(SharedKnownCallsViewModel::class.java) @@ -342,16 +351,15 @@ class RoomDetailFragment @Inject constructor( onTapToReturnToCall = ::onTapToReturnToCall ) keyboardStateUtils = KeyboardStateUtils(requireActivity()) + lazyLoadedViews.bind(views) setupToolbar(views.roomToolbar) setupRecyclerView() setupComposer() - setupInviteView() setupNotificationView() setupJumpToReadMarkerView() setupActiveCallView() setupJumpToBottomView() - setupEmojiPopup() - setupFailedMessagesWarningView() + setupEmojiButton() setupRemoveJitsiWidgetView() setupVoiceMessageView() @@ -368,16 +376,16 @@ class RoomDetailFragment @Inject constructor( knownCallsViewModel .liveKnownCalls - .observe(viewLifecycleOwner, { + .observe(viewLifecycleOwner) { currentCallsViewPresenter.updateCall(callManager.getCurrentCall(), it) invalidateOptionsMenu() - }) + } - roomDetailViewModel.selectSubscribe(RoomDetailViewState::canShowJumpToReadMarker, RoomDetailViewState::unreadState) { _, _ -> + roomDetailViewModel.selectSubscribe(this, RoomDetailViewState::canShowJumpToReadMarker, RoomDetailViewState::unreadState) { _, _ -> updateJumpToReadMarkerViewVisibility() } - roomDetailViewModel.selectSubscribe(RoomDetailViewState::sendMode, RoomDetailViewState::canSendMessage) { mode, canSend -> + textComposerViewModel.selectSubscribe(this, TextComposerViewState::sendMode, TextComposerViewState::canSendMessage) { mode, canSend -> if (!canSend) { return@selectSubscribe } @@ -389,8 +397,29 @@ class RoomDetailFragment @Inject constructor( } } - roomDetailViewModel.selectSubscribe(RoomDetailViewState::syncState) { syncState -> - views.syncStateView.render(syncState) + roomDetailViewModel.selectSubscribe( + this, + RoomDetailViewState::syncState, + RoomDetailViewState::incrementalSyncStatus, + RoomDetailViewState::pushCounter + ) { syncState, incrementalSyncStatus, pushCounter -> + views.syncStateView.render( + syncState, + incrementalSyncStatus, + pushCounter, + vectorPreferences.developerShowDebugInfo() + ) + } + + textComposerViewModel.observeViewEvents { + when (it) { + is TextComposerViewEvents.JoinRoomCommandSuccess -> handleJoinedToAnotherRoom(it) + is TextComposerViewEvents.SendMessageResult -> renderSendMessageResult(it) + is TextComposerViewEvents.ShowMessage -> showSnackWithMessage(it.message) + is TextComposerViewEvents.ShowRoomUpgradeDialog -> handleShowRoomUpgradeDialog(it) + is TextComposerViewEvents.AnimateSendButtonVisibility -> handleSendButtonVisibilityChanged(it) + is TextComposerViewEvents.OpenRoomMemberProfile -> openRoomMemberProfile(it.userId) + }.exhaustive } roomDetailViewModel.observeViewEvents { @@ -407,8 +436,6 @@ class RoomDetailFragment @Inject constructor( is RoomDetailViewEvents.ShowMessage -> showSnackWithMessage(it.message) is RoomDetailViewEvents.NavigateToEvent -> navigateToEvent(it) is RoomDetailViewEvents.DownloadFileState -> handleDownloadFileState(it) - is RoomDetailViewEvents.JoinRoomCommandSuccess -> handleJoinedToAnotherRoom(it) - is RoomDetailViewEvents.SendMessageResult -> renderSendMessageResult(it) is RoomDetailViewEvents.ShowE2EErrorMessage -> displayE2eError(it.withHeldCode) RoomDetailViewEvents.DisplayPromptForIntegrationManager -> displayPromptForIntegrationManager() is RoomDetailViewEvents.OpenStickerPicker -> openStickerPicker(it) @@ -433,7 +460,6 @@ class RoomDetailFragment @Inject constructor( RoomDetailViewEvents.StopChatEffects -> handleStopChatEffects() is RoomDetailViewEvents.DisplayAndAcceptCall -> acceptIncomingCall(it) RoomDetailViewEvents.RoomReplacementStarted -> handleRoomReplacement() - is RoomDetailViewEvents.ShowRoomUpgradeDialog -> handleShowRoomUpgradeDialog(it) }.exhaustive } @@ -443,6 +469,20 @@ class RoomDetailFragment @Inject constructor( } } + private fun handleSendButtonVisibilityChanged(event: TextComposerViewEvents.AnimateSendButtonVisibility) { + if (event.isVisible) { + views.voiceMessageRecorderView.isVisible = false + views.composerLayout.views.sendButton.alpha = 0f + views.composerLayout.views.sendButton.isVisible = true + views.composerLayout.views.sendButton.animate().alpha(1f).setDuration(150).start() + } else { + views.composerLayout.views.sendButton.isInvisible = true + views.voiceMessageRecorderView.alpha = 0f + views.voiceMessageRecorderView.isVisible = true + views.voiceMessageRecorderView.animate().alpha(1f).setDuration(150).start() + } + } + private fun setupRemoveJitsiWidgetView() { views.removeJitsiWidgetView.onCompleteSliding = { withState(roomDetailViewModel) { @@ -456,11 +496,11 @@ class RoomDetailFragment @Inject constructor( } private fun leaveJitsiConference() { - JitsiBroadcastEmitter(vectorBaseActivity).emitConferenceEnded() + ConferenceEventEmitter(vectorBaseActivity).emitConferenceEnded() } - private fun onBroadcastJitsiEvent(jitsiEvent: BroadcastEvent) { - roomDetailViewModel.handle(RoomDetailAction.UpdateJoinJitsiCallStatus(jitsiEvent)) + private fun onBroadcastJitsiEvent(conferenceEvent: ConferenceEvent) { + roomDetailViewModel.handle(RoomDetailAction.UpdateJoinJitsiCallStatus(conferenceEvent)) } private fun onCannotRecord() { @@ -484,7 +524,7 @@ class RoomDetailFragment @Inject constructor( JoinReplacementRoomBottomSheet().show(childFragmentManager, tag) } - private fun handleShowRoomUpgradeDialog(roomDetailViewEvents: RoomDetailViewEvents.ShowRoomUpgradeDialog) { + private fun handleShowRoomUpgradeDialog(roomDetailViewEvents: TextComposerViewEvents.ShowRoomUpgradeDialog) { val tag = MigrateRoomBottomSheet::javaClass.name MigrateRoomBottomSheet.newInstance(roomDetailArgs.roomId, roomDetailViewEvents.newVersion) .show(parentFragmentManager, tag) @@ -586,8 +626,14 @@ class RoomDetailFragment @Inject constructor( ) } - private fun setupEmojiPopup() { - emojiPopup = EmojiPopup + private fun setupEmojiButton() { + views.composerLayout.views.composerEmojiButton.debouncedClicks { + emojiPopup.toggle() + } + } + + private fun createEmojiPopup(): EmojiPopup { + return EmojiPopup .Builder .fromRootView(views.rootConstraintLayout) .setKeyboardAnimationStyle(R.style.emoji_fade_animation_style) @@ -604,14 +650,18 @@ class RoomDetailFragment @Inject constructor( } } .build(views.composerLayout.views.composerEditText) + } - views.composerLayout.views.composerEmojiButton.debouncedClicks { - emojiPopup.toggle() + private val permissionVoiceMessageLauncher = registerForPermissionsResult { allGranted, deniedPermanently -> + if (allGranted) { + // In this case, let the user start again the gesture + } else if (deniedPermanently) { + vectorBaseActivity.onPermissionDeniedSnackbar(R.string.denied_permission_voice_message) } } - private fun setupFailedMessagesWarningView() { - views.failedMessagesWarningView.callback = object : FailedMessagesWarningView.Callback { + private fun createFailedMessagesWarningCallback(): FailedMessagesWarningView.Callback { + return object : FailedMessagesWarningView.Callback { override fun onDeleteAllClicked() { MaterialAlertDialogBuilder(requireContext()) .setTitle(R.string.event_status_delete_all_failed_dialog_title) @@ -629,22 +679,14 @@ class RoomDetailFragment @Inject constructor( } } - private val permissionVoiceMessageLauncher = registerForPermissionsResult { allGranted, deniedPermanently -> - if (allGranted) { - // In this case, let the user start again the gesture - } else if (deniedPermanently) { - vectorBaseActivity.onPermissionDeniedSnackbar(R.string.denied_permission_voice_message) - } - } - private fun setupVoiceMessageView() { views.voiceMessageRecorderView.voiceMessagePlaybackTracker = voiceMessagePlaybackTracker views.voiceMessageRecorderView.callback = object : VoiceMessageRecorderView.Callback { override fun onVoiceRecordingStarted(): Boolean { return if (checkPermissions(PERMISSIONS_FOR_VOICE_MESSAGE, requireActivity(), permissionVoiceMessageLauncher)) { - views.composerLayout.isInvisible = true roomDetailViewModel.handle(RoomDetailAction.StartRecordingVoiceMessage) + textComposerViewModel.handle(TextComposerAction.OnVoiceRecordingStateChanged(true)) vibrate(requireContext()) true } else { @@ -654,8 +696,8 @@ class RoomDetailFragment @Inject constructor( } override fun onVoiceRecordingEnded(isCancelled: Boolean) { - views.composerLayout.isInvisible = false roomDetailViewModel.handle(RoomDetailAction.EndRecordingVoiceMessage(isCancelled)) + textComposerViewModel.handle(TextComposerAction.OnVoiceRecordingStateChanged(false)) } override fun onVoiceRecordingPlaybackModeOn() { @@ -740,8 +782,8 @@ class RoomDetailFragment @Inject constructor( .show() } - private fun handleJoinedToAnotherRoom(action: RoomDetailViewEvents.JoinRoomCommandSuccess) { - updateComposerText("") + private fun handleJoinedToAnotherRoom(action: TextComposerViewEvents.JoinRoomCommandSuccess) { + views.composerLayout.setTextIfDifferent("") lockSendButton = false navigator.openRoom(vectorBaseActivity, action.roomId) } @@ -749,7 +791,7 @@ class RoomDetailFragment @Inject constructor( private fun handleShareData() { when (val sharedData = roomDetailArgs.sharedData) { is SharedData.Text -> { - roomDetailViewModel.handle(RoomDetailAction.EnterRegularMode(sharedData.text, fromSharing = true)) + textComposerViewModel.handle(TextComposerAction.EnterRegularMode(sharedData.text, fromSharing = true)) } is SharedData.Attachments -> { // open share edition @@ -769,6 +811,7 @@ class RoomDetailFragment @Inject constructor( } override fun onDestroyView() { + lazyLoadedViews.unBind() timelineEventController.callback = null timelineEventController.removeModelBuildListener(modelBuildListener) currentCallsViewPresenter.unBind() @@ -776,8 +819,6 @@ class RoomDetailFragment @Inject constructor( autoCompleter.clear() debouncer.cancelAll() views.timelineRecyclerView.cleanup() - emojiPopup.dismiss() - super.onDestroyView() } @@ -869,20 +910,8 @@ class RoomDetailFragment @Inject constructor( } } val joinConfItem = menu.findItem(R.id.join_conference) - joinConfItem.actionView.findViewById(R.id.join_conference_button).also { joinButton -> - joinButton.setOnClickListener { roomDetailViewModel.handle(RoomDetailAction.JoinJitsiCall) } - val colorFrom = ContextCompat.getColor(joinButton.context, R.color.palette_element_green) - val colorTo = ContextCompat.getColor(joinButton.context, R.color.join_conference_animated_color) - // Animate button color to highlight - ValueAnimator.ofObject(ArgbEvaluator(), colorFrom, colorTo).apply { - repeatMode = ValueAnimator.REVERSE - repeatCount = ValueAnimator.INFINITE - duration = 500 - addUpdateListener { animator -> - val color = animator.animatedValue as Int - joinButton.setBackgroundColor(color) - } - }.start() + (joinConfItem.actionView as? JoinConferenceView)?.onJoinClicked = { + roomDetailViewModel.handle(RoomDetailAction.JoinJitsiCall) } } @@ -980,10 +1009,7 @@ class RoomDetailFragment @Inject constructor( private fun renderRegularMode(text: String) { autoCompleter.exitSpecialMode() views.composerLayout.collapse() - - views.voiceMessageRecorderView.isVisible = text.isBlank() - - updateComposerText(text) + views.composerLayout.setTextIfDifferent(text) views.composerLayout.views.sendButton.contentDescription = getString(R.string.send) } @@ -1022,7 +1048,7 @@ class RoomDetailFragment @Inject constructor( false } - updateComposerText(defaultContent) + views.composerLayout.setTextIfDifferent(defaultContent) views.composerLayout.views.composerRelatedMessageActionIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), iconRes)) views.composerLayout.views.sendButton.contentDescription = getString(descriptionRes) @@ -1034,21 +1060,11 @@ class RoomDetailFragment @Inject constructor( // need to do it here also when not using quick reply focusComposerAndShowKeyboard() views.composerLayout.views.composerRelatedMessageImage.isVisible = isImageVisible - views.voiceMessageRecorderView.isVisible = false } } focusComposerAndShowKeyboard() } - private fun updateComposerText(text: String) { - // Do not update if this is the same text to avoid the cursor to move - if (text != views.composerLayout.text.toString()) { - // Ignore update to avoid saving a draft - views.composerLayout.views.composerEditText.setText(text) - views.composerLayout.views.composerEditText.setSelection(views.composerLayout.text?.length ?: 0) - } - } - override fun onResume() { super.onResume() notificationDrawerManager.setCurrentRoom(roomDetailArgs.roomId) @@ -1077,7 +1093,7 @@ class RoomDetailFragment @Inject constructor( notificationDrawerManager.setCurrentRoom(null) - roomDetailViewModel.handle(RoomDetailAction.SaveDraft(views.composerLayout.text.toString())) + textComposerViewModel.handle(TextComposerAction.SaveDraft(views.composerLayout.text.toString())) // We should improve the UX to support going into playback mode when paused and delete the media when the view is destroyed. roomDetailViewModel.handle(RoomDetailAction.EndAllVoiceActions) @@ -1196,12 +1212,12 @@ class RoomDetailFragment @Inject constructor( override fun performQuickReplyOnHolder(model: EpoxyModel<*>) { (model as? AbsMessageItem)?.attributes?.informationData?.let { val eventId = it.eventId - roomDetailViewModel.handle(RoomDetailAction.EnterReplyMode(eventId, views.composerLayout.text.toString())) + textComposerViewModel.handle(TextComposerAction.EnterReplyMode(eventId, views.composerLayout.text.toString())) } } override fun canSwipeModel(model: EpoxyModel<*>): Boolean { - val canSendMessage = withState(roomDetailViewModel) { + val canSendMessage = withState(textComposerViewModel) { it.canSendMessage } if (!canSendMessage) { @@ -1279,10 +1295,10 @@ class RoomDetailFragment @Inject constructor( true } // Add external keyboard functionality (to send messages) - else if (null != keyEvent - && !keyEvent.isShiftPressed - && keyEvent.keyCode == KeyEvent.KEYCODE_ENTER - && resources.configuration.keyboard != Configuration.KEYBOARD_NOKEYS) { + else if (null != keyEvent && + !keyEvent.isShiftPressed && + keyEvent.keyCode == KeyEvent.KEYCODE_ENTER && + resources.configuration.keyboard != Configuration.KEYBOARD_NOKEYS) { sendTextMessage(v.text) true } else false @@ -1303,22 +1319,15 @@ class RoomDetailFragment @Inject constructor( } override fun onCloseRelatedMessage() { - roomDetailViewModel.handle(RoomDetailAction.EnterRegularMode(views.composerLayout.text.toString(), false)) + textComposerViewModel.handle(TextComposerAction.EnterRegularMode(views.composerLayout.text.toString(), false)) } override fun onRichContentSelected(contentUri: Uri): Boolean { return sendUri(contentUri) } - override fun onTextBlankStateChanged(isBlank: Boolean) { - if (!views.composerLayout.views.sendButton.isVisible) { - // Animate alpha to prevent overlapping with the animation of the send button - views.voiceMessageRecorderView.alpha = 0f - views.voiceMessageRecorderView.isVisible = true - views.voiceMessageRecorderView.animate().alpha(1f).setDuration(300).start() - } else { - views.voiceMessageRecorderView.isVisible = false - } + override fun onTextChanged(text: CharSequence) { + textComposerViewModel.handle(TextComposerAction.OnTextChanged(text)) } } } @@ -1332,7 +1341,7 @@ class RoomDetailFragment @Inject constructor( // We collapse ASAP, if not there will be a slight annoying delay views.composerLayout.collapse(true) lockSendButton = true - roomDetailViewModel.handle(RoomDetailAction.SendMessage(text, vectorPreferences.isMarkdownEnabled())) + textComposerViewModel.handle(TextComposerAction.SendMessage(text, vectorPreferences.isMarkdownEnabled())) emojiPopup.dismiss() } } @@ -1344,7 +1353,7 @@ class RoomDetailFragment @Inject constructor( .map { it.isNotEmpty() } .subscribe { Timber.d("Typing: User is typing: $it") - roomDetailViewModel.handle(RoomDetailAction.UserIsTyping(it)) + textComposerViewModel.handle(TextComposerAction.UserIsTyping(it)) } .disposeOnDestroyView() @@ -1364,51 +1373,56 @@ class RoomDetailFragment @Inject constructor( return isHandled } - private fun setupInviteView() { - views.inviteView.callback = this - } - - override fun invalidate() = withState(roomDetailViewModel) { state -> + override fun invalidate() = withState(roomDetailViewModel, textComposerViewModel) { mainState, textComposerState -> invalidateOptionsMenu() - val summary = state.asyncRoomSummary() - renderToolbar(summary, state.typingMessage) - views.removeJitsiWidgetView.render(state) - views.failedMessagesWarningView.render(state.hasFailedSending) - val inviter = state.asyncInviter() + val summary = mainState.asyncRoomSummary() + renderToolbar(summary, mainState.formattedTypingUsers) + views.removeJitsiWidgetView.render(mainState) + if (mainState.hasFailedSending) { + lazyLoadedViews.failedMessagesWarningView(inflateIfNeeded = true, createFailedMessagesWarningCallback())?.isVisible = true + } else { + lazyLoadedViews.failedMessagesWarningView(inflateIfNeeded = false)?.isVisible = false + } + val inviter = mainState.asyncInviter() if (summary?.membership == Membership.JOIN) { views.jumpToBottomView.count = summary.notificationCount views.jumpToBottomView.drawBadge = summary.hasUnreadMessages - timelineEventController.update(state) - views.inviteView.isVisible = false - if (state.tombstoneEvent == null) { - if (state.canSendMessage) { - if (!views.voiceMessageRecorderView.isActive()) { - views.composerLayout.isVisible = true - views.voiceMessageRecorderView.isVisible = views.composerLayout.text?.isBlank().orFalse() - views.composerLayout.setRoomEncrypted(summary.isEncrypted) - views.notificationAreaView.render(NotificationAreaView.State.Hidden) - views.composerLayout.alwaysShowSendButton = false - } + timelineEventController.update(mainState) + lazyLoadedViews.inviteView(false)?.isVisible = false + if (mainState.tombstoneEvent == null) { + views.composerLayout.isInvisible = !textComposerState.isComposerVisible + views.voiceMessageRecorderView.isVisible = !textComposerState.isSendButtonVisible + views.composerLayout.views.sendButton.isInvisible = !textComposerState.isSendButtonVisible + views.composerLayout.setRoomEncrypted(summary.isEncrypted) + // views.composerLayout.alwaysShowSendButton = false + if (textComposerState.canSendMessage) { + views.notificationAreaView.render(NotificationAreaView.State.Hidden) } else { - views.composerLayout.isVisible = false - views.voiceMessageRecorderView.isVisible = false views.notificationAreaView.render(NotificationAreaView.State.NoPermissionToPost) } } else { - views.composerLayout.isVisible = false - views.voiceMessageRecorderView.isVisible = false - views.notificationAreaView.render(NotificationAreaView.State.Tombstone(state.tombstoneEvent)) + views.hideComposerViews() + views.notificationAreaView.render(NotificationAreaView.State.Tombstone(mainState.tombstoneEvent)) } } else if (summary?.membership == Membership.INVITE && inviter != null) { - views.inviteView.isVisible = true - views.inviteView.render(inviter, VectorInviteView.Mode.LARGE, state.changeMembershipState) - // Intercept click event - views.inviteView.setOnClickListener { } - } else if (state.asyncInviter.complete) { + views.hideComposerViews() + lazyLoadedViews.inviteView(true)?.apply { + callback = this@RoomDetailFragment + isVisible = true + render(inviter, VectorInviteView.Mode.LARGE, mainState.changeMembershipState) + setOnClickListener { } + } + Unit + } else if (mainState.asyncInviter.complete) { vectorBaseActivity.finish() } } + private fun FragmentRoomDetailBinding.hideComposerViews() { + composerLayout.isVisible = false + voiceMessageRecorderView.isVisible = false + } + private fun renderToolbar(roomSummary: RoomSummary?, typingMessage: String?) { if (roomSummary == null) { views.roomToolbarContentView.isClickable = false @@ -1437,24 +1451,24 @@ class RoomDetailFragment @Inject constructor( } } - private fun renderSendMessageResult(sendMessageResult: RoomDetailViewEvents.SendMessageResult) { + private fun renderSendMessageResult(sendMessageResult: TextComposerViewEvents.SendMessageResult) { when (sendMessageResult) { - is RoomDetailViewEvents.SlashCommandHandled -> { + is TextComposerViewEvents.SlashCommandHandled -> { sendMessageResult.messageRes?.let { showSnackWithMessage(getString(it)) } } - is RoomDetailViewEvents.SlashCommandError -> { + is TextComposerViewEvents.SlashCommandError -> { displayCommandError(getString(R.string.command_problem_with_parameters, sendMessageResult.command.command)) } - is RoomDetailViewEvents.SlashCommandUnknown -> { + is TextComposerViewEvents.SlashCommandUnknown -> { displayCommandError(getString(R.string.unrecognized_command, sendMessageResult.command)) } - is RoomDetailViewEvents.SlashCommandResultOk -> { - updateComposerText("") + is TextComposerViewEvents.SlashCommandResultOk -> { + views.composerLayout.setTextIfDifferent("") } - is RoomDetailViewEvents.SlashCommandResultError -> { + is TextComposerViewEvents.SlashCommandResultError -> { displayCommandError(errorFormatter.toHumanReadable(sendMessageResult.throwable)) } - is RoomDetailViewEvents.SlashCommandNotImplemented -> { + is TextComposerViewEvents.SlashCommandNotImplemented -> { displayCommandError(getString(R.string.not_implemented)) } } // .exhaustive @@ -1727,7 +1741,7 @@ class RoomDetailFragment @Inject constructor( override fun onEventLongClicked(informationData: MessageInformationData, messageContent: Any?, view: View): Boolean { view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS) - val roomId = roomDetailViewModel.timeline.getTimelineEventWithId(informationData.eventId)?.roomId ?: return false + val roomId = roomDetailArgs.roomId this.view?.hideKeyboard() MessageActionsBottomSheet @@ -1860,8 +1874,8 @@ class RoomDetailFragment @Inject constructor( } private fun onSaveActionClicked(action: EventSharedAction.Save) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q - && !checkPermissions(PERMISSIONS_FOR_WRITING_FILES, requireActivity(), saveActionActivityResultLauncher)) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q && + !checkPermissions(PERMISSIONS_FOR_WRITING_FILES, requireActivity(), saveActionActivityResultLauncher)) { sharedActionViewModel.pendingAction = action return } @@ -1933,17 +1947,17 @@ class RoomDetailFragment @Inject constructor( } is EventSharedAction.Edit -> { if (!views.voiceMessageRecorderView.isActive()) { - roomDetailViewModel.handle(RoomDetailAction.EnterEditMode(action.eventId, views.composerLayout.text.toString())) + textComposerViewModel.handle(TextComposerAction.EnterEditMode(action.eventId, views.composerLayout.text.toString())) } else { requireActivity().toast(R.string.error_voice_message_cannot_reply_or_edit) } } is EventSharedAction.Quote -> { - roomDetailViewModel.handle(RoomDetailAction.EnterQuoteMode(action.eventId, views.composerLayout.text.toString())) + textComposerViewModel.handle(TextComposerAction.EnterQuoteMode(action.eventId, views.composerLayout.text.toString())) } is EventSharedAction.Reply -> { if (!views.voiceMessageRecorderView.isActive()) { - roomDetailViewModel.handle(RoomDetailAction.EnterReplyMode(action.eventId, views.composerLayout.text.toString())) + textComposerViewModel.handle(TextComposerAction.EnterReplyMode(action.eventId, views.composerLayout.text.toString())) } else { requireActivity().toast(R.string.error_voice_message_cannot_reply_or_edit) } @@ -2013,8 +2027,8 @@ class RoomDetailFragment @Inject constructor( private fun insertUserDisplayNameInTextEditor(userId: String) { val startToCompose = views.composerLayout.text.isNullOrBlank() - if (startToCompose - && userId == session.myUserId) { + if (startToCompose && + userId == session.myUserId) { // Empty composer, current user: start an emote views.composerLayout.views.composerEditText.setText(Command.EMOTE.command + " ") views.composerLayout.views.composerEditText.setSelection(Command.EMOTE.length) @@ -2155,7 +2169,7 @@ class RoomDetailFragment @Inject constructor( override fun onContactAttachmentReady(contactAttachment: ContactAttachment) { super.onContactAttachmentReady(contactAttachment) val formattedContact = contactAttachment.toHumanReadable() - roomDetailViewModel.handle(RoomDetailAction.SendMessage(formattedContact, false)) + textComposerViewModel.handle(TextComposerAction.SendMessage(formattedContact, false)) } private fun onViewWidgetsClicked() { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt index 2802ee2f83c..2e7f2bfd638 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt @@ -18,10 +18,8 @@ package im.vector.app.features.home.room.detail import android.net.Uri import android.view.View -import androidx.annotation.StringRes import im.vector.app.core.platform.VectorViewEvents import im.vector.app.features.call.webrtc.WebRtcCall -import im.vector.app.features.command.Command import org.matrix.android.sdk.api.session.widgets.model.Widget import org.matrix.android.sdk.api.util.MatrixItem import org.matrix.android.sdk.internal.crypto.model.event.WithHeldCode @@ -66,9 +64,7 @@ sealed class RoomDetailViewEvents : VectorViewEvents { val mimeType: String? ) : RoomDetailViewEvents() - abstract class SendMessageResult : RoomDetailViewEvents() - - data class DisplayAndAcceptCall(val call: WebRtcCall): RoomDetailViewEvents() + data class DisplayAndAcceptCall(val call: WebRtcCall) : RoomDetailViewEvents() object DisplayPromptForIntegrationManager : RoomDetailViewEvents() @@ -82,19 +78,7 @@ sealed class RoomDetailViewEvents : VectorViewEvents { val domain: String, val grantedEvents: RoomDetailViewEvents) : RoomDetailViewEvents() - object MessageSent : SendMessageResult() - data class JoinRoomCommandSuccess(val roomId: String) : SendMessageResult() - class SlashCommandError(val command: Command) : SendMessageResult() - class SlashCommandUnknown(val command: String) : SendMessageResult() - data class SlashCommandHandled(@StringRes val messageRes: Int? = null) : SendMessageResult() - object SlashCommandResultOk : SendMessageResult() - class SlashCommandResultError(val throwable: Throwable) : SendMessageResult() - - // TODO Remove - object SlashCommandNotImplemented : SendMessageResult() - data class StartChatEffect(val type: ChatEffect) : RoomDetailViewEvents() object StopChatEffects : RoomDetailViewEvents() object RoomReplacementStarted : RoomDetailViewEvents() - data class ShowRoomUpgradeDialog(val newVersion: String, val isPublic: Boolean): RoomDetailViewEvents() } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt index 3e902dc2efc..bd11ec6718d 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt @@ -38,36 +38,32 @@ import im.vector.app.core.extensions.exhaustive import im.vector.app.core.mvrx.runCatchingToAsync import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider -import im.vector.app.features.call.conference.JitsiActiveConferenceHolder import im.vector.app.features.attachments.toContentAttachmentData +import im.vector.app.features.call.conference.ConferenceEvent +import im.vector.app.features.call.conference.JitsiActiveConferenceHolder import im.vector.app.features.call.conference.JitsiService import im.vector.app.features.call.lookup.CallProtocolsChecker import im.vector.app.features.call.webrtc.WebRtcCallManager -import im.vector.app.features.command.CommandParser -import im.vector.app.features.command.ParsedCommand import im.vector.app.features.createdirect.DirectRoomHelper import im.vector.app.features.crypto.keysrequest.OutboundSessionKeySharingStrategy import im.vector.app.features.crypto.verification.SupportedVerificationMethodsProvider import im.vector.app.features.home.room.detail.composer.VoiceMessageHelper -import im.vector.app.features.home.room.detail.composer.rainbow.RainbowGenerator import im.vector.app.features.home.room.detail.sticker.StickerPickerActionHandler import im.vector.app.features.home.room.detail.timeline.factory.TimelineFactory import im.vector.app.features.home.room.detail.timeline.url.PreviewUrlRetriever import im.vector.app.features.home.room.typing.TypingHelper import im.vector.app.features.powerlevel.PowerLevelsObservableFactory import im.vector.app.features.session.coroutineScope +import im.vector.app.features.settings.VectorDataStore import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.voice.VoicePlayerHelper import io.reactivex.Observable import io.reactivex.rxkotlin.subscribeBy import io.reactivex.schedulers.Schedulers import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import org.commonmark.parser.Parser -import org.commonmark.renderer.html.HtmlRenderer -import org.jitsi.meet.sdk.BroadcastEvent -import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.MatrixPatterns import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.query.QueryStringValue @@ -80,28 +76,22 @@ import org.matrix.android.sdk.api.session.events.model.isTextMessage 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.file.FileService +import org.matrix.android.sdk.api.session.initsync.SyncStatusService import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState import org.matrix.android.sdk.api.session.room.members.roomMemberQueryParams 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.RoomMemberSummary import org.matrix.android.sdk.api.session.room.model.RoomSummary -import org.matrix.android.sdk.api.session.room.model.message.MessageType -import org.matrix.android.sdk.api.session.room.model.message.OptionItem import org.matrix.android.sdk.api.session.room.model.message.getFileUrl import org.matrix.android.sdk.api.session.room.model.tombstone.RoomTombstoneContent import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper import org.matrix.android.sdk.api.session.room.read.ReadService -import org.matrix.android.sdk.api.session.room.send.UserDraft import org.matrix.android.sdk.api.session.room.timeline.Timeline import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent -import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent -import org.matrix.android.sdk.api.session.room.timeline.getRelationContent -import org.matrix.android.sdk.api.session.room.timeline.getTextEditableContent -import org.matrix.android.sdk.api.session.space.CreateSpaceParams import org.matrix.android.sdk.api.session.widgets.model.WidgetType import org.matrix.android.sdk.api.util.toOptional import org.matrix.android.sdk.internal.crypto.model.event.WithHeldCode +import org.matrix.android.sdk.rx.asObservable import org.matrix.android.sdk.rx.rx import org.matrix.android.sdk.rx.unwrap import timber.log.Timber @@ -111,8 +101,8 @@ import java.util.concurrent.atomic.AtomicBoolean class RoomDetailViewModel @AssistedInject constructor( @Assisted private val initialState: RoomDetailViewState, private val vectorPreferences: VectorPreferences, + private val vectorDataStore: VectorDataStore, private val stringProvider: StringProvider, - private val rainbowGenerator: RainbowGenerator, private val session: Session, private val supportedVerificationMethodsProvider: SupportedVerificationMethodsProvider, private val stickerPickerActionHandler: StickerPickerActionHandler, @@ -174,8 +164,8 @@ class RoomDetailViewModel @AssistedInject constructor( observeSummaryState() getUnreadState() observeSyncState() + observeDataStore() observeEventDisplayedActions() - loadDraftIfAny() observeUnreadState() observeMyRoomMember() observeActiveRoomWidgets() @@ -198,6 +188,18 @@ class RoomDetailViewModel @AssistedInject constructor( } } + private fun observeDataStore() { + viewModelScope.launch { + vectorDataStore.pushCounterFlow.collect { nbOfPush -> + setState { + copy( + pushCounter = nbOfPush + ) + } + } + } + } + private fun prepareForEncryption() { // check if there is not already a call made, or if there has been an error if (prepareToEncrypt.shouldLoad) { @@ -217,13 +219,11 @@ class RoomDetailViewModel @AssistedInject constructor( private fun observePowerLevel() { PowerLevelsObservableFactory(room).createObservable() .subscribe { - val canSendMessage = PowerLevelsHelper(it).isUserAllowedToSend(session.myUserId, false, EventType.MESSAGE) val canInvite = PowerLevelsHelper(it).isUserAbleToInvite(session.myUserId) val isAllowedToManageWidgets = session.widgetService().hasPermissionsToHandleWidgets(room.roomId) val isAllowedToStartWebRTCCall = PowerLevelsHelper(it).isUserAllowedToSend(session.myUserId, false, EventType.CALL_INVITE) setState { copy( - canSendMessage = canSendMessage, canInvite = canInvite, isAllowedToManageWidgets = isAllowedToManageWidgets, isAllowedToStartWebRTCCall = isAllowedToStartWebRTCCall @@ -282,10 +282,7 @@ class RoomDetailViewModel @AssistedInject constructor( override fun handle(action: RoomDetailAction) { when (action) { - is RoomDetailAction.UserIsTyping -> handleUserIsTyping(action) is RoomDetailAction.ComposerFocusChange -> handleComposerFocusChange(action) - is RoomDetailAction.SaveDraft -> handleSaveDraft(action) - is RoomDetailAction.SendMessage -> handleSendMessage(action) is RoomDetailAction.SendMedia -> handleSendMedia(action) is RoomDetailAction.SendSticker -> handleSendSticker(action) is RoomDetailAction.TimelineEventTurnsVisible -> handleEventVisible(action) @@ -297,10 +294,6 @@ class RoomDetailViewModel @AssistedInject constructor( is RoomDetailAction.RedactAction -> handleRedactEvent(action) is RoomDetailAction.UndoReaction -> handleUndoReact(action) is RoomDetailAction.UpdateQuickReactAction -> handleUpdateQuickReaction(action) - is RoomDetailAction.EnterRegularMode -> handleEnterRegularMode(action) - is RoomDetailAction.EnterEditMode -> handleEditAction(action) - is RoomDetailAction.EnterQuoteMode -> handleQuoteAction(action) - is RoomDetailAction.EnterReplyMode -> handleReplyAction(action) is RoomDetailAction.DownloadOrOpen -> handleOpenOrDownloadFile(action) is RoomDetailAction.NavigateToEvent -> handleNavigateToEvent(action) is RoomDetailAction.JoinAndOpenReplacementRoom -> handleJoinAndOpenReplacementRoom() @@ -368,12 +361,12 @@ class RoomDetailViewModel @AssistedInject constructor( } return@withState } - when (action.jitsiEvent.type) { - BroadcastEvent.Type.CONFERENCE_JOINED, - BroadcastEvent.Type.CONFERENCE_TERMINATED -> { + when (action.conferenceEvent) { + is ConferenceEvent.Joined, + is ConferenceEvent.Terminated -> { setState { copy(jitsiState = jitsiState.copy(hasJoined = activeConferenceHolder.isJoined(jitsiState.confId))) } } - else -> Unit + else -> Unit } } @@ -532,8 +525,8 @@ class RoomDetailViewModel @AssistedInject constructor( val widget = action.widget val domain = action.widget.widgetContent.data["domain"] as? String ?: "" val isAllowed = action.userJustAccepted || if (widget.type == WidgetType.Jitsi) { - widget.senderInfo?.userId == session.myUserId - || session.integrationManagerService().isNativeWidgetDomainAllowed( + widget.senderInfo?.userId == session.myUserId || + session.integrationManagerService().isNativeWidgetDomainAllowed( action.widget.type.preferred, domain ) @@ -572,70 +565,6 @@ class RoomDetailViewModel @AssistedInject constructor( return room.getRoomMember(userId) } - /** - * Convert a send mode to a draft and save the draft - */ - private fun handleSaveDraft(action: RoomDetailAction.SaveDraft) = withState { - session.coroutineScope.launch { - when { - it.sendMode is SendMode.REGULAR && !it.sendMode.fromSharing -> { - setState { copy(sendMode = it.sendMode.copy(action.draft)) } - room.saveDraft(UserDraft.REGULAR(action.draft)) - } - it.sendMode is SendMode.REPLY -> { - setState { copy(sendMode = it.sendMode.copy(text = action.draft)) } - room.saveDraft(UserDraft.REPLY(it.sendMode.timelineEvent.root.eventId!!, action.draft)) - } - it.sendMode is SendMode.QUOTE -> { - setState { copy(sendMode = it.sendMode.copy(text = action.draft)) } - room.saveDraft(UserDraft.QUOTE(it.sendMode.timelineEvent.root.eventId!!, action.draft)) - } - it.sendMode is SendMode.EDIT -> { - setState { copy(sendMode = it.sendMode.copy(text = action.draft)) } - room.saveDraft(UserDraft.EDIT(it.sendMode.timelineEvent.root.eventId!!, action.draft)) - } - } - } - } - - private fun loadDraftIfAny() { - val currentDraft = room.getDraft() - setState { - copy( - // Create a sendMode from a draft and retrieve the TimelineEvent - sendMode = when (currentDraft) { - is UserDraft.REGULAR -> SendMode.REGULAR(currentDraft.text, false) - is UserDraft.QUOTE -> { - room.getTimeLineEvent(currentDraft.linkedEventId)?.let { timelineEvent -> - SendMode.QUOTE(timelineEvent, currentDraft.text) - } - } - is UserDraft.REPLY -> { - room.getTimeLineEvent(currentDraft.linkedEventId)?.let { timelineEvent -> - SendMode.REPLY(timelineEvent, currentDraft.text) - } - } - is UserDraft.EDIT -> { - room.getTimeLineEvent(currentDraft.linkedEventId)?.let { timelineEvent -> - SendMode.EDIT(timelineEvent, currentDraft.text) - } - } - else -> null - } ?: SendMode.REGULAR("", fromSharing = false) - ) - } - } - - private fun handleUserIsTyping(action: RoomDetailAction.UserIsTyping) { - if (vectorPreferences.sendTypingNotifs()) { - if (action.isTyping) { - room.userIsTyping() - } else { - room.userStopsTyping() - } - } - } - private fun handleComposerFocusChange(action: RoomDetailAction.ComposerFocusChange) { // Ensure outbound session keys if (OutboundSessionKeySharingStrategy.WhenTyping == BuildConfig.outboundSessionKeySharingStrategy && room.isEncrypted()) { @@ -748,417 +677,7 @@ class RoomDetailViewModel @AssistedInject constructor( } } -// PRIVATE METHODS ***************************************************************************** - - private fun handleSendMessage(action: RoomDetailAction.SendMessage) { - withState { state -> - when (state.sendMode) { - is SendMode.REGULAR -> { - when (val slashCommandResult = CommandParser.parseSplashCommand(action.text)) { - is ParsedCommand.ErrorNotACommand -> { - // Send the text message to the room - room.sendTextMessage(action.text, autoMarkdown = action.autoMarkdown) - _viewEvents.post(RoomDetailViewEvents.MessageSent) - popDraft() - } - is ParsedCommand.ErrorSyntax -> { - _viewEvents.post(RoomDetailViewEvents.SlashCommandError(slashCommandResult.command)) - } - is ParsedCommand.ErrorEmptySlashCommand -> { - _viewEvents.post(RoomDetailViewEvents.SlashCommandUnknown("/")) - } - is ParsedCommand.ErrorUnknownSlashCommand -> { - _viewEvents.post(RoomDetailViewEvents.SlashCommandUnknown(slashCommandResult.slashCommand)) - } - is ParsedCommand.SendPlainText -> { - // Send the text message to the room, without markdown - room.sendTextMessage(slashCommandResult.message, autoMarkdown = false) - _viewEvents.post(RoomDetailViewEvents.MessageSent) - popDraft() - } - is ParsedCommand.Invite -> { - handleInviteSlashCommand(slashCommandResult) - popDraft() - } - is ParsedCommand.Invite3Pid -> { - handleInvite3pidSlashCommand(slashCommandResult) - popDraft() - } - is ParsedCommand.SetUserPowerLevel -> { - handleSetUserPowerLevel(slashCommandResult) - popDraft() - } - is ParsedCommand.ClearScalarToken -> { - // TODO - _viewEvents.post(RoomDetailViewEvents.SlashCommandNotImplemented) - } - is ParsedCommand.SetMarkdown -> { - vectorPreferences.setMarkdownEnabled(slashCommandResult.enable) - _viewEvents.post(RoomDetailViewEvents.SlashCommandHandled( - if (slashCommandResult.enable) R.string.markdown_has_been_enabled else R.string.markdown_has_been_disabled)) - popDraft() - } - is ParsedCommand.UnbanUser -> { - handleUnbanSlashCommand(slashCommandResult) - popDraft() - } - is ParsedCommand.BanUser -> { - handleBanSlashCommand(slashCommandResult) - popDraft() - } - is ParsedCommand.KickUser -> { - handleKickSlashCommand(slashCommandResult) - popDraft() - } - is ParsedCommand.JoinRoom -> { - handleJoinToAnotherRoomSlashCommand(slashCommandResult) - popDraft() - } - is ParsedCommand.PartRoom -> { - // TODO - _viewEvents.post(RoomDetailViewEvents.SlashCommandNotImplemented) - } - is ParsedCommand.SendEmote -> { - room.sendTextMessage(slashCommandResult.message, msgType = MessageType.MSGTYPE_EMOTE, autoMarkdown = action.autoMarkdown) - _viewEvents.post(RoomDetailViewEvents.SlashCommandHandled()) - popDraft() - } - is ParsedCommand.SendRainbow -> { - slashCommandResult.message.toString().let { - room.sendFormattedTextMessage(it, rainbowGenerator.generate(it)) - } - _viewEvents.post(RoomDetailViewEvents.SlashCommandHandled()) - popDraft() - } - is ParsedCommand.SendRainbowEmote -> { - slashCommandResult.message.toString().let { - room.sendFormattedTextMessage(it, rainbowGenerator.generate(it), MessageType.MSGTYPE_EMOTE) - } - _viewEvents.post(RoomDetailViewEvents.SlashCommandHandled()) - popDraft() - } - is ParsedCommand.SendSpoiler -> { - room.sendFormattedTextMessage( - "[${stringProvider.getString(R.string.spoiler)}](${slashCommandResult.message})", - "${slashCommandResult.message}" - ) - _viewEvents.post(RoomDetailViewEvents.SlashCommandHandled()) - popDraft() - } - is ParsedCommand.SendShrug -> { - val sequence = buildString { - append("¯\\_(ツ)_/¯") - if (slashCommandResult.message.isNotEmpty()) { - append(" ") - append(slashCommandResult.message) - } - } - room.sendTextMessage(sequence) - _viewEvents.post(RoomDetailViewEvents.SlashCommandHandled()) - popDraft() - } - is ParsedCommand.SendChatEffect -> { - sendChatEffect(slashCommandResult) - _viewEvents.post(RoomDetailViewEvents.SlashCommandHandled()) - popDraft() - } - is ParsedCommand.SendPoll -> { - room.sendPoll(slashCommandResult.question, slashCommandResult.options.mapIndexed { index, s -> OptionItem(s, "$index. $s") }) - _viewEvents.post(RoomDetailViewEvents.SlashCommandHandled()) - popDraft() - } - is ParsedCommand.ChangeTopic -> { - handleChangeTopicSlashCommand(slashCommandResult) - popDraft() - } - is ParsedCommand.ChangeDisplayName -> { - handleChangeDisplayNameSlashCommand(slashCommandResult) - popDraft() - } - is ParsedCommand.DiscardSession -> { - if (room.isEncrypted()) { - session.cryptoService().discardOutboundSession(room.roomId) - _viewEvents.post(RoomDetailViewEvents.SlashCommandHandled()) - popDraft() - } else { - _viewEvents.post(RoomDetailViewEvents.SlashCommandHandled()) - _viewEvents.post( - RoomDetailViewEvents - .ShowMessage(stringProvider.getString(R.string.command_description_discard_session_not_handled)) - ) - } - } - is ParsedCommand.CreateSpace -> { - viewModelScope.launch(Dispatchers.IO) { - try { - val params = CreateSpaceParams().apply { - name = slashCommandResult.name - invitedUserIds.addAll(slashCommandResult.invitees) - } - val spaceId = session.spaceService().createSpace(params) - session.spaceService().getSpace(spaceId) - ?.addChildren( - state.roomId, - null, - null, - true - ) - } catch (failure: Throwable) { - _viewEvents.post(RoomDetailViewEvents.SlashCommandResultError(failure)) - } - } - _viewEvents.post(RoomDetailViewEvents.SlashCommandHandled()) - popDraft() - } - is ParsedCommand.AddToSpace -> { - viewModelScope.launch(Dispatchers.IO) { - try { - session.spaceService().getSpace(slashCommandResult.spaceId) - ?.addChildren( - room.roomId, - null, - null, - false - ) - } catch (failure: Throwable) { - _viewEvents.post(RoomDetailViewEvents.SlashCommandResultError(failure)) - } - } - _viewEvents.post(RoomDetailViewEvents.SlashCommandHandled()) - popDraft() - } - is ParsedCommand.JoinSpace -> { - viewModelScope.launch(Dispatchers.IO) { - try { - session.spaceService().joinSpace(slashCommandResult.spaceIdOrAlias) - } catch (failure: Throwable) { - _viewEvents.post(RoomDetailViewEvents.SlashCommandResultError(failure)) - } - } - _viewEvents.post(RoomDetailViewEvents.SlashCommandHandled()) - popDraft() - } - is ParsedCommand.LeaveRoom -> { - viewModelScope.launch(Dispatchers.IO) { - try { - session.getRoom(slashCommandResult.roomId)?.leave(null) - } catch (failure: Throwable) { - _viewEvents.post(RoomDetailViewEvents.SlashCommandResultError(failure)) - } - } - _viewEvents.post(RoomDetailViewEvents.SlashCommandHandled()) - popDraft() - } - is ParsedCommand.UpgradeRoom -> { - _viewEvents.post( - RoomDetailViewEvents.ShowRoomUpgradeDialog( - slashCommandResult.newVersion, - room.roomSummary()?.isPublic ?: false - ) - ) - _viewEvents.post(RoomDetailViewEvents.SlashCommandHandled()) - popDraft() - } - }.exhaustive - } - is SendMode.EDIT -> { - // is original event a reply? - val inReplyTo = state.sendMode.timelineEvent.getRelationContent()?.inReplyTo?.eventId - if (inReplyTo != null) { - // TODO check if same content? - room.getTimeLineEvent(inReplyTo)?.let { - room.editReply(state.sendMode.timelineEvent, it, action.text.toString()) - } - } else { - val messageContent = state.sendMode.timelineEvent.getLastMessageContent() - val existingBody = messageContent?.body ?: "" - if (existingBody != action.text) { - room.editTextMessage(state.sendMode.timelineEvent, - messageContent?.msgType ?: MessageType.MSGTYPE_TEXT, - action.text, - action.autoMarkdown) - } else { - Timber.w("Same message content, do not send edition") - } - } - _viewEvents.post(RoomDetailViewEvents.MessageSent) - popDraft() - } - is SendMode.QUOTE -> { - val messageContent = state.sendMode.timelineEvent.getLastMessageContent() - val textMsg = messageContent?.body - - val finalText = legacyRiotQuoteText(textMsg, action.text.toString()) - - // TODO check for pills? - - // TODO Refactor this, just temporary for quotes - val parser = Parser.builder().build() - val document = parser.parse(finalText) - val renderer = HtmlRenderer.builder().build() - val htmlText = renderer.render(document) - if (finalText == htmlText) { - room.sendTextMessage(finalText) - } else { - room.sendFormattedTextMessage(finalText, htmlText) - } - _viewEvents.post(RoomDetailViewEvents.MessageSent) - popDraft() - } - is SendMode.REPLY -> { - state.sendMode.timelineEvent.let { - room.replyToMessage(it, action.text.toString(), action.autoMarkdown) - _viewEvents.post(RoomDetailViewEvents.MessageSent) - popDraft() - } - } - }.exhaustive - } - } - - private fun sendChatEffect(sendChatEffect: ParsedCommand.SendChatEffect) { - // If message is blank, convert to an emote, with default message - if (sendChatEffect.message.isBlank()) { - val defaultMessage = stringProvider.getString(when (sendChatEffect.chatEffect) { - ChatEffect.CONFETTI -> R.string.default_message_emote_confetti - ChatEffect.SNOWFALL -> R.string.default_message_emote_snow - }) - room.sendTextMessage(defaultMessage, MessageType.MSGTYPE_EMOTE) - } else { - room.sendTextMessage(sendChatEffect.message, sendChatEffect.chatEffect.toMessageType()) - } - } - - private fun popDraft() = withState { - if (it.sendMode is SendMode.REGULAR && it.sendMode.fromSharing) { - // If we were sharing, we want to get back our last value from draft - loadDraftIfAny() - } else { - // Otherwise we clear the composer and remove the draft from db - setState { copy(sendMode = SendMode.REGULAR("", false)) } - viewModelScope.launch { - room.deleteDraft() - } - } - } - - private fun handleJoinToAnotherRoomSlashCommand(command: ParsedCommand.JoinRoom) { - viewModelScope.launch { - try { - session.joinRoom(command.roomAlias, command.reason, emptyList()) - } catch (failure: Throwable) { - _viewEvents.post(RoomDetailViewEvents.SlashCommandResultError(failure)) - return@launch - } - session.getRoomSummary(command.roomAlias) - ?.roomId - ?.let { - _viewEvents.post(RoomDetailViewEvents.JoinRoomCommandSuccess(it)) - } - } - } - - private fun legacyRiotQuoteText(quotedText: String?, myText: String): String { - val messageParagraphs = quotedText?.split("\n\n".toRegex())?.dropLastWhile { it.isEmpty() }?.toTypedArray() - return buildString { - if (messageParagraphs != null) { - for (i in messageParagraphs.indices) { - if (messageParagraphs[i].isNotBlank()) { - append("> ") - append(messageParagraphs[i]) - } - - if (i != messageParagraphs.lastIndex) { - append("\n\n") - } - } - } - append("\n\n") - append(myText) - } - } - - private fun handleChangeTopicSlashCommand(changeTopic: ParsedCommand.ChangeTopic) { - launchSlashCommandFlowSuspendable { - room.updateTopic(changeTopic.topic) - } - } - - private fun handleInviteSlashCommand(invite: ParsedCommand.Invite) { - launchSlashCommandFlowSuspendable { - room.invite(invite.userId, invite.reason) - } - } - - private fun handleInvite3pidSlashCommand(invite: ParsedCommand.Invite3Pid) { - launchSlashCommandFlowSuspendable { - room.invite3pid(invite.threePid) - } - } - - private fun handleSetUserPowerLevel(setUserPowerLevel: ParsedCommand.SetUserPowerLevel) { - val newPowerLevelsContent = room.getStateEvent(EventType.STATE_ROOM_POWER_LEVELS) - ?.content - ?.toModel() - ?.setUserPowerLevel(setUserPowerLevel.userId, setUserPowerLevel.powerLevel) - ?.toContent() - ?: return - - launchSlashCommandFlowSuspendable { - room.sendStateEvent(EventType.STATE_ROOM_POWER_LEVELS, null, newPowerLevelsContent) - } - } - - private fun handleChangeDisplayNameSlashCommand(changeDisplayName: ParsedCommand.ChangeDisplayName) { - launchSlashCommandFlowSuspendable { - session.setDisplayName(session.myUserId, changeDisplayName.displayName) - } - } - - private fun handleKickSlashCommand(kick: ParsedCommand.KickUser) { - launchSlashCommandFlowSuspendable { - room.kick(kick.userId, kick.reason) - } - } - - private fun handleBanSlashCommand(ban: ParsedCommand.BanUser) { - launchSlashCommandFlowSuspendable { - room.ban(ban.userId, ban.reason) - } - } - - private fun handleUnbanSlashCommand(unban: ParsedCommand.UnbanUser) { - launchSlashCommandFlowSuspendable { - room.unban(unban.userId, unban.reason) - } - } - - private fun launchSlashCommandFlow(lambda: (MatrixCallback) -> Unit) { - _viewEvents.post(RoomDetailViewEvents.SlashCommandHandled()) - val matrixCallback = object : MatrixCallback { - override fun onSuccess(data: Unit) { - _viewEvents.post(RoomDetailViewEvents.SlashCommandResultOk) - } - - override fun onFailure(failure: Throwable) { - _viewEvents.post(RoomDetailViewEvents.SlashCommandResultError(failure)) - } - } - lambda.invoke(matrixCallback) - } - - private fun launchSlashCommandFlowSuspendable(block: suspend () -> Unit) { - _viewEvents.post(RoomDetailViewEvents.SlashCommandHandled()) - viewModelScope.launch { - val event = try { - block() - RoomDetailViewEvents.SlashCommandResultOk - } catch (failure: Exception) { - RoomDetailViewEvents.SlashCommandResultError(failure) - } - _viewEvents.post(event) - } - } + // PRIVATE METHODS ***************************************************************************** private fun handleSendReaction(action: RoomDetailAction.SendReaction) { room.sendReaction(action.targetEventId, action.reaction) @@ -1228,32 +747,10 @@ class RoomDetailViewModel @AssistedInject constructor( } } - private fun handleEditAction(action: RoomDetailAction.EnterEditMode) { - room.getTimeLineEvent(action.eventId)?.let { timelineEvent -> - setState { copy(sendMode = SendMode.EDIT(timelineEvent, timelineEvent.getTextEditableContent() ?: "")) } - } - } - - private fun handleQuoteAction(action: RoomDetailAction.EnterQuoteMode) { - room.getTimeLineEvent(action.eventId)?.let { timelineEvent -> - setState { copy(sendMode = SendMode.QUOTE(timelineEvent, action.text)) } - } - } - - private fun handleReplyAction(action: RoomDetailAction.EnterReplyMode) { - room.getTimeLineEvent(action.eventId)?.let { timelineEvent -> - setState { copy(sendMode = SendMode.REPLY(timelineEvent, action.text)) } - } - } - - private fun handleEnterRegularMode(action: RoomDetailAction.EnterRegularMode) = setState { - copy(sendMode = SendMode.REGULAR(action.text, action.fromSharing)) - } - private fun handleOpenOrDownloadFile(action: RoomDetailAction.DownloadOrOpen) { val mxcUrl = action.messageFileContent.getFileUrl() ?: return - val isLocalSendingFile = action.senderId == session.myUserId - && mxcUrl.startsWith("content://") + val isLocalSendingFile = action.senderId == session.myUserId && + mxcUrl.startsWith("content://") if (isLocalSendingFile) { tryOrNull { Uri.parse(mxcUrl) }?.let { _viewEvents.post(RoomDetailViewEvents.OpenFile( @@ -1493,6 +990,17 @@ class RoomDetailViewModel @AssistedInject constructor( } } .disposeOnClear() + + session.getSyncStatusLive() + .asObservable() + .subscribe { it -> + if (it is SyncStatusService.Status.IncrementalSyncStatus) { + setState { + copy(incrementalSyncStatus = it) + } + } + } + .disposeOnClear() } private fun observeRoomSummary() { @@ -1575,7 +1083,7 @@ class RoomDetailViewModel @AssistedInject constructor( setState { val typingMessage = typingHelper.getTypingMessage(summary.typingUsers) copy( - typingMessage = typingMessage, + formattedTypingUsers = typingMessage, hasFailedSending = summary.hasFailedSending ) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt index 1c75429d112..4c6e4ac3fe0 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewState.kt @@ -21,35 +21,14 @@ import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.Uninitialized import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.events.model.Event +import org.matrix.android.sdk.api.session.initsync.SyncStatusService import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary import org.matrix.android.sdk.api.session.room.model.RoomSummary -import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import org.matrix.android.sdk.api.session.sync.SyncState import org.matrix.android.sdk.api.session.widgets.model.Widget import org.matrix.android.sdk.api.session.widgets.model.WidgetType -/** - * Describes the current send mode: - * REGULAR: sends the text as a regular message - * QUOTE: User is currently quoting a message - * EDIT: User is currently editing an existing message - * - * Depending on the state the bottom toolbar will change (icons/preview/actions...) - */ -sealed class SendMode(open val text: String) { - data class REGULAR( - override val text: String, - val fromSharing: Boolean, - // This is necessary for forcing refresh on selectSubscribe - private val ts: Long = System.currentTimeMillis() - ) : SendMode(text) - - data class QUOTE(val timelineEvent: TimelineEvent, override val text: String) : SendMode(text) - data class EDIT(val timelineEvent: TimelineEvent, override val text: String) : SendMode(text) - data class REPLY(val timelineEvent: TimelineEvent, override val text: String) : SendMode(text) -} - sealed class UnreadState { object Unknown : UnreadState() object HasNoUnread : UnreadState() @@ -72,16 +51,16 @@ data class RoomDetailViewState( val asyncInviter: Async = Uninitialized, val asyncRoomSummary: Async = Uninitialized, val activeRoomWidgets: Async> = Uninitialized, - val typingMessage: String? = null, - val sendMode: SendMode = SendMode.REGULAR("", false), + val formattedTypingUsers: String? = null, val tombstoneEvent: Event? = null, val joinUpgradedRoomAsync: Async = Uninitialized, val syncState: SyncState = SyncState.Idle, + val incrementalSyncStatus: SyncStatusService.Status.IncrementalSyncStatus = SyncStatusService.Status.IncrementalSyncIdle, + val pushCounter: Int = 0, val highlightedEventId: String? = null, val unreadState: UnreadState = UnreadState.Unknown, val canShowJumpToReadMarker: Boolean = true, val changeMembershipState: ChangeMembershipState = ChangeMembershipState.Unknown, - val canSendMessage: Boolean = true, val canInvite: Boolean = true, val isAllowedToManageWidgets: Boolean = false, val isAllowedToStartWebRTCCall: Boolean = true, diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/ComposerEditText.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/ComposerEditText.kt index 79ff7be4411..232323789ce 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/ComposerEditText.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/ComposerEditText.kt @@ -32,16 +32,15 @@ import im.vector.app.core.platform.SimpleTextWatcher import im.vector.app.features.html.PillImageSpan import timber.log.Timber -class ComposerEditText @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = android.R.attr.editTextStyle) - : EmojiEditText(context, attrs, defStyleAttr) { +class ComposerEditText @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = android.R.attr.editTextStyle) : + EmojiEditText(context, attrs, defStyleAttr) { interface Callback { fun onRichContentSelected(contentUri: Uri): Boolean - fun onTextBlankStateChanged(isBlank: Boolean) + fun onTextChanged(text: CharSequence) } var callback: Callback? = null - private var isBlankText = true override fun onCreateInputConnection(editorInfo: EditorInfo): InputConnection? { val ic = super.onCreateInputConnection(editorInfo) ?: return null @@ -95,11 +94,7 @@ class ComposerEditText @JvmOverloads constructor(context: Context, attrs: Attrib } spanToRemove = null } - // Report blank status of EditText to be able to arrange other elements of the composer - if (s.isBlank() != isBlankText) { - isBlankText = !isBlankText - callback?.onTextBlankStateChanged(isBlankText) - } + callback?.onTextChanged(s.toString()) } } ) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerAction.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerAction.kt new file mode 100644 index 00000000000..77254001875 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerAction.kt @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.home.room.detail.composer + +import im.vector.app.core.platform.VectorViewModelAction + +sealed class TextComposerAction : VectorViewModelAction { + data class SaveDraft(val draft: String) : TextComposerAction() + data class SendMessage(val text: CharSequence, val autoMarkdown: Boolean) : TextComposerAction() + data class EnterEditMode(val eventId: String, val text: String) : TextComposerAction() + data class EnterQuoteMode(val eventId: String, val text: String) : TextComposerAction() + data class EnterReplyMode(val eventId: String, val text: String) : TextComposerAction() + data class EnterRegularMode(val text: String, val fromSharing: Boolean) : TextComposerAction() + data class UserIsTyping(val isTyping: Boolean) : TextComposerAction() + data class OnTextChanged(val text: CharSequence) : TextComposerAction() + data class OnVoiceRecordingStateChanged(val isRecording: Boolean) : TextComposerAction() +} diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerView.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerView.kt index eb935f9e754..34b3c1777cb 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerView.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerView.kt @@ -24,17 +24,14 @@ import android.view.ViewGroup import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet import androidx.core.text.toSpannable -import androidx.core.view.isInvisible -import androidx.core.view.isVisible -import androidx.transition.AutoTransition import androidx.transition.ChangeBounds import androidx.transition.Fade import androidx.transition.Transition import androidx.transition.TransitionManager import androidx.transition.TransitionSet import im.vector.app.R +import im.vector.app.core.extensions.setTextIfDifferent import im.vector.app.databinding.ComposerLayoutBinding -import org.matrix.android.sdk.api.extensions.orFalse /** * Encapsulate the timeline composer UX. @@ -61,13 +58,6 @@ class TextComposerView @JvmOverloads constructor( val text: Editable? get() = views.composerEditText.text - var alwaysShowSendButton = false - set(value) { - field = value - val shouldShowSendButton = currentConstraintSetId == R.layout.composer_layout_constraint_set_expanded || text?.isNotBlank().orFalse() || value - views.sendButton.isInvisible = !shouldShowSendButton - } - init { inflate(context, R.layout.composer_layout, this) views = ComposerLayoutBinding.bind(this) @@ -79,17 +69,8 @@ class TextComposerView @JvmOverloads constructor( return callback?.onRichContentSelected(contentUri) ?: false } - override fun onTextBlankStateChanged(isBlank: Boolean) { - val shouldShowSendButton = currentConstraintSetId == R.layout.composer_layout_constraint_set_expanded || !isBlank || alwaysShowSendButton - TransitionManager.endTransitions(this@TextComposerView) - if (views.sendButton.isVisible != shouldShowSendButton) { - TransitionManager.beginDelayedTransition( - this@TextComposerView, - AutoTransition().also { it.duration = 150 } - ) - views.sendButton.isInvisible = !shouldShowSendButton - } - callback?.onTextBlankStateChanged(isBlank) + override fun onTextChanged(text: CharSequence) { + callback?.onTextChanged(text) } } views.composerRelatedMessageCloseButton.setOnClickListener { @@ -114,9 +95,6 @@ class TextComposerView @JvmOverloads constructor( } currentConstraintSetId = R.layout.composer_layout_constraint_set_compact applyNewConstraintSet(animate, transitionComplete) - - val shouldShowSendButton = !views.composerEditText.text.isNullOrEmpty() || alwaysShowSendButton - views.sendButton.isInvisible = !shouldShowSendButton } fun expand(animate: Boolean = true, transitionComplete: (() -> Unit)? = null) { @@ -126,10 +104,14 @@ class TextComposerView @JvmOverloads constructor( } currentConstraintSetId = R.layout.composer_layout_constraint_set_expanded applyNewConstraintSet(animate, transitionComplete) - views.sendButton.isInvisible = false + } + + fun setTextIfDifferent(text: CharSequence?): Boolean { + return views.composerEditText.setTextIfDifferent(text) } private fun applyNewConstraintSet(animate: Boolean, transitionComplete: (() -> Unit)?) { + // val wasSendButtonInvisible = views.sendButton.isInvisible if (animate) { configureAndBeginTransition(transitionComplete) } @@ -137,6 +119,8 @@ class TextComposerView @JvmOverloads constructor( it.clone(context, currentConstraintSetId) it.applyTo(this) } + // Might be updated by view state just after, but avoid blinks + // views.sendButton.isInvisible = wasSendButtonInvisible } private fun configureAndBeginTransition(transitionComplete: (() -> Unit)? = null) { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerViewEvents.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerViewEvents.kt new file mode 100644 index 00000000000..691ed4d93e0 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerViewEvents.kt @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.home.room.detail.composer + +import androidx.annotation.StringRes +import im.vector.app.core.platform.VectorViewEvents +import im.vector.app.features.command.Command + +sealed class TextComposerViewEvents : VectorViewEvents { + + data class AnimateSendButtonVisibility(val isVisible: Boolean) : TextComposerViewEvents() + + data class ShowMessage(val message: String) : TextComposerViewEvents() + + abstract class SendMessageResult : TextComposerViewEvents() + + object MessageSent : SendMessageResult() + data class JoinRoomCommandSuccess(val roomId: String) : SendMessageResult() + class SlashCommandError(val command: Command) : SendMessageResult() + class SlashCommandUnknown(val command: String) : SendMessageResult() + data class SlashCommandHandled(@StringRes val messageRes: Int? = null) : SendMessageResult() + object SlashCommandResultOk : SendMessageResult() + class SlashCommandResultError(val throwable: Throwable) : SendMessageResult() + + data class OpenRoomMemberProfile(val userId: String) : TextComposerViewEvents() + + // TODO Remove + object SlashCommandNotImplemented : SendMessageResult() + + data class ShowRoomUpgradeDialog(val newVersion: String, val isPublic: Boolean) : TextComposerViewEvents() +} diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerViewModel.kt new file mode 100644 index 00000000000..35871611c09 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerViewModel.kt @@ -0,0 +1,722 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.home.room.detail.composer + +import androidx.lifecycle.viewModelScope +import com.airbnb.mvrx.FragmentViewModelContext +import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.ViewModelContext +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import im.vector.app.R +import im.vector.app.core.extensions.exhaustive +import im.vector.app.core.platform.VectorViewModel +import im.vector.app.core.resources.StringProvider +import im.vector.app.features.command.CommandParser +import im.vector.app.features.command.ParsedCommand +import im.vector.app.features.home.room.detail.ChatEffect +import im.vector.app.features.home.room.detail.RoomDetailFragment +import im.vector.app.features.home.room.detail.composer.rainbow.RainbowGenerator +import im.vector.app.features.home.room.detail.toMessageType +import im.vector.app.features.powerlevel.PowerLevelsObservableFactory +import im.vector.app.features.session.coroutineScope +import im.vector.app.features.settings.VectorPreferences +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import org.commonmark.parser.Parser +import org.commonmark.renderer.html.HtmlRenderer +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.RoomAvatarContent +import org.matrix.android.sdk.api.session.room.model.RoomMemberContent +import org.matrix.android.sdk.api.session.room.model.message.MessageType +import org.matrix.android.sdk.api.session.room.model.message.OptionItem +import org.matrix.android.sdk.api.session.room.powerlevels.PowerLevelsHelper +import org.matrix.android.sdk.api.session.room.send.UserDraft +import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent +import org.matrix.android.sdk.api.session.room.timeline.getRelationContent +import org.matrix.android.sdk.api.session.room.timeline.getTextEditableContent +import org.matrix.android.sdk.api.session.space.CreateSpaceParams +import timber.log.Timber + +class TextComposerViewModel @AssistedInject constructor( + @Assisted initialState: TextComposerViewState, + private val session: Session, + private val stringProvider: StringProvider, + private val vectorPreferences: VectorPreferences, + private val rainbowGenerator: RainbowGenerator +) : VectorViewModel(initialState) { + + private val room = session.getRoom(initialState.roomId)!! + + // Keep it out of state to avoid invalidate being called + private var currentComposerText: CharSequence = "" + + init { + loadDraftIfAny() + observePowerLevel() + subscribeToStateInternal() + } + + override fun handle(action: TextComposerAction) { + Timber.v("Handle action: $action") + when (action) { + is TextComposerAction.EnterEditMode -> handleEnterEditMode(action) + is TextComposerAction.EnterQuoteMode -> handleEnterQuoteMode(action) + is TextComposerAction.EnterRegularMode -> handleEnterRegularMode(action) + is TextComposerAction.EnterReplyMode -> handleEnterReplyMode(action) + is TextComposerAction.SaveDraft -> handleSaveDraft(action) + is TextComposerAction.SendMessage -> handleSendMessage(action) + is TextComposerAction.UserIsTyping -> handleUserIsTyping(action) + is TextComposerAction.OnTextChanged -> handleOnTextChanged(action) + is TextComposerAction.OnVoiceRecordingStateChanged -> handleOnVoiceRecordingStateChanged(action) + } + } + + private fun handleOnVoiceRecordingStateChanged(action: TextComposerAction.OnVoiceRecordingStateChanged) = setState { + copy(isVoiceRecording = action.isRecording) + } + + private fun handleOnTextChanged(action: TextComposerAction.OnTextChanged) { + setState { + // Makes sure currentComposerText is upToDate when accessing further setState + currentComposerText = action.text + this + } + updateIsSendButtonVisibility(true) + } + + private fun subscribeToStateInternal() { + selectSubscribe(TextComposerViewState::sendMode, TextComposerViewState::canSendMessage, TextComposerViewState::isVoiceRecording) { _, _, _ -> + updateIsSendButtonVisibility(false) + } + } + + private fun updateIsSendButtonVisibility(triggerAnimation: Boolean) = setState { + val isSendButtonVisible = isComposerVisible && (sendMode !is SendMode.REGULAR || currentComposerText.isNotBlank()) + if (this.isSendButtonVisible != isSendButtonVisible && triggerAnimation) { + _viewEvents.post(TextComposerViewEvents.AnimateSendButtonVisibility(isSendButtonVisible)) + } + copy(isSendButtonVisible = isSendButtonVisible) + } + + private fun handleEnterRegularMode(action: TextComposerAction.EnterRegularMode) = setState { + copy(sendMode = SendMode.REGULAR(action.text, action.fromSharing)) + } + + private fun handleEnterEditMode(action: TextComposerAction.EnterEditMode) { + room.getTimeLineEvent(action.eventId)?.let { timelineEvent -> + setState { copy(sendMode = SendMode.EDIT(timelineEvent, timelineEvent.getTextEditableContent() ?: "")) } + } + } + + private fun observePowerLevel() { + PowerLevelsObservableFactory(room).createObservable() + .subscribe { + val canSendMessage = PowerLevelsHelper(it).isUserAllowedToSend(session.myUserId, false, EventType.MESSAGE) + setState { + copy(canSendMessage = canSendMessage) + } + } + .disposeOnClear() + } + + private fun handleEnterQuoteMode(action: TextComposerAction.EnterQuoteMode) { + room.getTimeLineEvent(action.eventId)?.let { timelineEvent -> + setState { copy(sendMode = SendMode.QUOTE(timelineEvent, action.text)) } + } + } + + private fun handleEnterReplyMode(action: TextComposerAction.EnterReplyMode) { + room.getTimeLineEvent(action.eventId)?.let { timelineEvent -> + setState { copy(sendMode = SendMode.REPLY(timelineEvent, action.text)) } + } + } + + private fun handleSendMessage(action: TextComposerAction.SendMessage) { + withState { state -> + when (state.sendMode) { + is SendMode.REGULAR -> { + when (val slashCommandResult = CommandParser.parseSplashCommand(action.text)) { + is ParsedCommand.ErrorNotACommand -> { + // Send the text message to the room + room.sendTextMessage(action.text, autoMarkdown = action.autoMarkdown) + _viewEvents.post(TextComposerViewEvents.MessageSent) + popDraft() + } + is ParsedCommand.ErrorSyntax -> { + _viewEvents.post(TextComposerViewEvents.SlashCommandError(slashCommandResult.command)) + } + is ParsedCommand.ErrorEmptySlashCommand -> { + _viewEvents.post(TextComposerViewEvents.SlashCommandUnknown("/")) + } + is ParsedCommand.ErrorUnknownSlashCommand -> { + _viewEvents.post(TextComposerViewEvents.SlashCommandUnknown(slashCommandResult.slashCommand)) + } + is ParsedCommand.SendPlainText -> { + // Send the text message to the room, without markdown + room.sendTextMessage(slashCommandResult.message, autoMarkdown = false) + _viewEvents.post(TextComposerViewEvents.MessageSent) + popDraft() + } + is ParsedCommand.ChangeRoomName -> { + handleChangeRoomNameSlashCommand(slashCommandResult) + popDraft() + } + is ParsedCommand.Invite -> { + handleInviteSlashCommand(slashCommandResult) + popDraft() + } + is ParsedCommand.Invite3Pid -> { + handleInvite3pidSlashCommand(slashCommandResult) + popDraft() + } + is ParsedCommand.SetUserPowerLevel -> { + handleSetUserPowerLevel(slashCommandResult) + popDraft() + } + is ParsedCommand.ClearScalarToken -> { + // TODO + _viewEvents.post(TextComposerViewEvents.SlashCommandNotImplemented) + } + is ParsedCommand.SetMarkdown -> { + vectorPreferences.setMarkdownEnabled(slashCommandResult.enable) + _viewEvents.post(TextComposerViewEvents.SlashCommandHandled( + if (slashCommandResult.enable) R.string.markdown_has_been_enabled else R.string.markdown_has_been_disabled)) + popDraft() + } + is ParsedCommand.BanUser -> { + handleBanSlashCommand(slashCommandResult) + popDraft() + } + is ParsedCommand.UnbanUser -> { + handleUnbanSlashCommand(slashCommandResult) + popDraft() + } + is ParsedCommand.IgnoreUser -> { + handleIgnoreSlashCommand(slashCommandResult) + popDraft() + } + is ParsedCommand.UnignoreUser -> { + handleUnignoreSlashCommand(slashCommandResult) + popDraft() + } + is ParsedCommand.KickUser -> { + handleKickSlashCommand(slashCommandResult) + popDraft() + } + is ParsedCommand.JoinRoom -> { + handleJoinToAnotherRoomSlashCommand(slashCommandResult) + popDraft() + } + is ParsedCommand.PartRoom -> { + // TODO + _viewEvents.post(TextComposerViewEvents.SlashCommandNotImplemented) + } + is ParsedCommand.SendEmote -> { + room.sendTextMessage(slashCommandResult.message, msgType = MessageType.MSGTYPE_EMOTE, autoMarkdown = action.autoMarkdown) + _viewEvents.post(TextComposerViewEvents.SlashCommandHandled()) + popDraft() + } + is ParsedCommand.SendRainbow -> { + slashCommandResult.message.toString().let { + room.sendFormattedTextMessage(it, rainbowGenerator.generate(it)) + } + _viewEvents.post(TextComposerViewEvents.SlashCommandHandled()) + popDraft() + } + is ParsedCommand.SendRainbowEmote -> { + slashCommandResult.message.toString().let { + room.sendFormattedTextMessage(it, rainbowGenerator.generate(it), MessageType.MSGTYPE_EMOTE) + } + _viewEvents.post(TextComposerViewEvents.SlashCommandHandled()) + popDraft() + } + is ParsedCommand.SendSpoiler -> { + room.sendFormattedTextMessage( + "[${stringProvider.getString(R.string.spoiler)}](${slashCommandResult.message})", + "${slashCommandResult.message}" + ) + _viewEvents.post(TextComposerViewEvents.SlashCommandHandled()) + popDraft() + } + is ParsedCommand.SendShrug -> { + sendPrefixedMessage("¯\\_(ツ)_/¯", slashCommandResult.message) + _viewEvents.post(TextComposerViewEvents.SlashCommandHandled()) + popDraft() + } + is ParsedCommand.SendLenny -> { + sendPrefixedMessage("( ͡° ͜ʖ ͡°)", slashCommandResult.message) + _viewEvents.post(TextComposerViewEvents.SlashCommandHandled()) + popDraft() + } + is ParsedCommand.SendChatEffect -> { + sendChatEffect(slashCommandResult) + _viewEvents.post(TextComposerViewEvents.SlashCommandHandled()) + popDraft() + } + is ParsedCommand.SendPoll -> { + room.sendPoll(slashCommandResult.question, slashCommandResult.options.mapIndexed { index, s -> OptionItem(s, "$index. $s") }) + _viewEvents.post(TextComposerViewEvents.SlashCommandHandled()) + popDraft() + } + is ParsedCommand.ChangeTopic -> { + handleChangeTopicSlashCommand(slashCommandResult) + popDraft() + } + is ParsedCommand.ChangeDisplayName -> { + handleChangeDisplayNameSlashCommand(slashCommandResult) + popDraft() + } + is ParsedCommand.ChangeDisplayNameForRoom -> { + handleChangeDisplayNameForRoomSlashCommand(slashCommandResult) + popDraft() + } + is ParsedCommand.ChangeRoomAvatar -> { + handleChangeRoomAvatarSlashCommand(slashCommandResult) + popDraft() + } + is ParsedCommand.ChangeAvatarForRoom -> { + handleChangeAvatarForRoomSlashCommand(slashCommandResult) + popDraft() + } + is ParsedCommand.ShowUser -> { + _viewEvents.post(TextComposerViewEvents.SlashCommandHandled()) + handleWhoisSlashCommand(slashCommandResult) + popDraft() + } + is ParsedCommand.DiscardSession -> { + if (room.isEncrypted()) { + session.cryptoService().discardOutboundSession(room.roomId) + _viewEvents.post(TextComposerViewEvents.SlashCommandHandled()) + popDraft() + } else { + _viewEvents.post(TextComposerViewEvents.SlashCommandHandled()) + _viewEvents.post( + TextComposerViewEvents + .ShowMessage(stringProvider.getString(R.string.command_description_discard_session_not_handled)) + ) + } + } + is ParsedCommand.CreateSpace -> { + viewModelScope.launch(Dispatchers.IO) { + try { + val params = CreateSpaceParams().apply { + name = slashCommandResult.name + invitedUserIds.addAll(slashCommandResult.invitees) + } + val spaceId = session.spaceService().createSpace(params) + session.spaceService().getSpace(spaceId) + ?.addChildren( + state.roomId, + null, + null, + true + ) + } catch (failure: Throwable) { + _viewEvents.post(TextComposerViewEvents.SlashCommandResultError(failure)) + } + } + _viewEvents.post(TextComposerViewEvents.SlashCommandHandled()) + popDraft() + } + is ParsedCommand.AddToSpace -> { + viewModelScope.launch(Dispatchers.IO) { + try { + session.spaceService().getSpace(slashCommandResult.spaceId) + ?.addChildren( + room.roomId, + null, + null, + false + ) + } catch (failure: Throwable) { + _viewEvents.post(TextComposerViewEvents.SlashCommandResultError(failure)) + } + } + _viewEvents.post(TextComposerViewEvents.SlashCommandHandled()) + popDraft() + } + is ParsedCommand.JoinSpace -> { + viewModelScope.launch(Dispatchers.IO) { + try { + session.spaceService().joinSpace(slashCommandResult.spaceIdOrAlias) + } catch (failure: Throwable) { + _viewEvents.post(TextComposerViewEvents.SlashCommandResultError(failure)) + } + } + _viewEvents.post(TextComposerViewEvents.SlashCommandHandled()) + popDraft() + } + is ParsedCommand.LeaveRoom -> { + viewModelScope.launch(Dispatchers.IO) { + try { + session.getRoom(slashCommandResult.roomId)?.leave(null) + } catch (failure: Throwable) { + _viewEvents.post(TextComposerViewEvents.SlashCommandResultError(failure)) + } + } + _viewEvents.post(TextComposerViewEvents.SlashCommandHandled()) + popDraft() + } + is ParsedCommand.UpgradeRoom -> { + _viewEvents.post( + TextComposerViewEvents.ShowRoomUpgradeDialog( + slashCommandResult.newVersion, + room.roomSummary()?.isPublic ?: false + ) + ) + _viewEvents.post(TextComposerViewEvents.SlashCommandHandled()) + popDraft() + } + }.exhaustive + } + is SendMode.EDIT -> { + // is original event a reply? + val inReplyTo = state.sendMode.timelineEvent.getRelationContent()?.inReplyTo?.eventId + if (inReplyTo != null) { + // TODO check if same content? + room.getTimeLineEvent(inReplyTo)?.let { + room.editReply(state.sendMode.timelineEvent, it, action.text.toString()) + } + } else { + val messageContent = state.sendMode.timelineEvent.getLastMessageContent() + val existingBody = messageContent?.body ?: "" + if (existingBody != action.text) { + room.editTextMessage(state.sendMode.timelineEvent, + messageContent?.msgType ?: MessageType.MSGTYPE_TEXT, + action.text, + action.autoMarkdown) + } else { + Timber.w("Same message content, do not send edition") + } + } + _viewEvents.post(TextComposerViewEvents.MessageSent) + popDraft() + } + is SendMode.QUOTE -> { + val messageContent = state.sendMode.timelineEvent.getLastMessageContent() + val textMsg = messageContent?.body + + val finalText = legacyRiotQuoteText(textMsg, action.text.toString()) + + // TODO check for pills? + + // TODO Refactor this, just temporary for quotes + val parser = Parser.builder().build() + val document = parser.parse(finalText) + val renderer = HtmlRenderer.builder().build() + val htmlText = renderer.render(document) + if (finalText == htmlText) { + room.sendTextMessage(finalText) + } else { + room.sendFormattedTextMessage(finalText, htmlText) + } + _viewEvents.post(TextComposerViewEvents.MessageSent) + popDraft() + } + is SendMode.REPLY -> { + state.sendMode.timelineEvent.let { + room.replyToMessage(it, action.text.toString(), action.autoMarkdown) + _viewEvents.post(TextComposerViewEvents.MessageSent) + popDraft() + } + } + }.exhaustive + } + } + + private fun popDraft() = withState { + if (it.sendMode is SendMode.REGULAR && it.sendMode.fromSharing) { + // If we were sharing, we want to get back our last value from draft + loadDraftIfAny() + } else { + // Otherwise we clear the composer and remove the draft from db + setState { copy(sendMode = SendMode.REGULAR("", false)) } + viewModelScope.launch { + room.deleteDraft() + } + } + } + + private fun loadDraftIfAny() { + val currentDraft = room.getDraft() + setState { + copy( + // Create a sendMode from a draft and retrieve the TimelineEvent + sendMode = when (currentDraft) { + is UserDraft.REGULAR -> SendMode.REGULAR(currentDraft.text, false) + is UserDraft.QUOTE -> { + room.getTimeLineEvent(currentDraft.linkedEventId)?.let { timelineEvent -> + SendMode.QUOTE(timelineEvent, currentDraft.text) + } + } + is UserDraft.REPLY -> { + room.getTimeLineEvent(currentDraft.linkedEventId)?.let { timelineEvent -> + SendMode.REPLY(timelineEvent, currentDraft.text) + } + } + is UserDraft.EDIT -> { + room.getTimeLineEvent(currentDraft.linkedEventId)?.let { timelineEvent -> + SendMode.EDIT(timelineEvent, currentDraft.text) + } + } + else -> null + } ?: SendMode.REGULAR("", fromSharing = false) + ) + } + } + + private fun handleUserIsTyping(action: TextComposerAction.UserIsTyping) { + if (vectorPreferences.sendTypingNotifs()) { + if (action.isTyping) { + room.userIsTyping() + } else { + room.userStopsTyping() + } + } + } + + private fun sendChatEffect(sendChatEffect: ParsedCommand.SendChatEffect) { + // If message is blank, convert to an emote, with default message + if (sendChatEffect.message.isBlank()) { + val defaultMessage = stringProvider.getString(when (sendChatEffect.chatEffect) { + ChatEffect.CONFETTI -> R.string.default_message_emote_confetti + ChatEffect.SNOWFALL -> R.string.default_message_emote_snow + }) + room.sendTextMessage(defaultMessage, MessageType.MSGTYPE_EMOTE) + } else { + room.sendTextMessage(sendChatEffect.message, sendChatEffect.chatEffect.toMessageType()) + } + } + + private fun handleJoinToAnotherRoomSlashCommand(command: ParsedCommand.JoinRoom) { + viewModelScope.launch { + try { + session.joinRoom(command.roomAlias, command.reason, emptyList()) + } catch (failure: Throwable) { + _viewEvents.post(TextComposerViewEvents.SlashCommandResultError(failure)) + return@launch + } + session.getRoomSummary(command.roomAlias) + ?.roomId + ?.let { + _viewEvents.post(TextComposerViewEvents.JoinRoomCommandSuccess(it)) + } + } + } + + private fun legacyRiotQuoteText(quotedText: String?, myText: String): String { + val messageParagraphs = quotedText?.split("\n\n".toRegex())?.dropLastWhile { it.isEmpty() }?.toTypedArray() + return buildString { + if (messageParagraphs != null) { + for (i in messageParagraphs.indices) { + if (messageParagraphs[i].isNotBlank()) { + append("> ") + append(messageParagraphs[i]) + } + + if (i != messageParagraphs.lastIndex) { + append("\n\n") + } + } + } + append("\n\n") + append(myText) + } + } + + private fun handleChangeTopicSlashCommand(changeTopic: ParsedCommand.ChangeTopic) { + launchSlashCommandFlowSuspendable { + room.updateTopic(changeTopic.topic) + } + } + + private fun handleInviteSlashCommand(invite: ParsedCommand.Invite) { + launchSlashCommandFlowSuspendable { + room.invite(invite.userId, invite.reason) + } + } + + private fun handleInvite3pidSlashCommand(invite: ParsedCommand.Invite3Pid) { + launchSlashCommandFlowSuspendable { + room.invite3pid(invite.threePid) + } + } + + private fun handleSetUserPowerLevel(setUserPowerLevel: ParsedCommand.SetUserPowerLevel) { + val newPowerLevelsContent = room.getStateEvent(EventType.STATE_ROOM_POWER_LEVELS) + ?.content + ?.toModel() + ?.setUserPowerLevel(setUserPowerLevel.userId, setUserPowerLevel.powerLevel) + ?.toContent() + ?: return + + launchSlashCommandFlowSuspendable { + room.sendStateEvent(EventType.STATE_ROOM_POWER_LEVELS, null, newPowerLevelsContent) + } + } + + private fun handleChangeDisplayNameSlashCommand(changeDisplayName: ParsedCommand.ChangeDisplayName) { + launchSlashCommandFlowSuspendable { + session.setDisplayName(session.myUserId, changeDisplayName.displayName) + } + } + + private fun handleKickSlashCommand(kick: ParsedCommand.KickUser) { + launchSlashCommandFlowSuspendable { + room.kick(kick.userId, kick.reason) + } + } + + private fun handleBanSlashCommand(ban: ParsedCommand.BanUser) { + launchSlashCommandFlowSuspendable { + room.ban(ban.userId, ban.reason) + } + } + + private fun handleUnbanSlashCommand(unban: ParsedCommand.UnbanUser) { + launchSlashCommandFlowSuspendable { + room.unban(unban.userId, unban.reason) + } + } + + private fun handleChangeRoomNameSlashCommand(changeRoomName: ParsedCommand.ChangeRoomName) { + launchSlashCommandFlowSuspendable { + room.updateName(changeRoomName.name) + } + } + + private fun getMyRoomMemberContent(): RoomMemberContent? { + return room.getStateEvent(EventType.STATE_ROOM_MEMBER, QueryStringValue.Equals(session.myUserId)) + ?.content + ?.toModel() + } + + private fun handleChangeDisplayNameForRoomSlashCommand(changeDisplayName: ParsedCommand.ChangeDisplayNameForRoom) { + launchSlashCommandFlowSuspendable { + getMyRoomMemberContent() + ?.copy(displayName = changeDisplayName.displayName) + ?.toContent() + ?.let { + room.sendStateEvent(EventType.STATE_ROOM_MEMBER, session.myUserId, it) + } + } + } + + private fun handleChangeRoomAvatarSlashCommand(changeAvatar: ParsedCommand.ChangeRoomAvatar) { + launchSlashCommandFlowSuspendable { + room.sendStateEvent(EventType.STATE_ROOM_AVATAR, null, RoomAvatarContent(changeAvatar.url).toContent()) + } + } + + private fun handleChangeAvatarForRoomSlashCommand(changeAvatar: ParsedCommand.ChangeAvatarForRoom) { + launchSlashCommandFlowSuspendable { + getMyRoomMemberContent() + ?.copy(avatarUrl = changeAvatar.url) + ?.toContent() + ?.let { + room.sendStateEvent(EventType.STATE_ROOM_MEMBER, session.myUserId, it) + } + } + } + + private fun handleIgnoreSlashCommand(ignore: ParsedCommand.IgnoreUser) { + launchSlashCommandFlowSuspendable { + session.ignoreUserIds(listOf(ignore.userId)) + } + } + + private fun handleUnignoreSlashCommand(unignore: ParsedCommand.UnignoreUser) { + launchSlashCommandFlowSuspendable { + session.unIgnoreUserIds(listOf(unignore.userId)) + } + } + + private fun handleWhoisSlashCommand(whois: ParsedCommand.ShowUser) { + _viewEvents.post(TextComposerViewEvents.OpenRoomMemberProfile(whois.userId)) + } + + private fun sendPrefixedMessage(prefix: String, message: CharSequence) { + val sequence = buildString { + append(prefix) + if (message.isNotEmpty()) { + append(" ") + append(message) + } + } + room.sendTextMessage(sequence) + } + + /** + * Convert a send mode to a draft and save the draft + */ + private fun handleSaveDraft(action: TextComposerAction.SaveDraft) = withState { + session.coroutineScope.launch { + when { + it.sendMode is SendMode.REGULAR && !it.sendMode.fromSharing -> { + setState { copy(sendMode = it.sendMode.copy(action.draft)) } + room.saveDraft(UserDraft.REGULAR(action.draft)) + } + it.sendMode is SendMode.REPLY -> { + setState { copy(sendMode = it.sendMode.copy(text = action.draft)) } + room.saveDraft(UserDraft.REPLY(it.sendMode.timelineEvent.root.eventId!!, action.draft)) + } + it.sendMode is SendMode.QUOTE -> { + setState { copy(sendMode = it.sendMode.copy(text = action.draft)) } + room.saveDraft(UserDraft.QUOTE(it.sendMode.timelineEvent.root.eventId!!, action.draft)) + } + it.sendMode is SendMode.EDIT -> { + setState { copy(sendMode = it.sendMode.copy(text = action.draft)) } + room.saveDraft(UserDraft.EDIT(it.sendMode.timelineEvent.root.eventId!!, action.draft)) + } + } + } + } + + private fun launchSlashCommandFlowSuspendable(block: suspend () -> Unit) { + _viewEvents.post(TextComposerViewEvents.SlashCommandHandled()) + viewModelScope.launch { + val event = try { + block() + TextComposerViewEvents.SlashCommandResultOk + } catch (failure: Exception) { + TextComposerViewEvents.SlashCommandResultError(failure) + } + _viewEvents.post(event) + } + } + + @AssistedFactory + interface Factory { + fun create(initialState: TextComposerViewState): TextComposerViewModel + } + + companion object : MvRxViewModelFactory { + + @JvmStatic + override fun create(viewModelContext: ViewModelContext, state: TextComposerViewState): TextComposerViewModel { + val fragment: RoomDetailFragment = (viewModelContext as FragmentViewModelContext).fragment() + return fragment.textComposerViewModelFactory.create(state) + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerViewState.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerViewState.kt new file mode 100644 index 00000000000..fd1dd2d0ad3 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerViewState.kt @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.home.room.detail.composer + +import com.airbnb.mvrx.MvRxState +import im.vector.app.features.home.room.detail.RoomDetailArgs +import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent + +/** + * Describes the current send mode: + * REGULAR: sends the text as a regular message + * QUOTE: User is currently quoting a message + * EDIT: User is currently editing an existing message + * + * Depending on the state the bottom toolbar will change (icons/preview/actions...) + */ +sealed class SendMode(open val text: String) { + data class REGULAR( + override val text: String, + val fromSharing: Boolean, + // This is necessary for forcing refresh on selectSubscribe + private val ts: Long = System.currentTimeMillis() + ) : SendMode(text) + + data class QUOTE(val timelineEvent: TimelineEvent, override val text: String) : SendMode(text) + data class EDIT(val timelineEvent: TimelineEvent, override val text: String) : SendMode(text) + data class REPLY(val timelineEvent: TimelineEvent, override val text: String) : SendMode(text) +} + +data class TextComposerViewState( + val roomId: String, + val canSendMessage: Boolean = true, + val isVoiceRecording: Boolean = false, + val isSendButtonVisible: Boolean = false, + val sendMode: SendMode = SendMode.REGULAR("", false) +) : MvRxState { + + val isComposerVisible: Boolean + get() = canSendMessage && !isVoiceRecording + + constructor(args: RoomDetailArgs) : this(roomId = args.roomId) +} diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/VoiceMessageHelper.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/VoiceMessageHelper.kt index 0225ef28534..b0880623dee 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/VoiceMessageHelper.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/VoiceMessageHelper.kt @@ -60,6 +60,7 @@ class VoiceMessageHelper @Inject constructor( try { voiceRecorder.startRecord() } catch (failure: Throwable) { + Timber.e(failure, "Unable to start recording") throw VoiceFailure.UnableToRecord(failure) } startRecordingAmplitudes() @@ -146,6 +147,7 @@ class VoiceMessageHelper @Inject constructor( } } } catch (failure: Throwable) { + Timber.e(failure, "Unable to start playback") throw VoiceFailure.UnableToPlay(failure) } startPlaybackTicker(id) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/VoiceMessageRecorderView.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/VoiceMessageRecorderView.kt index 5f4f53cf1eb..f7b8cead37b 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/VoiceMessageRecorderView.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/VoiceMessageRecorderView.kt @@ -27,6 +27,9 @@ import androidx.core.view.isVisible import androidx.core.view.updateLayoutParams import im.vector.app.BuildConfig import im.vector.app.R +import im.vector.app.core.extensions.setAttributeBackground +import im.vector.app.core.extensions.setAttributeTintedBackground +import im.vector.app.core.extensions.setAttributeTintedImageResource import im.vector.app.core.hardware.vibrate import im.vector.app.core.utils.CountUpTimer import im.vector.app.core.utils.DimensionConverter @@ -40,7 +43,7 @@ import kotlin.math.floor /** * Encapsulates the voice message recording view and animations. */ -class VoiceMessageRecorderView: ConstraintLayout, VoiceMessagePlaybackTracker.Listener { +class VoiceMessageRecorderView : ConstraintLayout, VoiceMessagePlaybackTracker.Listener { interface Callback { // Return true if the recording is started @@ -217,7 +220,7 @@ class VoiceMessageRecorderView: ConstraintLayout, VoiceMessagePlaybackTracker.Li views.voiceMessageLockArrow.translationY = 0F } RecordingState.LOCKING -> { - views.voiceMessageLockImage.setImageResource(R.drawable.ic_voice_message_locked) + views.voiceMessageLockImage.setAttributeTintedImageResource(R.drawable.ic_voice_message_locked, R.attr.colorPrimary) val translationAmount = -distanceY.coerceIn(0F, distanceToLock) views.voiceMessageMicButton.translationY = translationAmount views.voiceMessageLockArrow.translationY = translationAmount @@ -257,8 +260,8 @@ class VoiceMessageRecorderView: ConstraintLayout, VoiceMessagePlaybackTracker.Li val previousRecordingState = recordingState if (recordingState == RecordingState.STARTED) { // Determine if cancelling or locking for the first move action. - if (((currentX < firstX && rtlXMultiplier == 1) || (currentX > firstX && rtlXMultiplier == -1)) - && distanceX > distanceY && distanceX > lastDistanceX) { + if (((currentX < firstX && rtlXMultiplier == 1) || (currentX > firstX && rtlXMultiplier == -1)) && + distanceX > distanceY && distanceX > lastDistanceX) { recordingState = RecordingState.CANCELLING } else if (currentY < firstY && distanceY > distanceX && distanceY > lastDistanceY) { recordingState = RecordingState.LOCKING @@ -366,6 +369,7 @@ class VoiceMessageRecorderView: ConstraintLayout, VoiceMessagePlaybackTracker.Li private fun showRecordingViews() { views.voiceMessageMicButton.setImageResource(R.drawable.ic_voice_mic_recording) + views.voiceMessageMicButton.setAttributeTintedBackground(R.drawable.circle_with_halo, R.attr.colorPrimary) views.voiceMessageMicButton.updateLayoutParams { setMargins(0, 0, 0, 0) } @@ -443,6 +447,7 @@ class VoiceMessageRecorderView: ConstraintLayout, VoiceMessagePlaybackTracker.Li private fun resetMicButtonUi() { views.voiceMessageMicButton.isVisible = true views.voiceMessageMicButton.setImageResource(R.drawable.ic_voice_mic) + views.voiceMessageMicButton.setAttributeBackground(android.R.attr.selectableItemBackgroundBorderless) views.voiceMessageMicButton.updateLayoutParams { if (rtlXMultiplier == -1) { // RTL @@ -499,12 +504,14 @@ class VoiceMessageRecorderView: ConstraintLayout, VoiceMessagePlaybackTracker.Li views.voiceMessagePlaybackTimerIndicator.isVisible = true views.voicePlaybackControlButton.isVisible = false views.voiceMessageSendButton.isVisible = true + views.voicePlaybackWaveform.importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES renderToast(context.getString(R.string.voice_message_tap_to_stop_toast)) } private fun showPlaybackViews() { views.voiceMessagePlaybackTimerIndicator.isVisible = false views.voicePlaybackControlButton.isVisible = true + views.voicePlaybackWaveform.importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO callback?.onVoiceRecordingPlaybackModeOn() } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/readreceipts/DisplayReadReceiptItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/readreceipts/DisplayReadReceiptItem.kt index c097b9adb50..7a91dae1836 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/readreceipts/DisplayReadReceiptItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/readreceipts/DisplayReadReceiptItem.kt @@ -26,6 +26,7 @@ import im.vector.app.R import im.vector.app.core.epoxy.ClickListener import im.vector.app.core.epoxy.VectorEpoxyHolder import im.vector.app.core.epoxy.onClick +import im.vector.app.features.displayname.getBestName import im.vector.app.features.home.AvatarRenderer import org.matrix.android.sdk.api.util.MatrixItem diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/readreceipts/DisplayReadReceiptsController.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/readreceipts/DisplayReadReceiptsController.kt index f5348bd3988..bf88218fa6f 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/readreceipts/DisplayReadReceiptsController.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/readreceipts/DisplayReadReceiptsController.kt @@ -30,8 +30,8 @@ import javax.inject.Inject */ class DisplayReadReceiptsController @Inject constructor(private val dateFormatter: VectorDateFormatter, private val session: Session, - private val avatarRender: AvatarRenderer) - : TypedEpoxyController>() { + private val avatarRender: AvatarRenderer) : + TypedEpoxyController>() { var listener: Listener? = null diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultController.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultController.kt index 1debf321041..edef92ee2d9 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultController.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultController.kt @@ -102,9 +102,9 @@ class SearchResultController @Inject constructor( data.searchResult.forEach { eventAndSender -> val event = eventAndSender.event - @Suppress("UNCHECKED_CAST") // Take new content first - val text = ((event.content?.get("m.new_content") as? Content) ?: event.content)?.get("body") as? String ?: return@forEach + @Suppress("UNCHECKED_CAST") +val text = ((event.content?.get("m.new_content") as? Content) ?: event.content)?.get("body") as? String ?: return@forEach val spannable = setHighLightedText(text, data.highlights) ?: return@forEach val eventDate = Calendar.getInstance().apply { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultItem.kt index 14ea94ffd5a..c0f71ed6cc6 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/search/SearchResultItem.kt @@ -26,6 +26,7 @@ import im.vector.app.core.epoxy.VectorEpoxyHolder import im.vector.app.core.epoxy.VectorEpoxyModel import im.vector.app.core.epoxy.onClick import im.vector.app.core.extensions.setTextOrHide +import im.vector.app.features.displayname.getBestName import im.vector.app.features.home.AvatarRenderer import org.matrix.android.sdk.api.util.MatrixItem diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt index 8be319f2a80..5a37a343e64 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt @@ -39,13 +39,13 @@ import im.vector.app.features.home.room.detail.timeline.factory.MergedHeaderItem import im.vector.app.features.home.room.detail.timeline.factory.ReadReceiptsItemFactory import im.vector.app.features.home.room.detail.timeline.factory.TimelineItemFactory import im.vector.app.features.home.room.detail.timeline.factory.TimelineItemFactoryParams -import im.vector.app.features.home.room.detail.timeline.helper.TimelineEventsGroups import im.vector.app.features.home.room.detail.timeline.helper.ContentDownloadStateTrackerBinder import im.vector.app.features.home.room.detail.timeline.helper.ContentUploadStateTrackerBinder import im.vector.app.features.home.room.detail.timeline.helper.TimelineControllerInterceptorHelper import im.vector.app.features.home.room.detail.timeline.helper.TimelineEventDiffUtilCallback import im.vector.app.features.home.room.detail.timeline.helper.TimelineEventVisibilityHelper import im.vector.app.features.home.room.detail.timeline.helper.TimelineEventVisibilityStateChangedListener +import im.vector.app.features.home.room.detail.timeline.helper.TimelineEventsGroups import im.vector.app.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider import im.vector.app.features.home.room.detail.timeline.item.BasedMergedItem import im.vector.app.features.home.room.detail.timeline.item.DaySeparatorItem @@ -249,8 +249,8 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec if (partialState.highlightedEventId != newPartialState.highlightedEventId) { // Clear cache to force a refresh for (i in 0 until modelCache.size) { - if (modelCache[i]?.eventId == viewState.highlightedEventId - || modelCache[i]?.eventId == partialState.highlightedEventId) { + if (modelCache[i]?.eventId == viewState.highlightedEventId || + modelCache[i]?.eventId == partialState.highlightedEventId) { modelCache[i] = null } } @@ -276,6 +276,10 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec } override fun buildModels() { + // Don't build anything if membership is not joined + if (partialState.roomSummary?.membership != Membership.JOIN) { + return + } val timestamp = System.currentTimeMillis() val showingForwardLoader = LoadingItem_() @@ -494,8 +498,8 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec if (vectorPreferences.labShowCompleteHistoryInEncryptedRoom()) { return } - if (event.root.type == EventType.STATE_ROOM_MEMBER - && event.root.stateKey == session.myUserId) { + if (event.root.type == EventType.STATE_ROOM_MEMBER && + event.root.stateKey == session.myUserId) { val content = event.root.content.toModel() if (content?.membership == Membership.INVITE) { hasReachedInvite = true diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsBottomSheet.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsBottomSheet.kt index cb64c8b5adc..6de8864f103 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsBottomSheet.kt @@ -27,7 +27,6 @@ import im.vector.app.core.extensions.configureWith import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.app.databinding.BottomSheetGenericListBinding import im.vector.app.features.home.room.detail.timeline.item.MessageInformationData - import javax.inject.Inject /** diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt index 6e6c7c1dbe4..275d37e2953 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/action/MessageActionsViewModel.kt @@ -341,14 +341,14 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted add(EventSharedAction.AddReaction(eventId)) } - if (canQuote(timelineEvent, messageContent, actionPermissions)) { - add(EventSharedAction.Quote(eventId)) - } - if (canViewReactions(timelineEvent)) { add(EventSharedAction.ViewReactions(informationData)) } + if (canQuote(timelineEvent, messageContent, actionPermissions)) { + add(EventSharedAction.Quote(eventId)) + } + if (timelineEvent.hasBeenEdited()) { add(EventSharedAction.ViewEditHistory(informationData)) } @@ -365,14 +365,14 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted if (vectorPreferences.developerMode()) { if (timelineEvent.isEncrypted() && timelineEvent.root.mCryptoError != null) { val keysBackupService = session.cryptoService().keysBackupService() - if (keysBackupService.state == KeysBackupState.NotTrusted - || (keysBackupService.state == KeysBackupState.ReadyToBackUp - && keysBackupService.canRestoreKeys()) + if (keysBackupService.state == KeysBackupState.NotTrusted || + (keysBackupService.state == KeysBackupState.ReadyToBackUp && + keysBackupService.canRestoreKeys()) ) { add(EventSharedAction.UseKeyBackup) } - if (session.cryptoService().getCryptoDeviceInfo(session.myUserId).size > 1 - || timelineEvent.senderInfo.userId != session.myUserId) { + if (session.cryptoService().getCryptoDeviceInfo(session.myUserId).size > 1 || + timelineEvent.senderInfo.userId != session.myUserId) { add(EventSharedAction.ReRequestKey(timelineEvent.eventId)) } } @@ -435,9 +435,9 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted } private fun canRetry(event: TimelineEvent, actionPermissions: ActionPermissions): Boolean { - return event.root.sendState.hasFailed() - && actionPermissions.canSendMessage - && (event.root.isAttachmentMessage() || event.root.isTextMessage()) + return event.root.sendState.hasFailed() && + actionPermissions.canSendMessage && + (event.root.isAttachmentMessage() || event.root.isTextMessage()) } private fun canViewReactions(event: TimelineEvent): Boolean { @@ -453,8 +453,8 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted // TODO if user is admin or moderator val messageContent = event.root.getClearContent().toModel() return event.root.senderId == myUserId && ( - messageContent?.msgType == MessageType.MSGTYPE_TEXT - || messageContent?.msgType == MessageType.MSGTYPE_EMOTE + messageContent?.msgType == MessageType.MSGTYPE_TEXT || + messageContent?.msgType == MessageType.MSGTYPE_EMOTE ) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryBottomSheet.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryBottomSheet.kt index 7be4be4b577..d8ce6e3e7a8 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryBottomSheet.kt @@ -30,13 +30,12 @@ import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.app.databinding.BottomSheetGenericListWithTitleBinding import im.vector.app.features.home.room.detail.timeline.action.TimelineEventFragmentArgs import im.vector.app.features.home.room.detail.timeline.item.MessageInformationData - import javax.inject.Inject /** * Bottom sheet displaying list of edits for a given event ordered by timestamp */ -class ViewEditHistoryBottomSheet: +class ViewEditHistoryBottomSheet : VectorBaseBottomSheetDialogFragment() { private val viewModel: ViewEditHistoryViewModel by fragmentViewModel(ViewEditHistoryViewModel::class) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryViewState.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryViewState.kt index 62f08eef7f0..ca80c29cb42 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryViewState.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryViewState.kt @@ -26,8 +26,8 @@ data class ViewEditHistoryViewState( val eventId: String, val roomId: String, val isOriginalAReply: Boolean = false, - val editList: Async> = Uninitialized) - : MvRxState { + val editList: Async> = Uninitialized) : + MvRxState { constructor(args: TimelineEventFragmentArgs) : this(roomId = args.roomId, eventId = args.eventId) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MergedHeaderItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MergedHeaderItemFactory.kt index f552266a1c7..e378969b4a7 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MergedHeaderItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MergedHeaderItemFactory.kt @@ -63,10 +63,9 @@ class MergedHeaderItemFactory @Inject constructor(private val activeSessionHolde currentPosition: Int, eventIdToHighlight: String?, callback: TimelineEventController.Callback?, - requestModelBuild: () -> Unit) - : BasedMergedItem<*>? { - return if (nextEvent?.root?.getClearType() == EventType.STATE_ROOM_CREATE - && event.isRoomConfiguration(nextEvent.root.getClearContent()?.toModel()?.creator)) { + requestModelBuild: () -> Unit): BasedMergedItem<*>? { + return if (nextEvent?.root?.getClearType() == EventType.STATE_ROOM_CREATE && + event.isRoomConfiguration(nextEvent.root.getClearContent()?.toModel()?.creator)) { // It's the first item before room.create // Collapse all room configuration events buildRoomCreationMergedSummary(currentPosition, items, partialState, event, eventIdToHighlight, requestModelBuild, callback) @@ -206,6 +205,7 @@ class MergedHeaderItemFactory @Inject constructor(private val activeSessionHolde callback = callback, currentUserId = currentUserId, roomSummary = partialState.roomSummary, + canInvite = powerLevelsHelper?.isUserAbleToInvite(currentUserId) ?: false, canChangeAvatar = powerLevelsHelper?.isUserAllowedToSend(currentUserId, true, EventType.STATE_ROOM_AVATAR) ?: false, canChangeTopic = powerLevelsHelper?.isUserAllowedToSend(currentUserId, true, EventType.STATE_ROOM_TOPIC) ?: false, canChangeName = powerLevelsHelper?.isUserAllowedToSend(currentUserId, true, EventType.STATE_ROOM_NAME) ?: false diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt index e67fa7cca08..98deaaf9c37 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt @@ -69,6 +69,7 @@ import im.vector.app.features.media.ImageContentRenderer import im.vector.app.features.media.VideoContentRenderer import me.gujun.android.span.span import org.commonmark.node.Document +import org.matrix.android.sdk.api.MatrixUrls.isMxcUrl import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.events.model.RelationType import org.matrix.android.sdk.api.session.events.model.toModel @@ -142,8 +143,8 @@ class MessageItemFactory @Inject constructor( val malformedText = stringProvider.getString(R.string.malformed_message) return defaultItemFactory.create(malformedText, informationData, highlight, callback) } - if (messageContent.relatesTo?.type == RelationType.REPLACE - || event.isEncrypted() && event.root.content.toModel()?.relatesTo?.type == RelationType.REPLACE + if (messageContent.relatesTo?.type == RelationType.REPLACE || + event.isEncrypted() && event.root.content.toModel()?.relatesTo?.type == RelationType.REPLACE ) { // This is an edit event, we should display it when debugging as a notice event return noticeItemFactory.create(params) @@ -213,7 +214,7 @@ class MessageItemFactory @Inject constructor( if (informationData.sentByMe && !informationData.sendState.isSent()) { it } else { - it.takeIf { it.startsWith("mxc://") } + it.takeIf { it.isMxcUrl() } } } ?: "" return MessageFileItem_() @@ -244,7 +245,7 @@ class MessageItemFactory @Inject constructor( if (informationData.sentByMe && !informationData.sendState.isSent()) { it } else { - it.takeIf { it.startsWith("mxc://") } + it.takeIf { it.isMxcUrl() } } } ?: "" @@ -622,11 +623,13 @@ class MessageItemFactory @Inject constructor( .highlighted(highlight) } - private fun List?.toFft(): List? { - return this?.map { - // Value comes from AudioRecordView.maxReportableAmp, and 1024 is the max value in the Matrix spec - it * 22760 / 1024 - } + private fun List?.toFft(): List? { + return this + ?.filterNotNull() + ?.map { + // Value comes from AudioRecordView.maxReportableAmp, and 1024 is the max value in the Matrix spec + it * 22760 / 1024 + } } companion object { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/DisplayableEventFormatter.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/DisplayableEventFormatter.kt index 5a9af975edc..b5831b33b86 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/DisplayableEventFormatter.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/DisplayableEventFormatter.kt @@ -46,8 +46,8 @@ class DisplayableEventFormatter @Inject constructor( return noticeEventFormatter.formatRedactedEvent(timelineEvent.root) } - if (timelineEvent.root.isEncrypted() - && timelineEvent.root.mxDecryptionResult == null) { + if (timelineEvent.root.isEncrypted() && + timelineEvent.root.mxDecryptionResult == null) { return stringProvider.getString(R.string.encrypted_message) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt index c80a92d568c..4ca15572081 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt @@ -363,8 +363,8 @@ class NoticeEventFormatter @Inject constructor( private fun formatRoomMemberEvent(event: Event, senderName: String?, isDm: Boolean): String? { val eventContent: RoomMemberContent? = event.getClearContent().toModel() val prevEventContent: RoomMemberContent? = event.resolvedPrevContent().toModel() - val isMembershipEvent = prevEventContent?.membership != eventContent?.membership - || eventContent?.membership == Membership.LEAVE + val isMembershipEvent = prevEventContent?.membership != eventContent?.membership || + eventContent?.membership == Membership.LEAVE return if (isMembershipEvent) { buildMembershipNotice(event, senderName, eventContent, prevEventContent, isDm) } else { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MessageInformationDataFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MessageInformationDataFactory.kt index da75a808d8d..6385494fe1b 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MessageInformationDataFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MessageInformationDataFactory.kt @@ -65,13 +65,13 @@ class MessageInformationDataFactory @Inject constructor(private val session: Ses ?: false val showInformation = - addDaySeparator - || event.senderInfo.avatarUrl != nextDisplayableEvent?.senderInfo?.avatarUrl - || event.senderInfo.disambiguatedDisplayName != nextDisplayableEvent?.senderInfo?.disambiguatedDisplayName - || nextDisplayableEvent.root.getClearType() !in listOf(EventType.MESSAGE, EventType.STICKER, EventType.ENCRYPTED) - || isNextMessageReceivedMoreThanOneHourAgo - || isTileTypeMessage(nextDisplayableEvent) - || nextDisplayableEvent.isEdition() + addDaySeparator || + event.senderInfo.avatarUrl != nextDisplayableEvent?.senderInfo?.avatarUrl || + event.senderInfo.disambiguatedDisplayName != nextDisplayableEvent?.senderInfo?.disambiguatedDisplayName || + nextDisplayableEvent.root.getClearType() !in listOf(EventType.MESSAGE, EventType.STICKER, EventType.ENCRYPTED) || + isNextMessageReceivedMoreThanOneHourAgo || + isTileTypeMessage(nextDisplayableEvent) || + nextDisplayableEvent.isEdition() val time = dateFormatter.format(event.root.originServerTs, DateFormatKind.MESSAGE_SIMPLE) val roomSummary = params.partialState.roomSummary @@ -143,10 +143,10 @@ class MessageInformationDataFactory @Inject constructor(private val session: Ses private fun getE2EDecoration(roomSummary: RoomSummary?, event: TimelineEvent): E2EDecoration { return if ( - event.root.sendState == SendState.SYNCED - && roomSummary?.isEncrypted.orFalse() + event.root.sendState == SendState.SYNCED && + roomSummary?.isEncrypted.orFalse() && // is user verified - && session.cryptoService().crossSigningService().getUserCrossSigningKeys(event.root.senderId ?: "")?.isTrusted() == true) { + session.cryptoService().crossSigningService().getUserCrossSigningKeys(event.root.senderId ?: "")?.isTrusted() == true) { val ts = roomSummary?.encryptionEventTs ?: 0 val eventTs = event.root.originServerTs ?: 0 if (event.isEncrypted()) { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineControllerInterceptorHelper.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineControllerInterceptorHelper.kt index 736da63ee27..7165921b353 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineControllerInterceptorHelper.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineControllerInterceptorHelper.kt @@ -65,8 +65,8 @@ class TimelineControllerInterceptorHelper(private val positionOfReadMarker: KMut } epoxyModel.getEventIds().forEach { eventId -> adapterPositionMapping[eventId] = index - appendReadMarker = appendReadMarker - || (epoxyModel.canAppendReadMarker() && eventId == firstUnreadEventId && atLeastOneVisibleItemsBeforeReadMarker) + appendReadMarker = appendReadMarker || + (epoxyModel.canAppendReadMarker() && eventId == firstUnreadEventId && atLeastOneVisibleItemsBeforeReadMarker) } } if (epoxyModel is DaySeparatorItem) { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineVisibilityStateChangedListeners.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineVisibilityStateChangedListeners.kt index 33e81aeb9b2..2337a6ea153 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineVisibilityStateChangedListeners.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineVisibilityStateChangedListeners.kt @@ -21,8 +21,8 @@ import im.vector.app.core.epoxy.VectorEpoxyModel import im.vector.app.features.home.room.detail.timeline.TimelineEventController import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent -class ReadMarkerVisibilityStateChangedListener(private val callback: TimelineEventController.Callback?) - : VectorEpoxyModel.OnVisibilityStateChangedListener { +class ReadMarkerVisibilityStateChangedListener(private val callback: TimelineEventController.Callback?) : + VectorEpoxyModel.OnVisibilityStateChangedListener { override fun onVisibilityStateChanged(visibilityState: Int) { if (visibilityState == VisibilityState.VISIBLE) { @@ -32,8 +32,8 @@ class ReadMarkerVisibilityStateChangedListener(private val callback: TimelineEve } class TimelineEventVisibilityStateChangedListener(private val callback: TimelineEventController.Callback?, - private val event: TimelineEvent) - : VectorEpoxyModel.OnVisibilityStateChangedListener { + private val event: TimelineEvent) : + VectorEpoxyModel.OnVisibilityStateChangedListener { override fun onVisibilityStateChanged(visibilityState: Int) { if (visibilityState == VisibilityState.VISIBLE) { @@ -45,8 +45,8 @@ class TimelineEventVisibilityStateChangedListener(private val callback: Timeline } class MergedTimelineEventVisibilityStateChangedListener(private val callback: TimelineEventController.Callback?, - private val events: List) - : VectorEpoxyModel.OnVisibilityStateChangedListener { + private val events: List) : + VectorEpoxyModel.OnVisibilityStateChangedListener { override fun onVisibilityStateChanged(visibilityState: Int) { if (visibilityState == VisibilityState.VISIBLE) { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/CallTileTimelineItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/CallTileTimelineItem.kt index 46392a494fc..5f8ac822dab 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/CallTileTimelineItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/CallTileTimelineItem.kt @@ -32,6 +32,7 @@ import im.vector.app.R import im.vector.app.core.epoxy.ClickListener import im.vector.app.core.epoxy.onClick import im.vector.app.core.extensions.setLeftDrawable +import im.vector.app.features.displayname.getBestName import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.room.detail.RoomDetailAction import im.vector.app.features.home.room.detail.timeline.MessageColorProvider diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MergedRoomCreationItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MergedRoomCreationItem.kt index 43a9618a06f..1e8e96426f4 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MergedRoomCreationItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MergedRoomCreationItem.kt @@ -143,7 +143,8 @@ abstract class MergedRoomCreationItem : BasedMergedItem= 2)) - && roomItem?.avatarUrl.isNullOrBlank() + val shouldSetAvatar = attributes.canChangeAvatar && + (roomSummary?.isDirect == false || (isDirect && membersCount >= 2)) && + roomItem?.avatarUrl.isNullOrBlank() holder.roomAvatarImageView.isVisible = roomItem != null if (roomItem != null) { @@ -189,8 +190,9 @@ abstract class MergedRoomCreationItem : BasedMergedItem() { + private val emojiCompatWrapper: EmojiCompatWrapper) : + TypedEpoxyController() { var listener: Listener? = null diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsViewModel.kt index 35d208e30ef..e9e7b471f37 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsViewModel.kt @@ -23,8 +23,8 @@ import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.core.date.DateFormatKind import im.vector.app.core.date.VectorDateFormatter import im.vector.app.core.platform.EmptyAction @@ -41,8 +41,8 @@ import org.matrix.android.sdk.rx.unwrap data class DisplayReactionsViewState( val eventId: String, val roomId: String, - val mapReactionKeyToMemberList: Async> = Uninitialized) - : MvRxState { + val mapReactionKeyToMemberList: Async> = Uninitialized) : + MvRxState { constructor(args: TimelineEventFragmentArgs) : this(roomId = args.roomId, eventId = args.eventId) } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/url/PreviewUrlRetriever.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/url/PreviewUrlRetriever.kt index 4c018c8a992..c97fae055d9 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/url/PreviewUrlRetriever.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/url/PreviewUrlRetriever.kt @@ -146,7 +146,7 @@ class PreviewUrlRetriever(session: Session, companion object { // One week in millis - private const val CACHE_VALIDITY: Long = 7 * 24 * 3_600 * 1_000 + private const val CACHE_VALIDITY = 604_800_000L // 7 * 24 * 3_600 * 1_000 private val blockedDomains = listOf( "https://matrix.to", diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/url/PreviewUrlView.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/url/PreviewUrlView.kt index f5d09efd187..3e08ce5589c 100755 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/url/PreviewUrlView.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/url/PreviewUrlView.kt @@ -26,7 +26,6 @@ import im.vector.app.core.extensions.setTextOrHide import im.vector.app.databinding.ViewUrlPreviewBinding import im.vector.app.features.home.room.detail.timeline.TimelineEventController import im.vector.app.features.media.ImageContentRenderer - import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.media.PreviewUrlData diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/upgrade/MigrateRoomViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/upgrade/MigrateRoomViewModel.kt index 3dc5d0e3baf..71dc794cb02 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/upgrade/MigrateRoomViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/upgrade/MigrateRoomViewModel.kt @@ -34,8 +34,8 @@ import org.matrix.android.sdk.api.session.Session class MigrateRoomViewModel @AssistedInject constructor( @Assisted initialState: MigrateRoomViewState, private val session: Session, - private val upgradeRoomViewModelTask: UpgradeRoomViewModelTask) - : VectorViewModel(initialState) { + private val upgradeRoomViewModelTask: UpgradeRoomViewModelTask) : + VectorViewModel(initialState) { init { val room = session.getRoom(initialState.roomId) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/views/RoomDetailLazyLoadedViews.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/views/RoomDetailLazyLoadedViews.kt new file mode 100644 index 00000000000..fafb49ad5c5 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/views/RoomDetailLazyLoadedViews.kt @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.home.room.detail.views + +import android.view.View +import android.view.ViewStub +import im.vector.app.core.ui.views.FailedMessagesWarningView +import im.vector.app.databinding.FragmentRoomDetailBinding +import im.vector.app.features.invite.VectorInviteView +import kotlin.reflect.KMutableProperty0 + +/** + * This is an holder for lazy loading some views of the RoomDetail screen. + * It's using some ViewStub where it makes sense. + */ +class RoomDetailLazyLoadedViews { + + private var roomDetailBinding: FragmentRoomDetailBinding? = null + + private var failedMessagesWarningView: FailedMessagesWarningView? = null + private var inviteView: VectorInviteView? = null + + fun bind(roomDetailBinding: FragmentRoomDetailBinding) { + this.roomDetailBinding = roomDetailBinding + } + + fun unBind() { + roomDetailBinding = null + inviteView = null + failedMessagesWarningView = null + } + + fun failedMessagesWarningView(inflateIfNeeded: Boolean, callback: FailedMessagesWarningView.Callback? = null): FailedMessagesWarningView? { + return getOrInflate(inflateIfNeeded, roomDetailBinding?.failedMessagesWarningStub, this::failedMessagesWarningView)?.apply { + this.callback = callback + } + } + + fun inviteView(inflateIfNeeded: Boolean): VectorInviteView? { + return getOrInflate(inflateIfNeeded, roomDetailBinding?.inviteViewStub, this::inviteView) + } + + private inline fun getOrInflate(inflateIfNeeded: Boolean, stub: ViewStub?, reference: KMutableProperty0): T? { + if (!inflateIfNeeded || stub == null || stub.parent == null) return reference.get() + val inflatedView = stub.inflate() as T + reference.set(inflatedView) + return inflatedView + } +} diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/widget/RoomWidgetsBannerView.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/widget/RoomWidgetsBannerView.kt index 44923618328..b934136dc2f 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/widget/RoomWidgetsBannerView.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/widget/RoomWidgetsBannerView.kt @@ -22,7 +22,6 @@ import android.view.View import android.widget.RelativeLayout import im.vector.app.R import im.vector.app.databinding.ViewRoomWidgetsBannerBinding - import org.matrix.android.sdk.api.session.widgets.model.Widget class RoomWidgetsBannerView @JvmOverloads constructor( diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/widget/RoomWidgetsBottomSheet.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/widget/RoomWidgetsBottomSheet.kt index 4e778735223..ab6eb2e658b 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/widget/RoomWidgetsBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/widget/RoomWidgetsBottomSheet.kt @@ -33,7 +33,6 @@ import im.vector.app.features.home.room.detail.RoomDetailAction import im.vector.app.features.home.room.detail.RoomDetailViewModel import im.vector.app.features.home.room.detail.RoomDetailViewState import im.vector.app.features.navigation.Navigator - import org.matrix.android.sdk.api.session.widgets.model.Widget import javax.inject.Inject diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/widget/RoomWidgetsController.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/widget/RoomWidgetsController.kt index 14f8b539700..2da3dab16a6 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/widget/RoomWidgetsController.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/widget/RoomWidgetsController.kt @@ -30,8 +30,8 @@ import javax.inject.Inject */ class RoomWidgetsController @Inject constructor( val stringProvider: StringProvider, - val colorProvider: ColorProvider) - : TypedEpoxyController>() { + val colorProvider: ColorProvider) : + TypedEpoxyController>() { var listener: Listener? = null diff --git a/vector/src/main/java/im/vector/app/features/home/room/filtered/FilteredRoomFooterItem.kt b/vector/src/main/java/im/vector/app/features/home/room/filtered/FilteredRoomFooterItem.kt index 5e450045797..4c163f2f566 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/filtered/FilteredRoomFooterItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/filtered/FilteredRoomFooterItem.kt @@ -17,6 +17,7 @@ package im.vector.app.features.home.room.filtered import android.widget.Button +import androidx.core.view.isVisible import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyModelClass import im.vector.app.R @@ -33,11 +34,21 @@ abstract class FilteredRoomFooterItem : VectorEpoxyModel - : EpoxyController(), CollapsableControllerExtension { +abstract class CollapsableTypedEpoxyController : + EpoxyController(), CollapsableControllerExtension { private var currentData: T? = null private var allowModelBuildRequests = false @@ -43,8 +43,8 @@ abstract class CollapsableTypedEpoxyController override fun requestModelBuild() { check(allowModelBuildRequests) { - ("You cannot call `requestModelBuild` directly. Call `setData` instead to trigger a " - + "model refresh with new data.") + ("You cannot call `requestModelBuild` directly. Call `setData` instead to trigger a " + + "model refresh with new data.") } super.requestModelBuild() } @@ -57,8 +57,8 @@ abstract class CollapsableTypedEpoxyController override fun requestDelayedModelBuild(delayMs: Int) { check(allowModelBuildRequests) { - ("You cannot call `requestModelBuild` directly. Call `setData` instead to trigger a " - + "model refresh with new data.") + ("You cannot call `requestModelBuild` directly. Call `setData` instead to trigger a " + + "model refresh with new data.") } super.requestDelayedModelBuild(delayMs) } @@ -69,8 +69,8 @@ abstract class CollapsableTypedEpoxyController override fun buildModels() { check(isBuildingModels()) { - ("You cannot call `buildModels` directly. Call `setData` instead to trigger a model " - + "refresh with new data.") + ("You cannot call `buildModels` directly. Call `setData` instead to trigger a model " + + "refresh with new data.") } if (collapsed) { buildModels(null) diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomInvitationItem.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomInvitationItem.kt index b0cc8fec6ca..4413776636a 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomInvitationItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomInvitationItem.kt @@ -28,6 +28,7 @@ import im.vector.app.core.epoxy.VectorEpoxyModel import im.vector.app.core.epoxy.onClick import im.vector.app.core.extensions.setTextOrHide import im.vector.app.core.platform.ButtonStateView +import im.vector.app.features.displayname.getBestName import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.invite.InviteButtonStateBinder import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFooterController.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFooterController.kt index 22cd0ae639b..00d59f4c374 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFooterController.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFooterController.kt @@ -24,6 +24,7 @@ import im.vector.app.core.resources.UserPreferencesProvider import im.vector.app.features.home.RoomListDisplayMode import im.vector.app.features.home.room.filtered.FilteredRoomFooterItem import im.vector.app.features.home.room.filtered.filteredRoomFooterItem +import im.vector.app.space import javax.inject.Inject class RoomListFooterController @Inject constructor( @@ -41,6 +42,7 @@ class RoomListFooterController @Inject constructor( id("filter_footer") listener(host.listener) currentFilter(data.roomFilter) + inSpace(data.currentRoomGrouping.invoke()?.space() != null) } } else -> { diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt index fdfe1714396..f9ee303e1c5 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt @@ -415,8 +415,8 @@ class RoomListFragment @Inject constructor( } private fun checkEmptyState() { - val shouldShowEmpty = adapterInfosList.all { it.sectionHeaderAdapter.roomsSectionData.isHidden } - && !adapterInfosList.any { it.sectionHeaderAdapter.roomsSectionData.isLoading } + val shouldShowEmpty = adapterInfosList.all { it.sectionHeaderAdapter.roomsSectionData.isHidden } && + !adapterInfosList.any { it.sectionHeaderAdapter.roomsSectionData.isLoading } if (shouldShowEmpty) { val emptyState = when (roomListParams.displayMode) { RoomListDisplayMode.NOTIFICATIONS -> { diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListSectionBuilder.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListSectionBuilder.kt index 019a6ceddf4..2b3152f8cf1 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListSectionBuilder.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListSectionBuilder.kt @@ -19,7 +19,7 @@ package im.vector.app.features.home.room.list import im.vector.app.features.home.RoomListDisplayMode interface RoomListSectionBuilder { - fun buildSections(mode: RoomListDisplayMode) : List + fun buildSections(mode: RoomListDisplayMode): List fun dispose() } diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt index 11f92284a6c..b7a2d4e26b0 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModel.kt @@ -30,6 +30,7 @@ import im.vector.app.RoomGroupingMethod import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider +import im.vector.app.features.displayname.getBestName import im.vector.app.features.invite.AutoAcceptInvites import im.vector.app.features.settings.VectorPreferences import kotlinx.coroutines.Dispatchers @@ -41,6 +42,7 @@ import org.matrix.android.sdk.api.session.room.UpdatableLivePageResult import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState import org.matrix.android.sdk.api.session.room.model.tag.RoomTag import org.matrix.android.sdk.api.session.room.state.isPublic +import org.matrix.android.sdk.api.util.toMatrixItem import org.matrix.android.sdk.rx.rx import timber.log.Timber import javax.inject.Inject @@ -72,11 +74,13 @@ class RoomListViewModel @Inject constructor( * If current space is null, will return orphan rooms only */ ORPHANS_IF_SPACE_NULL, + /** * Special case when we don't want to discriminate rooms when current space is null. * In this case return all. */ ALL_IF_SPACE_NULL, + /** Do not filter based on space*/ NONE } @@ -93,7 +97,7 @@ class RoomListViewModel @Inject constructor( } session.rx().liveUser(session.myUserId) - .map { it.getOrNull()?.getBestName() } + .map { it.getOrNull()?.toMatrixItem()?.getBestName() } .distinctUntilChanged() .execute { copy( diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModelFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModelFactory.kt index 6b269356c72..e017a8fe080 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModelFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomListViewModelFactory.kt @@ -28,8 +28,8 @@ class RoomListViewModelFactory @Inject constructor(private val session: Provider private val appStateHandler: AppStateHandler, private val stringProvider: StringProvider, private val vectorPreferences: VectorPreferences, - private val autoAcceptInvites: AutoAcceptInvites) - : RoomListViewModel.Factory { + private val autoAcceptInvites: AutoAcceptInvites) : + RoomListViewModel.Factory { override fun create(initialState: RoomListViewState): RoomListViewModel { return RoomListViewModel( diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryItem.kt b/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryItem.kt index 6cfd04738a5..e4abbeb1ffe 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryItem.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/RoomSummaryItem.kt @@ -33,6 +33,7 @@ import im.vector.app.core.epoxy.VectorEpoxyModel import im.vector.app.core.epoxy.onClick import im.vector.app.core.extensions.setTextOrHide import im.vector.app.core.ui.views.ShieldImageView +import im.vector.app.features.displayname.getBestName import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.themes.ThemeUtils import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsSharedAction.kt b/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsSharedAction.kt index 6f93599d024..2d61da0dd57 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsSharedAction.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsSharedAction.kt @@ -24,8 +24,8 @@ import im.vector.app.core.platform.VectorSharedAction sealed class RoomListQuickActionsSharedAction( @StringRes val titleRes: Int, @DrawableRes val iconResId: Int?, - val destructive: Boolean = false) - : VectorSharedAction { + val destructive: Boolean = false) : + VectorSharedAction { data class NotificationsAllNoisy(val roomId: String) : RoomListQuickActionsSharedAction( R.string.room_list_quick_actions_notifications_all_noisy, diff --git a/vector/src/main/java/im/vector/app/features/homeserver/HomeServerCapabilitiesViewModel.kt b/vector/src/main/java/im/vector/app/features/homeserver/HomeServerCapabilitiesViewModel.kt index 083b843b330..312e2766cce 100644 --- a/vector/src/main/java/im/vector/app/features/homeserver/HomeServerCapabilitiesViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/homeserver/HomeServerCapabilitiesViewModel.kt @@ -21,8 +21,8 @@ import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.core.di.HasScreenInjector import im.vector.app.core.platform.EmptyAction import im.vector.app.core.platform.EmptyViewEvents diff --git a/vector/src/main/java/im/vector/app/features/html/PillImageSpan.kt b/vector/src/main/java/im/vector/app/features/html/PillImageSpan.kt index 76947e8d3d7..c1040a8cc0e 100644 --- a/vector/src/main/java/im/vector/app/features/html/PillImageSpan.kt +++ b/vector/src/main/java/im/vector/app/features/html/PillImageSpan.kt @@ -30,6 +30,7 @@ import com.bumptech.glide.request.transition.Transition import com.google.android.material.chip.ChipDrawable import im.vector.app.R import im.vector.app.core.glide.GlideRequests +import im.vector.app.features.displayname.getBestName import im.vector.app.features.home.AvatarRenderer import org.matrix.android.sdk.api.session.room.send.MatrixItemSpan import org.matrix.android.sdk.api.util.MatrixItem diff --git a/vector/src/main/java/im/vector/app/features/html/PillsPostProcessor.kt b/vector/src/main/java/im/vector/app/features/html/PillsPostProcessor.kt index 716baff7578..f8a2ee51370 100644 --- a/vector/src/main/java/im/vector/app/features/html/PillsPostProcessor.kt +++ b/vector/src/main/java/im/vector/app/features/html/PillsPostProcessor.kt @@ -20,8 +20,8 @@ import android.content.Context import android.text.Spannable import android.text.Spanned import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.glide.GlideApp import im.vector.app.features.home.AvatarRenderer @@ -35,8 +35,8 @@ import org.matrix.android.sdk.api.util.toMatrixItem class PillsPostProcessor @AssistedInject constructor(@Assisted private val roomId: String?, private val context: Context, private val avatarRenderer: AvatarRenderer, - private val sessionHolder: ActiveSessionHolder) - : EventHtmlRenderer.PostProcessor { + private val sessionHolder: ActiveSessionHolder) : + EventHtmlRenderer.PostProcessor { @AssistedFactory interface Factory { diff --git a/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomViewModel.kt b/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomViewModel.kt index 2f20fef3c95..49ff8bde0d0 100644 --- a/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/invite/InviteUsersToRoomViewModel.kt @@ -21,8 +21,8 @@ import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.R import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider @@ -34,8 +34,8 @@ import org.matrix.android.sdk.rx.rx class InviteUsersToRoomViewModel @AssistedInject constructor(@Assisted initialState: InviteUsersToRoomViewState, session: Session, - val stringProvider: StringProvider) - : VectorViewModel(initialState) { + val stringProvider: StringProvider) : + VectorViewModel(initialState) { private val room = session.getRoom(initialState.roomId)!! diff --git a/vector/src/main/java/im/vector/app/features/invite/VectorInviteView.kt b/vector/src/main/java/im/vector/app/features/invite/VectorInviteView.kt index b2919d59b8d..22f9e9a18c3 100644 --- a/vector/src/main/java/im/vector/app/features/invite/VectorInviteView.kt +++ b/vector/src/main/java/im/vector/app/features/invite/VectorInviteView.kt @@ -30,8 +30,8 @@ import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary import org.matrix.android.sdk.api.util.toMatrixItem import javax.inject.Inject -class VectorInviteView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) - : ConstraintLayout(context, attrs, defStyle) { +class VectorInviteView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) : + ConstraintLayout(context, attrs, defStyle) { interface Callback { fun onAcceptInvite() diff --git a/vector/src/main/java/im/vector/app/features/link/LinkHandlerActivity.kt b/vector/src/main/java/im/vector/app/features/link/LinkHandlerActivity.kt index a0b8efd5aa8..39105185b13 100644 --- a/vector/src/main/java/im/vector/app/features/link/LinkHandlerActivity.kt +++ b/vector/src/main/java/im/vector/app/features/link/LinkHandlerActivity.kt @@ -27,13 +27,12 @@ import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.utils.toast import im.vector.app.databinding.ActivityProgressBinding +import im.vector.app.features.home.HomeActivity import im.vector.app.features.login.LoginConfig import im.vector.app.features.permalink.PermalinkHandler -import io.reactivex.android.schedulers.AndroidSchedulers import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.permalinks.PermalinkService import timber.log.Timber -import java.util.concurrent.TimeUnit import javax.inject.Inject /** @@ -45,30 +44,38 @@ class LinkHandlerActivity : VectorBaseActivity() { @Inject lateinit var errorFormatter: ErrorFormatter @Inject lateinit var permalinkHandler: PermalinkHandler + override fun getBinding() = ActivityProgressBinding.inflate(layoutInflater) + override fun injectWith(injector: ScreenComponent) { injector.inject(this) } - override fun getBinding() = ActivityProgressBinding.inflate(layoutInflater) - override fun initUiAndData() { - val uri = intent.data + handleIntent() + } - if (uri == null) { - // Should not happen - Timber.w("Uri is null") - finish() - return - } + override fun onNewIntent(intent: Intent?) { + super.onNewIntent(intent) + handleIntent() + } - if (uri.getQueryParameter(LoginConfig.CONFIG_HS_PARAMETER) != null) { - handleConfigUrl(uri) - } else if (SUPPORTED_HOSTS.contains(uri.host)) { - handleSupportedHostUrl(uri) - } else { - // Other links are not yet handled, but should not come here (manifest configuration error?) - toast(R.string.universal_link_malformed) - finish() + private fun handleIntent() { + val uri = intent.data + when { + uri == null -> { + // Should not happen + Timber.w("Uri is null") + finish() + } + uri.getQueryParameter(LoginConfig.CONFIG_HS_PARAMETER) != null -> handleConfigUrl(uri) + uri.toString().startsWith(PermalinkService.MATRIX_TO_URL_BASE) -> handleSupportedHostUrl() + uri.toString().startsWith(PermalinkHandler.MATRIX_TO_CUSTOM_SCHEME_URL_BASE) -> handleSupportedHostUrl() + resources.getStringArray(R.array.permalink_supported_hosts).contains(uri.host) -> handleSupportedHostUrl() + else -> { + // Other links are not yet handled, but should not come here (manifest configuration error?) + toast(R.string.universal_link_malformed) + finish() + } } } @@ -81,53 +88,28 @@ class LinkHandlerActivity : VectorBaseActivity() { } } - private fun handleSupportedHostUrl(uri: Uri) { + private fun handleSupportedHostUrl() { + // If we are not logged in, open login screen. + // In the future, we might want to relaunch the process after login. if (!sessionHolder.hasActiveSession()) { - startLoginActivity(uri) - finish() - } else { - convertUriToPermalink(uri)?.let { permalink -> - startPermalinkHandler(permalink) - } ?: run { - // Host is correct but we do not recognize path - Timber.w("Unable to handle this uri: $uri") - finish() - } + startLoginActivity() + return } - } - /** - * Convert a URL of element web instance to a matrix.to url - * Examples: - * - https://riot.im/develop/#/room/#element-android:matrix.org -> https://matrix.to/#/#element-android:matrix.org - * - https://app.element.io/#/room/#element-android:matrix.org -> https://matrix.to/#/#element-android:matrix.org - */ - private fun convertUriToPermalink(uri: Uri): String? { - val uriString = uri.toString() - val path = SUPPORTED_PATHS.find { it in uriString } ?: return null - return PermalinkService.MATRIX_TO_URL_BASE + uriString.substringAfter(path) - } - - private fun startPermalinkHandler(permalink: String) { - permalinkHandler.launch(this, permalink, buildTask = true) - .delay(500, TimeUnit.MILLISECONDS) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe { isHandled -> - if (!isHandled) { - toast(R.string.universal_link_malformed) - } - finish() - } - .disposeOnDestroy() + // We forward intent to HomeActivity (singleTask) to avoid the dueling app problem + // https://stackoverflow.com/questions/25884954/deep-linking-and-multiple-app-instances + intent.setClass(this, HomeActivity::class.java) + intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP + startActivity(intent) } /** - * Start the login screen with identity server and homeserver pre-filled + * Start the login screen with identity server and homeserver pre-filled, if any */ - private fun startLoginActivity(uri: Uri) { + private fun startLoginActivity(uri: Uri? = null) { navigator.openLogin( context = this, - loginConfig = LoginConfig.parse(uri), + loginConfig = uri?.let { LoginConfig.parse(uri) }, flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK ) finish() @@ -173,21 +155,4 @@ class LinkHandlerActivity : VectorBaseActivity() { .setPositiveButton(R.string.ok) { _, _ -> finish() } .show() } - - companion object { - private val SUPPORTED_HOSTS = listOf( - // Regular Element Web instance - "app.element.io", - // Other known instances of Element Web - "develop.element.io", - "staging.element.io", - // Previous Web instance, kept for compatibility reason - "riot.im" - ) - private val SUPPORTED_PATHS = listOf( - "/#/room/", - "/#/user/", - "/#/group/" - ) - } } diff --git a/vector/src/main/java/im/vector/app/features/login/AbstractLoginFragment.kt b/vector/src/main/java/im/vector/app/features/login/AbstractLoginFragment.kt index 3cb5435c175..8b83873142a 100644 --- a/vector/src/main/java/im/vector/app/features/login/AbstractLoginFragment.kt +++ b/vector/src/main/java/im/vector/app/features/login/AbstractLoginFragment.kt @@ -37,7 +37,7 @@ import javax.net.ssl.HttpsURLConnection /** * Parent Fragment for all the login/registration screens */ -abstract class AbstractLoginFragment : VectorBaseFragment(), OnBackPressed { +abstract class AbstractLoginFragment : VectorBaseFragment(), OnBackPressed { protected val loginViewModel: LoginViewModel by activityViewModel() @@ -83,8 +83,8 @@ abstract class AbstractLoginFragment : VectorBaseFragment() /* Ignore this error, user has cancelled the action */ Unit is Failure.ServerError -> - if (throwable.error.code == MatrixError.M_FORBIDDEN - && throwable.httpCode == HttpsURLConnection.HTTP_FORBIDDEN /* 403 */) { + if (throwable.error.code == MatrixError.M_FORBIDDEN && + throwable.httpCode == HttpsURLConnection.HTTP_FORBIDDEN /* 403 */) { MaterialAlertDialogBuilder(requireActivity()) .setTitle(R.string.dialog_title_error) .setMessage(getString(R.string.login_registration_disabled)) diff --git a/vector/src/main/java/im/vector/app/features/login/AbstractSSOLoginFragment.kt b/vector/src/main/java/im/vector/app/features/login/AbstractSSOLoginFragment.kt index 3fc5037ae70..8663b7c73f0 100644 --- a/vector/src/main/java/im/vector/app/features/login/AbstractSSOLoginFragment.kt +++ b/vector/src/main/java/im/vector/app/features/login/AbstractSSOLoginFragment.kt @@ -25,7 +25,7 @@ import androidx.viewbinding.ViewBinding import com.airbnb.mvrx.withState import im.vector.app.core.utils.openUrlInChromeCustomTab -abstract class AbstractSSOLoginFragment : AbstractLoginFragment() { +abstract class AbstractSSOLoginFragment : AbstractLoginFragment() { // For sso private var customTabsServiceConnection: CustomTabsServiceConnection? = null diff --git a/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt b/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt index 07dfc626026..f0a1b1f9374 100644 --- a/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt +++ b/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt @@ -21,7 +21,6 @@ import android.content.Intent import android.view.View import android.view.ViewGroup import androidx.annotation.CallSuper -import com.google.android.material.appbar.MaterialToolbar import androidx.core.view.ViewCompat import androidx.core.view.children import androidx.core.view.isVisible @@ -30,6 +29,7 @@ import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentTransaction import com.airbnb.mvrx.viewModel import com.airbnb.mvrx.withState +import com.google.android.material.appbar.MaterialToolbar import com.google.android.material.dialog.MaterialAlertDialogBuilder import im.vector.app.R import im.vector.app.core.di.ScreenComponent @@ -45,7 +45,6 @@ import im.vector.app.features.login.terms.LoginTermsFragment import im.vector.app.features.login.terms.LoginTermsFragmentArgument import im.vector.app.features.login.terms.toLocalizedLoginTerms import im.vector.app.features.pin.UnlockedActivity - import org.matrix.android.sdk.api.auth.registration.FlowResult import org.matrix.android.sdk.api.auth.registration.Stage import org.matrix.android.sdk.api.extensions.tryOrNull diff --git a/vector/src/main/java/im/vector/app/features/login/LoginFragment.kt b/vector/src/main/java/im/vector/app/features/login/LoginFragment.kt index 016e4ca0c77..c613cf93d50 100644 --- a/vector/src/main/java/im/vector/app/features/login/LoginFragment.kt +++ b/vector/src/main/java/im/vector/app/features/login/LoginFragment.kt @@ -37,7 +37,6 @@ import im.vector.app.core.extensions.toReducedUrl import im.vector.app.databinding.FragmentLoginBinding import io.reactivex.Observable import io.reactivex.rxkotlin.subscribeBy - import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.failure.MatrixError import org.matrix.android.sdk.api.failure.isInvalidPassword @@ -251,8 +250,8 @@ class LoginFragment @Inject constructor() : AbstractSSOLoginFragment { val error = state.asyncLoginAction.error - if (error is Failure.ServerError - && error.error.code == MatrixError.M_FORBIDDEN - && error.error.message.isEmpty()) { + if (error is Failure.ServerError && + error.error.code == MatrixError.M_FORBIDDEN && + error.error.message.isEmpty()) { // Login with email, but email unknown views.loginFieldTil.error = getString(R.string.login_login_with_email_error) } else { diff --git a/vector/src/main/java/im/vector/app/features/login/LoginMode.kt b/vector/src/main/java/im/vector/app/features/login/LoginMode.kt index 2be198ab962..00945968fb5 100644 --- a/vector/src/main/java/im/vector/app/features/login/LoginMode.kt +++ b/vector/src/main/java/im/vector/app/features/login/LoginMode.kt @@ -29,7 +29,7 @@ sealed class LoginMode : Parcelable @Parcelize object Unsupported : LoginMode() } -fun LoginMode.ssoIdentityProviders() : List? { +fun LoginMode.ssoIdentityProviders(): List? { return when (this) { is LoginMode.Sso -> ssoIdentityProviders is LoginMode.SsoAndPassword -> ssoIdentityProviders @@ -37,7 +37,7 @@ fun LoginMode.ssoIdentityProviders() : List? { } } -fun LoginMode.hasSso() : Boolean { +fun LoginMode.hasSso(): Boolean { return when (this) { is LoginMode.Sso -> true is LoginMode.SsoAndPassword -> true diff --git a/vector/src/main/java/im/vector/app/features/login/LoginResetPasswordFragment.kt b/vector/src/main/java/im/vector/app/features/login/LoginResetPasswordFragment.kt index 07c27ab2962..cc113b1eeda 100644 --- a/vector/src/main/java/im/vector/app/features/login/LoginResetPasswordFragment.kt +++ b/vector/src/main/java/im/vector/app/features/login/LoginResetPasswordFragment.kt @@ -27,13 +27,12 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.jakewharton.rxbinding3.widget.textChanges import im.vector.app.R import im.vector.app.core.extensions.hideKeyboard -import im.vector.app.core.extensions.isEmail import im.vector.app.core.extensions.hidePassword +import im.vector.app.core.extensions.isEmail import im.vector.app.core.extensions.toReducedUrl import im.vector.app.databinding.FragmentLoginResetPasswordBinding import io.reactivex.Observable import io.reactivex.rxkotlin.subscribeBy - import javax.inject.Inject /** diff --git a/vector/src/main/java/im/vector/app/features/login/LoginResetPasswordMailConfirmationFragment.kt b/vector/src/main/java/im/vector/app/features/login/LoginResetPasswordMailConfirmationFragment.kt index c5551bce91c..67a72471307 100644 --- a/vector/src/main/java/im/vector/app/features/login/LoginResetPasswordMailConfirmationFragment.kt +++ b/vector/src/main/java/im/vector/app/features/login/LoginResetPasswordMailConfirmationFragment.kt @@ -25,7 +25,6 @@ import com.airbnb.mvrx.Success import com.google.android.material.dialog.MaterialAlertDialogBuilder import im.vector.app.R import im.vector.app.databinding.FragmentLoginResetPasswordMailConfirmationBinding - import org.matrix.android.sdk.api.failure.is401 import javax.inject.Inject diff --git a/vector/src/main/java/im/vector/app/features/login/LoginResetPasswordSuccessFragment.kt b/vector/src/main/java/im/vector/app/features/login/LoginResetPasswordSuccessFragment.kt index 059195dbce1..8300326b27a 100644 --- a/vector/src/main/java/im/vector/app/features/login/LoginResetPasswordSuccessFragment.kt +++ b/vector/src/main/java/im/vector/app/features/login/LoginResetPasswordSuccessFragment.kt @@ -21,7 +21,6 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import im.vector.app.databinding.FragmentLoginResetPasswordSuccessBinding - import javax.inject.Inject /** diff --git a/vector/src/main/java/im/vector/app/features/login/LoginServerSelectionFragment.kt b/vector/src/main/java/im/vector/app/features/login/LoginServerSelectionFragment.kt index 0c81c47129c..2d40ea818a0 100644 --- a/vector/src/main/java/im/vector/app/features/login/LoginServerSelectionFragment.kt +++ b/vector/src/main/java/im/vector/app/features/login/LoginServerSelectionFragment.kt @@ -23,7 +23,6 @@ import android.view.ViewGroup import im.vector.app.R import im.vector.app.core.utils.openUrlInChromeCustomTab import im.vector.app.databinding.FragmentLoginServerSelectionBinding - import me.gujun.android.span.span import javax.inject.Inject diff --git a/vector/src/main/java/im/vector/app/features/login/LoginServerUrlFormFragment.kt b/vector/src/main/java/im/vector/app/features/login/LoginServerUrlFormFragment.kt index 538e4700de2..d0b4d65b191 100644 --- a/vector/src/main/java/im/vector/app/features/login/LoginServerUrlFormFragment.kt +++ b/vector/src/main/java/im/vector/app/features/login/LoginServerUrlFormFragment.kt @@ -33,7 +33,6 @@ import im.vector.app.core.extensions.hideKeyboard import im.vector.app.core.utils.ensureProtocol import im.vector.app.core.utils.openUrlInChromeCustomTab import im.vector.app.databinding.FragmentLoginServerUrlFormBinding - import org.matrix.android.sdk.api.failure.Failure import java.net.UnknownHostException import javax.inject.Inject @@ -144,8 +143,8 @@ class LoginServerUrlFormFragment @Inject constructor() : AbstractLoginFragment LoginMode.SsoAndPassword(data.ssoIdentityProviders) - data.supportedLoginTypes.contains(LoginFlowTypes.SSO) -> LoginMode.Sso(data.ssoIdentityProviders) - data.supportedLoginTypes.contains(LoginFlowTypes.PASSWORD) -> LoginMode.Password - else -> LoginMode.Unsupported + data.supportedLoginTypes.contains(LoginFlowTypes.SSO) && + data.supportedLoginTypes.contains(LoginFlowTypes.PASSWORD) -> LoginMode.SsoAndPassword(data.ssoIdentityProviders) + data.supportedLoginTypes.contains(LoginFlowTypes.SSO) -> LoginMode.Sso(data.ssoIdentityProviders) + data.supportedLoginTypes.contains(LoginFlowTypes.PASSWORD) -> LoginMode.Password + else -> LoginMode.Unsupported } // FIXME We should post a view event here normally? @@ -798,8 +798,8 @@ class LoginViewModel @AssistedInject constructor( loginModeSupportedTypes = data.supportedLoginTypes.toList() ) } - if ((loginMode == LoginMode.Password && !data.isLoginAndRegistrationSupported) - || data.isOutdatedHomeserver) { + if ((loginMode == LoginMode.Password && !data.isLoginAndRegistrationSupported) || + data.isOutdatedHomeserver) { // Notify the UI _viewEvents.post(LoginViewEvents.OutdatedHomeserver) } diff --git a/vector/src/main/java/im/vector/app/features/login/LoginViewState.kt b/vector/src/main/java/im/vector/app/features/login/LoginViewState.kt index 187d39d9eb1..d6f2c8f0872 100644 --- a/vector/src/main/java/im/vector/app/features/login/LoginViewState.kt +++ b/vector/src/main/java/im/vector/app/features/login/LoginViewState.kt @@ -51,20 +51,20 @@ data class LoginViewState( // Network result @PersistState val loginMode: LoginMode = LoginMode.Unknown, - @PersistState // Supported types for the login. We cannot use a sealed class for LoginType because it is not serializable - val loginModeSupportedTypes: List = emptyList(), + @PersistState +val loginModeSupportedTypes: List = emptyList(), val knownCustomHomeServersUrls: List = emptyList() ) : MvRxState { fun isLoading(): Boolean { - return asyncLoginAction is Loading - || asyncHomeServerLoginFlowRequest is Loading - || asyncResetPassword is Loading - || asyncResetMailConfirmed is Loading - || asyncRegistration is Loading + return asyncLoginAction is Loading || + asyncHomeServerLoginFlowRequest is Loading || + asyncResetPassword is Loading || + asyncResetMailConfirmed is Loading || + asyncRegistration is Loading || // Keep loading when it is success because of the delay to switch to the next Activity - || asyncLoginAction is Success + asyncLoginAction is Success } fun isUserLogged(): Boolean { diff --git a/vector/src/main/java/im/vector/app/features/login/LoginWebFragment.kt b/vector/src/main/java/im/vector/app/features/login/LoginWebFragment.kt index f01bf010297..0eee121854d 100644 --- a/vector/src/main/java/im/vector/app/features/login/LoginWebFragment.kt +++ b/vector/src/main/java/im/vector/app/features/login/LoginWebFragment.kt @@ -37,7 +37,6 @@ import im.vector.app.core.utils.AssetReader import im.vector.app.databinding.FragmentLoginWebBinding import im.vector.app.features.signout.soft.SoftLogoutAction import im.vector.app.features.signout.soft.SoftLogoutViewModel - import org.matrix.android.sdk.api.auth.data.Credentials import org.matrix.android.sdk.internal.di.MoshiProvider import timber.log.Timber diff --git a/vector/src/main/java/im/vector/app/features/login/SocialLoginButtonsView.kt b/vector/src/main/java/im/vector/app/features/login/SocialLoginButtonsView.kt index 4dc688ad223..f40cad9ec5b 100644 --- a/vector/src/main/java/im/vector/app/features/login/SocialLoginButtonsView.kt +++ b/vector/src/main/java/im/vector/app/features/login/SocialLoginButtonsView.kt @@ -27,8 +27,8 @@ import com.google.android.material.button.MaterialButton import im.vector.app.R import org.matrix.android.sdk.api.auth.data.SsoIdentityProvider -class SocialLoginButtonsView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) - : LinearLayout(context, attrs, defStyle) { +class SocialLoginButtonsView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) : + LinearLayout(context, attrs, defStyle) { interface InteractionListener { fun onProviderSelected(id: String?) diff --git a/vector/src/main/java/im/vector/app/features/login/SupportedStage.kt b/vector/src/main/java/im/vector/app/features/login/SupportedStage.kt index d9036f9ee4d..5ff00f7e859 100644 --- a/vector/src/main/java/im/vector/app/features/login/SupportedStage.kt +++ b/vector/src/main/java/im/vector/app/features/login/SupportedStage.kt @@ -22,9 +22,9 @@ import org.matrix.android.sdk.api.auth.registration.Stage * Stage.Other is not supported, as well as any other new stages added to the SDK before it is added to the list below */ fun Stage.isSupported(): Boolean { - return this is Stage.ReCaptcha - || this is Stage.Dummy - || this is Stage.Msisdn - || this is Stage.Terms - || this is Stage.Email + return this is Stage.ReCaptcha || + this is Stage.Dummy || + this is Stage.Msisdn || + this is Stage.Terms || + this is Stage.Email } diff --git a/vector/src/main/java/im/vector/app/features/login2/AbstractSSOLoginFragment2.kt b/vector/src/main/java/im/vector/app/features/login2/AbstractSSOLoginFragment2.kt index b12d9638dc5..43f301d9b42 100644 --- a/vector/src/main/java/im/vector/app/features/login2/AbstractSSOLoginFragment2.kt +++ b/vector/src/main/java/im/vector/app/features/login2/AbstractSSOLoginFragment2.kt @@ -27,7 +27,7 @@ import im.vector.app.core.utils.openUrlInChromeCustomTab import im.vector.app.features.login.hasSso import im.vector.app.features.login.ssoIdentityProviders -abstract class AbstractSSOLoginFragment2 : AbstractLoginFragment2() { +abstract class AbstractSSOLoginFragment2 : AbstractLoginFragment2() { // For sso private var customTabsServiceConnection: CustomTabsServiceConnection? = null diff --git a/vector/src/main/java/im/vector/app/features/login2/LoginActivity2.kt b/vector/src/main/java/im/vector/app/features/login2/LoginActivity2.kt index 60eb1934d0a..a640f022790 100644 --- a/vector/src/main/java/im/vector/app/features/login2/LoginActivity2.kt +++ b/vector/src/main/java/im/vector/app/features/login2/LoginActivity2.kt @@ -21,7 +21,6 @@ import android.content.Intent import android.view.View import android.view.ViewGroup import androidx.annotation.CallSuper -import com.google.android.material.appbar.MaterialToolbar import androidx.core.view.ViewCompat import androidx.core.view.children import androidx.core.view.isVisible @@ -29,6 +28,7 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentTransaction import com.airbnb.mvrx.viewModel +import com.google.android.material.appbar.MaterialToolbar import com.google.android.material.dialog.MaterialAlertDialogBuilder import im.vector.app.R import im.vector.app.core.di.ScreenComponent @@ -52,7 +52,6 @@ import im.vector.app.features.login.terms.toLocalizedLoginTerms import im.vector.app.features.login2.created.AccountCreatedFragment import im.vector.app.features.login2.terms.LoginTermsFragment2 import im.vector.app.features.pin.UnlockedActivity - import org.matrix.android.sdk.api.auth.registration.FlowResult import org.matrix.android.sdk.api.auth.registration.Stage import org.matrix.android.sdk.api.extensions.tryOrNull diff --git a/vector/src/main/java/im/vector/app/features/login2/LoginFragmentSigninUsername2.kt b/vector/src/main/java/im/vector/app/features/login2/LoginFragmentSigninUsername2.kt index 10fe0aae3a8..95862a4e909 100644 --- a/vector/src/main/java/im/vector/app/features/login2/LoginFragmentSigninUsername2.kt +++ b/vector/src/main/java/im/vector/app/features/login2/LoginFragmentSigninUsername2.kt @@ -95,9 +95,9 @@ class LoginFragmentSigninUsername2 @Inject constructor() : AbstractLoginFragment } override fun onError(throwable: Throwable) { - if (throwable is Failure.ServerError - && throwable.error.code == MatrixError.M_FORBIDDEN - && throwable.error.message.isEmpty()) { + if (throwable is Failure.ServerError && + throwable.error.code == MatrixError.M_FORBIDDEN && + throwable.error.message.isEmpty()) { // Login with email, but email unknown views.loginFieldTil.error = getString(R.string.login_login_with_email_error) } else { diff --git a/vector/src/main/java/im/vector/app/features/login2/LoginFragmentToAny2.kt b/vector/src/main/java/im/vector/app/features/login2/LoginFragmentToAny2.kt index 8a12b7e9b67..a889c870a0b 100644 --- a/vector/src/main/java/im/vector/app/features/login2/LoginFragmentToAny2.kt +++ b/vector/src/main/java/im/vector/app/features/login2/LoginFragmentToAny2.kt @@ -162,13 +162,13 @@ class LoginFragmentToAny2 @Inject constructor() : AbstractSSOLoginFragment2) : LoginViewEvents2() @@ -57,7 +57,7 @@ sealed class LoginViewEvents2 : VectorViewEvents { data class OnWebLoginError(val errorCode: Int, val description: String, val failingUrl: String) : LoginViewEvents2() - data class OnSessionCreated(val newAccount: Boolean): LoginViewEvents2() + data class OnSessionCreated(val newAccount: Boolean) : LoginViewEvents2() object Finish : LoginViewEvents2() } diff --git a/vector/src/main/java/im/vector/app/features/login2/LoginViewModel2.kt b/vector/src/main/java/im/vector/app/features/login2/LoginViewModel2.kt index e3f97a1c01f..2c063528683 100644 --- a/vector/src/main/java/im/vector/app/features/login2/LoginViewModel2.kt +++ b/vector/src/main/java/im/vector/app/features/login2/LoginViewModel2.kt @@ -587,16 +587,16 @@ class LoginViewModel2 @AssistedInject constructor( return@launch } when (data) { - is WellknownResult.Prompt -> + is WellknownResult.Prompt -> onWellknownSuccess(action, data, homeServerConnectionConfig) - is WellknownResult.FailPrompt -> + is WellknownResult.FailPrompt -> // Relax on IS discovery if homeserver is valid if (data.homeServerUrl != null && data.wellKnown != null) { onWellknownSuccess(action, WellknownResult.Prompt(data.homeServerUrl!!, null, data.wellKnown!!), homeServerConnectionConfig) } else { onWellKnownError() } - else -> { + else -> { onWellKnownError() } }.exhaustive @@ -630,11 +630,11 @@ class LoginViewModel2 @AssistedInject constructor( } ?: return val loginMode = when { - data.supportedLoginTypes.contains(LoginFlowTypes.SSO) - && data.supportedLoginTypes.contains(LoginFlowTypes.PASSWORD) -> LoginMode.SsoAndPassword(data.ssoIdentityProviders) - data.supportedLoginTypes.contains(LoginFlowTypes.SSO) -> LoginMode.Sso(data.ssoIdentityProviders) - data.supportedLoginTypes.contains(LoginFlowTypes.PASSWORD) -> LoginMode.Password - else -> LoginMode.Unsupported + data.supportedLoginTypes.contains(LoginFlowTypes.SSO) && + data.supportedLoginTypes.contains(LoginFlowTypes.PASSWORD) -> LoginMode.SsoAndPassword(data.ssoIdentityProviders) + data.supportedLoginTypes.contains(LoginFlowTypes.SSO) -> LoginMode.Sso(data.ssoIdentityProviders) + data.supportedLoginTypes.contains(LoginFlowTypes.PASSWORD) -> LoginMode.Password + else -> LoginMode.Unsupported } val viewEvent = when (loginMode) { @@ -662,8 +662,8 @@ class LoginViewModel2 @AssistedInject constructor( ) } - if ((loginMode == LoginMode.Password && !data.isLoginAndRegistrationSupported) - || data.isOutdatedHomeserver) { + if ((loginMode == LoginMode.Password && !data.isLoginAndRegistrationSupported) || + data.isOutdatedHomeserver) { // Notify the UI _viewEvents.post(LoginViewEvents2.OutdatedHomeserver) } @@ -688,8 +688,8 @@ class LoginViewModel2 @AssistedInject constructor( private fun onFlowResponse(flowResult: FlowResult) { // If dummy stage is mandatory, and password is already sent, do the dummy stage now - if (isRegistrationStarted - && flowResult.missingStages.any { it is Stage.Dummy && it.mandatory }) { + if (isRegistrationStarted && + flowResult.missingStages.any { it is Stage.Dummy && it.mandatory }) { handleRegisterDummy() } else { // Notify the user @@ -757,11 +757,11 @@ class LoginViewModel2 @AssistedInject constructor( rememberHomeServer(homeServerConnectionConfig.homeServerUri.toString()) val loginMode = when { - data.supportedLoginTypes.contains(LoginFlowTypes.SSO) - && data.supportedLoginTypes.contains(LoginFlowTypes.PASSWORD) -> LoginMode.SsoAndPassword(data.ssoIdentityProviders) - data.supportedLoginTypes.contains(LoginFlowTypes.SSO) -> LoginMode.Sso(data.ssoIdentityProviders) - data.supportedLoginTypes.contains(LoginFlowTypes.PASSWORD) -> LoginMode.Password - else -> LoginMode.Unsupported + data.supportedLoginTypes.contains(LoginFlowTypes.SSO) && + data.supportedLoginTypes.contains(LoginFlowTypes.PASSWORD) -> LoginMode.SsoAndPassword(data.ssoIdentityProviders) + data.supportedLoginTypes.contains(LoginFlowTypes.SSO) -> LoginMode.Sso(data.ssoIdentityProviders) + data.supportedLoginTypes.contains(LoginFlowTypes.PASSWORD) -> LoginMode.Password + else -> LoginMode.Unsupported } val viewEvent = when (loginMode) { @@ -802,8 +802,8 @@ class LoginViewModel2 @AssistedInject constructor( } viewEvent?.let { _viewEvents.post(it) } - if ((loginMode == LoginMode.Password && !data.isLoginAndRegistrationSupported) - || data.isOutdatedHomeserver) { + if ((loginMode == LoginMode.Password && !data.isLoginAndRegistrationSupported) || + data.isOutdatedHomeserver) { // Notify the UI _viewEvents.post(LoginViewEvents2.OutdatedHomeserver) } diff --git a/vector/src/main/java/im/vector/app/features/login2/LoginWebFragment2.kt b/vector/src/main/java/im/vector/app/features/login2/LoginWebFragment2.kt index b1e3eaf098d..b0741f65205 100644 --- a/vector/src/main/java/im/vector/app/features/login2/LoginWebFragment2.kt +++ b/vector/src/main/java/im/vector/app/features/login2/LoginWebFragment2.kt @@ -38,7 +38,6 @@ import im.vector.app.databinding.FragmentLoginWebBinding import im.vector.app.features.login.JavascriptResponse import im.vector.app.features.signout.soft.SoftLogoutAction import im.vector.app.features.signout.soft.SoftLogoutViewModel - import org.matrix.android.sdk.api.auth.data.Credentials import org.matrix.android.sdk.internal.di.MoshiProvider import timber.log.Timber diff --git a/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedFragment.kt b/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedFragment.kt index bb190943a7d..e3d25ef283a 100644 --- a/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedFragment.kt +++ b/vector/src/main/java/im/vector/app/features/login2/created/AccountCreatedFragment.kt @@ -33,6 +33,7 @@ import im.vector.app.core.intent.getFilenameFromUri import im.vector.app.core.resources.ColorProvider import im.vector.app.databinding.DialogBaseEditTextBinding import im.vector.app.databinding.FragmentLoginAccountCreatedBinding +import im.vector.app.features.displayname.getBestName import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorProvider import im.vector.app.features.login2.AbstractLoginFragment2 diff --git a/vector/src/main/java/im/vector/app/features/matrixto/MatrixToBottomSheet.kt b/vector/src/main/java/im/vector/app/features/matrixto/MatrixToBottomSheet.kt index 3e75b96c323..bf3e7a5d784 100644 --- a/vector/src/main/java/im/vector/app/features/matrixto/MatrixToBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/matrixto/MatrixToBottomSheet.kt @@ -36,7 +36,6 @@ import im.vector.app.databinding.BottomSheetMatrixToCardBinding import im.vector.app.features.home.AvatarRenderer import kotlinx.parcelize.Parcelize import org.matrix.android.sdk.api.session.permalinks.PermalinkData -import java.lang.ref.WeakReference import javax.inject.Inject import kotlin.reflect.KClass @@ -57,13 +56,7 @@ class MatrixToBottomSheet : injector.inject(this) } - private var weakReference = WeakReference(null) - - var interactionListener: InteractionListener? - set(value) { - weakReference = WeakReference(value) - } - get() = weakReference.get() + var interactionListener: InteractionListener? = null override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetMatrixToCardBinding { return BottomSheetMatrixToCardBinding.inflate(inflater, container, false) @@ -72,8 +65,8 @@ class MatrixToBottomSheet : private val viewModel by fragmentViewModel(MatrixToBottomSheetViewModel::class) interface InteractionListener { - fun navigateToRoom(roomId: String) - fun switchToSpace(spaceId: String) {} + fun mxToBottomSheetNavigateToRoom(roomId: String) + fun mxToBottomSheetSwitchToSpace(spaceId: String) } override fun invalidate() = withState(viewModel) { state -> @@ -112,12 +105,12 @@ class MatrixToBottomSheet : viewModel.observeViewEvents { when (it) { is MatrixToViewEvents.NavigateToRoom -> { - interactionListener?.navigateToRoom(it.roomId) + interactionListener?.mxToBottomSheetNavigateToRoom(it.roomId) dismiss() } MatrixToViewEvents.Dismiss -> dismiss() is MatrixToViewEvents.NavigateToSpace -> { - interactionListener?.switchToSpace(it.spaceId) + interactionListener?.mxToBottomSheetSwitchToSpace(it.spaceId) dismiss() } is MatrixToViewEvents.ShowModalError -> { @@ -131,14 +124,13 @@ class MatrixToBottomSheet : } companion object { - fun withLink(matrixToLink: String, listener: InteractionListener?): MatrixToBottomSheet { + fun withLink(matrixToLink: String): MatrixToBottomSheet { return MatrixToBottomSheet().apply { arguments = Bundle().apply { putParcelable(MvRx.KEY_ARG, MatrixToArgs( matrixToLink = matrixToLink )) } - interactionListener = listener } } } diff --git a/vector/src/main/java/im/vector/app/features/matrixto/MatrixToBottomSheetViewModel.kt b/vector/src/main/java/im/vector/app/features/matrixto/MatrixToBottomSheetViewModel.kt index 566cf769f22..f78724d4546 100644 --- a/vector/src/main/java/im/vector/app/features/matrixto/MatrixToBottomSheetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/matrixto/MatrixToBottomSheetViewModel.kt @@ -50,8 +50,8 @@ class MatrixToBottomSheetViewModel @AssistedInject constructor( private val session: Session, private val stringProvider: StringProvider, private val directRoomHelper: DirectRoomHelper, - private val errorFormatter: ErrorFormatter) - : VectorViewModel(initialState) { + private val errorFormatter: ErrorFormatter) : + VectorViewModel(initialState) { @AssistedFactory interface Factory { diff --git a/vector/src/main/java/im/vector/app/features/matrixto/MatrixToRoomSpaceFragment.kt b/vector/src/main/java/im/vector/app/features/matrixto/MatrixToRoomSpaceFragment.kt index 733eb0575e3..26df42dbe16 100644 --- a/vector/src/main/java/im/vector/app/features/matrixto/MatrixToRoomSpaceFragment.kt +++ b/vector/src/main/java/im/vector/app/features/matrixto/MatrixToRoomSpaceFragment.kt @@ -70,7 +70,6 @@ class MatrixToRoomSpaceFragment @Inject constructor( val matrixItem = peek.roomItem avatarRenderer.render(matrixItem, views.matrixToCardAvatar) if (peek.roomType == RoomType.SPACE) { - views.matrixToBetaTag.isVisible = true views.matrixToAccessImage.isVisible = true if (peek.isPublic) { views.matrixToAccessText.setTextOrHide(context?.getString(R.string.public_space)) @@ -79,8 +78,6 @@ class MatrixToRoomSpaceFragment @Inject constructor( views.matrixToAccessText.setTextOrHide(context?.getString(R.string.private_space)) views.matrixToAccessImage.setImageResource(R.drawable.ic_room_private) } - } else { - views.matrixToBetaTag.isVisible = false } views.matrixToCardNameText.setTextOrHide(peek.name) views.matrixToCardAliasText.setTextOrHide(peek.alias) diff --git a/vector/src/main/java/im/vector/app/features/matrixto/SpaceCardRenderer.kt b/vector/src/main/java/im/vector/app/features/matrixto/SpaceCardRenderer.kt index 00d107513c7..c56481d3f2a 100644 --- a/vector/src/main/java/im/vector/app/features/matrixto/SpaceCardRenderer.kt +++ b/vector/src/main/java/im/vector/app/features/matrixto/SpaceCardRenderer.kt @@ -50,7 +50,6 @@ class SpaceCardRenderer @Inject constructor( inCard.matrixToCardButtonLoading.isVisible = false avatarRenderer.render(spaceSummary.toMatrixItem(), inCard.matrixToCardAvatar) inCard.matrixToCardNameText.text = spaceSummary.name - inCard.matrixToBetaTag.isVisible = true inCard.matrixToCardAliasText.setTextOrHide(spaceSummary.canonicalAlias) inCard.matrixToCardDescText.setTextOrHide(spaceSummary.topic.linkify(matrixLinkCallback)) if (spaceSummary.isPublic) { @@ -97,7 +96,6 @@ class SpaceCardRenderer @Inject constructor( inCard.matrixToCardButtonLoading.isVisible = false avatarRenderer.render(spaceChildInfo.toMatrixItem(), inCard.matrixToCardAvatar) inCard.matrixToCardNameText.setTextOrHide(spaceChildInfo.name) - inCard.matrixToBetaTag.isVisible = true inCard.matrixToCardAliasText.setTextOrHide(spaceChildInfo.canonicalAlias) inCard.matrixToCardDescText.setTextOrHide(spaceChildInfo.topic?.linkify(matrixLinkCallback)) if (spaceChildInfo.worldReadable) { diff --git a/vector/src/main/java/im/vector/app/features/media/BigImageViewerActivity.kt b/vector/src/main/java/im/vector/app/features/media/BigImageViewerActivity.kt index 6b4ed34cfb0..28d16adcbb3 100644 --- a/vector/src/main/java/im/vector/app/features/media/BigImageViewerActivity.kt +++ b/vector/src/main/java/im/vector/app/features/media/BigImageViewerActivity.kt @@ -24,7 +24,6 @@ import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.ScreenComponent import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.databinding.ActivityBigImageViewerBinding - import javax.inject.Inject /** diff --git a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt index fd163f7a34c..55b74e9b348 100644 --- a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt +++ b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt @@ -257,17 +257,11 @@ class DefaultNavigator @Inject constructor( override fun openMatrixToBottomSheet(context: Context, link: String) { if (context is AppCompatActivity) { - val listener = object : MatrixToBottomSheet.InteractionListener { - override fun navigateToRoom(roomId: String) { - openRoom(context, roomId) - } - - override fun switchToSpace(spaceId: String) { - this@DefaultNavigator.switchToSpace(context, spaceId, Navigator.PostSwitchSpaceAction.None) - } + if (context !is MatrixToBottomSheet.InteractionListener) { + fatalError("Caller context should implement MatrixToBottomSheet.InteractionListener", vectorPreferences.failFast()) } // TODO check if there is already one?? - MatrixToBottomSheet.withLink(link, listener) + MatrixToBottomSheet.withLink(link) .show(context.supportFragmentManager, "HA#MatrixToBottomSheet") } } @@ -366,8 +360,8 @@ class DefaultNavigator @Inject constructor( override fun openKeysBackupSetup(context: Context, showManualExport: Boolean) { // if cross signing is enabled and trusted or not set up at all we should propose full 4S sessionHolder.getSafeActiveSession()?.let { session -> - if (session.cryptoService().crossSigningService().getMyCrossSigningKeys() == null - || session.cryptoService().crossSigningService().canCrossSign()) { + if (session.cryptoService().crossSigningService().getMyCrossSigningKeys() == null || + session.cryptoService().crossSigningService().canCrossSign()) { (context as? AppCompatActivity)?.let { BootstrapBottomSheet.show(it.supportFragmentManager, SetupMode.NORMAL) } diff --git a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt index ffe95627616..c11b8405229 100644 --- a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt +++ b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt @@ -23,6 +23,7 @@ import android.view.View import androidx.activity.result.ActivityResultLauncher import androidx.core.util.Pair import im.vector.app.features.crypto.recover.SetupMode +import im.vector.app.features.displayname.getBestName import im.vector.app.features.login.LoginConfig import im.vector.app.features.media.AttachmentData import im.vector.app.features.pin.PinMode diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventResolver.kt b/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventResolver.kt index a19d579c053..63c296f418a 100644 --- a/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventResolver.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/NotifiableEventResolver.kt @@ -19,6 +19,7 @@ import androidx.core.app.NotificationCompat import im.vector.app.BuildConfig import im.vector.app.R import im.vector.app.core.resources.StringProvider +import im.vector.app.features.displayname.getBestName import im.vector.app.features.home.room.detail.timeline.format.DisplayableEventFormatter import im.vector.app.features.home.room.detail.timeline.format.NoticeEventFormatter import org.matrix.android.sdk.api.extensions.orFalse @@ -34,6 +35,7 @@ import org.matrix.android.sdk.api.session.room.model.RoomMemberContent import org.matrix.android.sdk.api.session.room.sender.SenderInfo import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import org.matrix.android.sdk.api.session.room.timeline.getEditedEventId +import org.matrix.android.sdk.api.util.toMatrixItem import org.matrix.android.sdk.internal.crypto.algorithms.olm.OlmDecryptionResult import timber.log.Timber import java.util.UUID @@ -107,7 +109,7 @@ class NotifiableEventResolver @Inject constructor( displayIndex = 0, senderInfo = SenderInfo( userId = user.userId, - displayName = user.getBestName(), + displayName = user.toMatrixItem().getBestName(), isUniqueDisplayName = true, avatarUrl = user.avatarUrl ) diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotificationDrawerManager.kt b/vector/src/main/java/im/vector/app/features/notifications/NotificationDrawerManager.kt index fea4716b39e..37de7b1a755 100644 --- a/vector/src/main/java/im/vector/app/features/notifications/NotificationDrawerManager.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/NotificationDrawerManager.kt @@ -27,11 +27,13 @@ import im.vector.app.BuildConfig import im.vector.app.R import im.vector.app.core.resources.StringProvider import im.vector.app.core.utils.FirstThrottler +import im.vector.app.features.displayname.getBestName import im.vector.app.features.invite.AutoAcceptInvites import im.vector.app.features.settings.VectorPreferences import me.gujun.android.span.span import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.content.ContentUrlResolver +import org.matrix.android.sdk.api.util.toMatrixItem import timber.log.Timber import java.io.File import java.io.FileOutputStream @@ -119,9 +121,9 @@ class NotificationDrawerManager @Inject constructor(private val context: Context // This is an edition val eventBeforeEdition = eventList.firstOrNull { // Edition of an event - it.eventId == notifiableEvent.editedEventId + it.eventId == notifiableEvent.editedEventId || // or edition of an edition - || it.editedEventId == notifiableEvent.editedEventId + it.editedEventId == notifiableEvent.editedEventId } if (eventBeforeEdition != null) { @@ -228,7 +230,7 @@ class NotificationDrawerManager @Inject constructor(private val context: Context val user = session.getUser(session.myUserId) // myUserDisplayName cannot be empty else NotificationCompat.MessagingStyle() will crash - val myUserDisplayName = user?.getBestName() ?: session.myUserId + val myUserDisplayName = user?.toMatrixItem()?.getBestName() ?: session.myUserId val myUserAvatarUrl = session.contentUrlResolver().resolveThumbnail(user?.avatarUrl, avatarSize, avatarSize, ContentUrlResolver.ThumbnailMethod.SCALE) synchronized(eventList) { Timber.v("%%%%%%%% REFRESH NOTIFICATION DRAWER ") @@ -458,7 +460,7 @@ class NotificationDrawerManager @Inject constructor(private val context: Context if (eventList.isEmpty() || eventList.all { it.isRedacted }) { notificationUtils.cancelNotificationMessage(null, SUMMARY_NOTIFICATION_ID) - } else { + } else if (hasNewEvent) { // FIXME roomIdToEventMap.size is not correct, this is the number of rooms val nbEvents = roomIdToEventMap.size + simpleEvents.size val sumTitle = stringProvider.getQuantityString(R.plurals.notification_compat_summary_title, nbEvents, nbEvents) diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt b/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt index a74c13a4969..14dfe5c6ee8 100755 --- a/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt +++ b/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt @@ -53,6 +53,7 @@ import im.vector.app.core.utils.startNotificationChannelSettingsIntent import im.vector.app.features.call.VectorCallActivity import im.vector.app.features.call.service.CallHeadsUpActionReceiver import im.vector.app.features.call.webrtc.WebRtcCall +import im.vector.app.features.displayname.getBestName import im.vector.app.features.home.HomeActivity import im.vector.app.features.home.room.detail.RoomDetailActivity import im.vector.app.features.home.room.detail.RoomDetailArgs @@ -468,7 +469,6 @@ class NotificationUtils @Inject constructor(private val context: Context, setSmallIcon(R.drawable.ic_call_answer) } } - // This is a trick to make the previous notification with same id disappear as cancel notification is not working with Foreground Service. .setTimeoutAfter(1) .setColor(ThemeUtils.getColor(context, android.R.attr.colorPrimary)) .setCategory(NotificationCompat.CATEGORY_CALL) @@ -544,36 +544,27 @@ class NotificationUtils @Inject constructor(private val context: Context, .setWhen(lastMessageTimestamp) // MESSAGING_STYLE sets title and content for API 16 and above devices. .setStyle(messageStyle) - // A category allows groups of notifications to be ranked and filtered – per user or system settings. // For example, alarm notifications should display before promo notifications, or message from known contact // that can be displayed in not disturb mode if white listed (the later will need compat28.x) .setCategory(NotificationCompat.CATEGORY_MESSAGE) - // ID of the corresponding shortcut, for conversation features under API 30+ .setShortcutId(roomInfo.roomId) - // Title for API < 16 devices. .setContentTitle(roomInfo.roomDisplayName) // Content for API < 16 devices. .setContentText(stringProvider.getString(R.string.notification_new_messages)) - // Number of new notifications for API <24 (M and below) devices. .setSubText(stringProvider.getQuantityString(R.plurals.room_new_messages_notification, messageStyle.messages.size, messageStyle.messages.size)) - // Auto-bundling is enabled for 4 or more notifications on API 24+ (N+) // devices and all Wear devices. But we want a custom grouping, so we specify the groupID // TODO Group should be current user display name .setGroup(stringProvider.getString(R.string.app_name)) - // In order to avoid notification making sound twice (due to the summary notification) .setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY) - .setSmallIcon(smallIcon) - // Set primary color (important for Wear 2.0 Notifications). .setColor(accentColor) - // Sets priority for 25 and below. For 26 and above, 'priority' is deprecated for // 'importance' which is set in the NotificationChannel. The integers representing // 'priority' are different from 'importance', so make sure you don't mix them. @@ -598,10 +589,12 @@ class NotificationUtils @Inject constructor(private val context: Context, val markRoomReadPendingIntent = PendingIntent.getBroadcast(context, System.currentTimeMillis().toInt(), markRoomReadIntent, PendingIntent.FLAG_UPDATE_CURRENT) - addAction(NotificationCompat.Action( - R.drawable.ic_material_done_all_white, - stringProvider.getString(R.string.action_mark_room_read), - markRoomReadPendingIntent)) + NotificationCompat.Action.Builder(R.drawable.ic_material_done_all_white, + stringProvider.getString(R.string.action_mark_room_read), markRoomReadPendingIntent) + .setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_MARK_AS_READ) + .setShowsUserInterface(false) + .build() + .let { addAction(it) } // Quick reply if (!roomInfo.hasSmartReplyError) { @@ -612,6 +605,8 @@ class NotificationUtils @Inject constructor(private val context: Context, NotificationCompat.Action.Builder(R.drawable.vector_notification_quick_reply, stringProvider.getString(R.string.action_quick_reply), replyPendingIntent) .addRemoteInput(remoteInput) + .setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_REPLY) + .setShowsUserInterface(false) .build() .let { addAction(it) } } @@ -679,7 +674,7 @@ class NotificationUtils @Inject constructor(private val context: Context, stringProvider.getString(R.string.join), joinIntentPendingIntent) - val contentIntent = HomeActivity.newIntent(context) + val contentIntent = HomeActivity.newIntent(context, inviteNotificationRoomId = inviteNotifiableEvent.roomId) contentIntent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP // pending intent get reused by system, this will mess up the extra params, so put unique info to avoid that contentIntent.data = Uri.parse("foobar://" + inviteNotifiableEvent.eventId) @@ -919,8 +914,8 @@ class NotificationUtils @Inject constructor(private val context: Context, // We cannot use NotificationManagerCompat here. val setting = context.getSystemService()!!.currentInterruptionFilter - return setting == NotificationManager.INTERRUPTION_FILTER_NONE - || setting == NotificationManager.INTERRUPTION_FILTER_ALARMS + return setting == NotificationManager.INTERRUPTION_FILTER_NONE || + setting == NotificationManager.INTERRUPTION_FILTER_ALARMS } private fun getActionText(@StringRes stringRes: Int, @AttrRes colorRes: Int): Spannable { diff --git a/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandler.kt b/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandler.kt index ecaeea18991..7469f445837 100644 --- a/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandler.kt +++ b/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandler.kt @@ -29,6 +29,7 @@ import io.reactivex.schedulers.Schedulers import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.permalinks.PermalinkData import org.matrix.android.sdk.api.session.permalinks.PermalinkParser +import org.matrix.android.sdk.api.session.permalinks.PermalinkService import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.RoomType import org.matrix.android.sdk.api.util.Optional @@ -55,7 +56,7 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti navigationInterceptor: NavigationInterceptor? = null, buildTask: Boolean = false ): Single { - if (deepLink == null) { + if (deepLink == null || !isPermalinkSupported(context, deepLink.toString())) { return Single.just(false) } return Single @@ -122,6 +123,13 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti } } + private fun isPermalinkSupported(context: Context, url: String): Boolean { + return url.startsWith(PermalinkService.MATRIX_TO_URL_BASE) || + context.resources.getStringArray(R.array.permalink_supported_hosts).any { + url.startsWith(it) + } + } + private fun PermalinkData.RoomLink.getRoomId(): Single> { val session = activeSessionHolder.getSafeActiveSession() return if (isRoomAlias && session != null) { @@ -179,6 +187,12 @@ class PermalinkHandler @Inject constructor(private val activeSessionHolder: Acti } } } + + companion object { + const val MATRIX_TO_CUSTOM_SCHEME_URL_BASE = "element://" + const val ROOM_LINK_PREFIX = "${MATRIX_TO_CUSTOM_SCHEME_URL_BASE}room/" + const val USER_LINK_PREFIX = "${MATRIX_TO_CUSTOM_SCHEME_URL_BASE}user/" + } } interface NavigationInterceptor { diff --git a/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandlerActivity.kt b/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandlerActivity.kt deleted file mode 100644 index ee4e0e05b56..00000000000 --- a/vector/src/main/java/im/vector/app/features/permalink/PermalinkHandlerActivity.kt +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2019 New Vector Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package im.vector.app.features.permalink - -import android.content.Intent -import android.os.Bundle -import im.vector.app.R -import im.vector.app.core.di.ActiveSessionHolder -import im.vector.app.core.di.ScreenComponent -import im.vector.app.core.extensions.replaceFragment -import im.vector.app.core.platform.VectorBaseActivity -import im.vector.app.databinding.FragmentProgressBinding -import im.vector.app.features.home.HomeActivity -import im.vector.app.features.home.LoadingFragment -import javax.inject.Inject - -class PermalinkHandlerActivity : VectorBaseActivity() { - - @Inject lateinit var permalinkHandler: PermalinkHandler - @Inject lateinit var sessionHolder: ActiveSessionHolder - - override fun getBinding() = FragmentProgressBinding.inflate(layoutInflater) - - override fun injectWith(injector: ScreenComponent) { - injector.inject(this) - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_simple) - if (isFirstCreation()) { - replaceFragment(R.id.simpleFragmentContainer, LoadingFragment::class.java) - } - handleIntent() - } - - private fun handleIntent() { - // If we are not logged in, open login screen. - // In the future, we might want to relaunch the process after login. - if (!sessionHolder.hasActiveSession()) { - startLoginActivity() - return - } - // We forward intent to HomeActivity (singleTask) to avoid the dueling app problem - // https://stackoverflow.com/questions/25884954/deep-linking-and-multiple-app-instances - intent.setClass(this, HomeActivity::class.java) - intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP - startActivity(intent) - - finish() - } - - override fun onNewIntent(intent: Intent?) { - super.onNewIntent(intent) - handleIntent() - } - - private fun startLoginActivity() { - navigator.openLogin( - context = this, - flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK - ) - finish() - } -} diff --git a/vector/src/main/java/im/vector/app/features/pin/PinActivity.kt b/vector/src/main/java/im/vector/app/features/pin/PinActivity.kt index 6866afa0a69..a36e9456994 100644 --- a/vector/src/main/java/im/vector/app/features/pin/PinActivity.kt +++ b/vector/src/main/java/im/vector/app/features/pin/PinActivity.kt @@ -18,8 +18,8 @@ package im.vector.app.features.pin import android.content.Context import android.content.Intent -import com.google.android.material.appbar.MaterialToolbar import com.airbnb.mvrx.MvRx +import com.google.android.material.appbar.MaterialToolbar import im.vector.app.R import im.vector.app.core.extensions.addFragment import im.vector.app.core.platform.ToolbarConfigurable diff --git a/vector/src/main/java/im/vector/app/features/pin/PinFragment.kt b/vector/src/main/java/im/vector/app/features/pin/PinFragment.kt index 99b0a935283..2309d42f60b 100644 --- a/vector/src/main/java/im/vector/app/features/pin/PinFragment.kt +++ b/vector/src/main/java/im/vector/app/features/pin/PinFragment.kt @@ -36,8 +36,8 @@ import im.vector.app.databinding.FragmentPinBinding import im.vector.app.features.MainActivity import im.vector.app.features.MainActivityArgs import im.vector.app.features.settings.VectorPreferences -import kotlinx.parcelize.Parcelize import kotlinx.coroutines.launch +import kotlinx.parcelize.Parcelize import javax.inject.Inject @Parcelize diff --git a/vector/src/main/java/im/vector/app/features/popup/IncomingCallAlert.kt b/vector/src/main/java/im/vector/app/features/popup/IncomingCallAlert.kt index 17ad35f07ac..3b7aa18ee9c 100644 --- a/vector/src/main/java/im/vector/app/features/popup/IncomingCallAlert.kt +++ b/vector/src/main/java/im/vector/app/features/popup/IncomingCallAlert.kt @@ -22,6 +22,7 @@ import im.vector.app.R import im.vector.app.core.extensions.setLeftDrawable import im.vector.app.core.glide.GlideApp import im.vector.app.databinding.AlerterIncomingCallLayoutBinding +import im.vector.app.features.displayname.getBestName import im.vector.app.features.home.AvatarRenderer import org.matrix.android.sdk.api.util.MatrixItem diff --git a/vector/src/main/java/im/vector/app/features/popup/JitsiCallAlert.kt b/vector/src/main/java/im/vector/app/features/popup/JitsiCallAlert.kt index 1c0ec65c365..00c429a6212 100644 --- a/vector/src/main/java/im/vector/app/features/popup/JitsiCallAlert.kt +++ b/vector/src/main/java/im/vector/app/features/popup/JitsiCallAlert.kt @@ -21,6 +21,7 @@ import android.view.View import im.vector.app.R import im.vector.app.core.glide.GlideApp import im.vector.app.databinding.AlerterJitsiCallLayoutBinding +import im.vector.app.features.displayname.getBestName import im.vector.app.features.home.AvatarRenderer import org.matrix.android.sdk.api.util.MatrixItem diff --git a/vector/src/main/java/im/vector/app/features/popup/PopupAlertManager.kt b/vector/src/main/java/im/vector/app/features/popup/PopupAlertManager.kt index 9842709f9e9..9084c64a402 100644 --- a/vector/src/main/java/im/vector/app/features/popup/PopupAlertManager.kt +++ b/vector/src/main/java/im/vector/app/features/popup/PopupAlertManager.kt @@ -52,7 +52,7 @@ class PopupAlertManager @Inject constructor() { private val alertQueue = mutableListOf() - fun hasAlertsToShow() : Boolean { + fun hasAlertsToShow(): Boolean { return currentAlerter != null || alertQueue.isNotEmpty() } @@ -298,10 +298,10 @@ class PopupAlertManager @Inject constructor() { } private fun shouldBeDisplayedIn(alert: VectorAlert?, activity: Activity): Boolean { - return alert != null - && activity !is PinActivity - && activity !is SignedOutActivity - && activity is VectorBaseActivity<*> - && alert.shouldBeDisplayedIn.invoke(activity) + return alert != null && + activity !is PinActivity && + activity !is SignedOutActivity && + activity is VectorBaseActivity<*> && + alert.shouldBeDisplayedIn.invoke(activity) } } diff --git a/vector/src/main/java/im/vector/app/features/qrcode/QrCodeScannerFragment.kt b/vector/src/main/java/im/vector/app/features/qrcode/QrCodeScannerFragment.kt index 1455a85edf2..3a3841c0267 100644 --- a/vector/src/main/java/im/vector/app/features/qrcode/QrCodeScannerFragment.kt +++ b/vector/src/main/java/im/vector/app/features/qrcode/QrCodeScannerFragment.kt @@ -21,12 +21,11 @@ import android.view.ViewGroup import com.google.zxing.Result import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.databinding.FragmentQrCodeScannerBinding - import me.dm7.barcodescanner.zxing.ZXingScannerView import javax.inject.Inject -class QrCodeScannerFragment @Inject constructor() - : VectorBaseFragment(), +class QrCodeScannerFragment @Inject constructor() : + VectorBaseFragment(), ZXingScannerView.ResultHandler { override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentQrCodeScannerBinding { diff --git a/vector/src/main/java/im/vector/app/features/rageshake/BugReportActivity.kt b/vector/src/main/java/im/vector/app/features/rageshake/BugReportActivity.kt index 27adefd50fd..89aa307dc45 100755 --- a/vector/src/main/java/im/vector/app/features/rageshake/BugReportActivity.kt +++ b/vector/src/main/java/im/vector/app/features/rageshake/BugReportActivity.kt @@ -30,7 +30,6 @@ import im.vector.app.core.di.ScreenComponent import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.databinding.ActivityBugReportBinding import org.matrix.android.sdk.api.extensions.tryOrNull - import timber.log.Timber import javax.inject.Inject diff --git a/vector/src/main/java/im/vector/app/features/rageshake/BugReporter.kt b/vector/src/main/java/im/vector/app/features/rageshake/BugReporter.kt index 9ae1d37f4d2..5b3d194d335 100755 --- a/vector/src/main/java/im/vector/app/features/rageshake/BugReporter.kt +++ b/vector/src/main/java/im/vector/app/features/rageshake/BugReporter.kt @@ -267,8 +267,8 @@ class BugReporter @Inject constructor( .addFormDataPart("device", Build.MODEL.trim()) .addFormDataPart("verbose_log", vectorPreferences.labAllowedExtendedLogging().toOnOff()) .addFormDataPart("multi_window", inMultiWindowMode.toOnOff()) - .addFormDataPart("os", Build.VERSION.RELEASE + " (API " + Build.VERSION.SDK_INT + ") " - + Build.VERSION.INCREMENTAL + "-" + Build.VERSION.CODENAME) + .addFormDataPart("os", Build.VERSION.RELEASE + " (API " + Build.VERSION.SDK_INT + ") " + + Build.VERSION.INCREMENTAL + "-" + Build.VERSION.CODENAME) .addFormDataPart("locale", Locale.getDefault().toString()) .addFormDataPart("app_language", VectorLocale.applicationLocale.toString()) .addFormDataPart("default_app_language", systemLocaleProvider.getSystemLocale().toString()) diff --git a/vector/src/main/java/im/vector/app/features/reactions/EmojiChooserFragment.kt b/vector/src/main/java/im/vector/app/features/reactions/EmojiChooserFragment.kt index 51dc62af8bc..bcc18a995a9 100644 --- a/vector/src/main/java/im/vector/app/features/reactions/EmojiChooserFragment.kt +++ b/vector/src/main/java/im/vector/app/features/reactions/EmojiChooserFragment.kt @@ -23,7 +23,6 @@ import androidx.lifecycle.lifecycleScope import im.vector.app.core.extensions.cleanup import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.databinding.EmojiChooserFragmentBinding - import javax.inject.Inject class EmojiChooserFragment @Inject constructor( @@ -41,15 +40,15 @@ class EmojiChooserFragment @Inject constructor( override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) viewModel = activityViewModelProvider.get(EmojiChooserViewModel::class.java) - emojiRecyclerAdapter.reactionClickListener = this emojiRecyclerAdapter.interactionListener = this - views.emojiRecyclerView.adapter = emojiRecyclerAdapter - viewModel.moveToSection.observe(viewLifecycleOwner) { section -> emojiRecyclerAdapter.scrollToSection(section) } + viewModel.emojiData.observe(viewLifecycleOwner) { + emojiRecyclerAdapter.update(it) + } } override fun getCoroutineScope() = lifecycleScope diff --git a/vector/src/main/java/im/vector/app/features/reactions/EmojiChooserViewModel.kt b/vector/src/main/java/im/vector/app/features/reactions/EmojiChooserViewModel.kt index df2085e41ba..3a4caa296a5 100644 --- a/vector/src/main/java/im/vector/app/features/reactions/EmojiChooserViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/reactions/EmojiChooserViewModel.kt @@ -17,11 +17,16 @@ package im.vector.app.features.reactions import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope import im.vector.app.core.utils.LiveEvent +import im.vector.app.features.reactions.data.EmojiData +import im.vector.app.features.reactions.data.EmojiDataSource +import kotlinx.coroutines.launch import javax.inject.Inject -class EmojiChooserViewModel @Inject constructor() : ViewModel() { +class EmojiChooserViewModel @Inject constructor(private val emojiDataSource: EmojiDataSource) : ViewModel() { + val emojiData: MutableLiveData = MutableLiveData() val navigateEvent: MutableLiveData> = MutableLiveData() var selectedReaction: String? = null var eventId: String? = null @@ -29,6 +34,17 @@ class EmojiChooserViewModel @Inject constructor() : ViewModel() { val currentSection: MutableLiveData = MutableLiveData() val moveToSection: MutableLiveData = MutableLiveData() + init { + loadEmojiData() + } + + private fun loadEmojiData() { + viewModelScope.launch { + val rawData = emojiDataSource.rawData.await() + emojiData.postValue(rawData) + } + } + fun onReactionSelected(reaction: String) { selectedReaction = reaction navigateEvent.value = LiveEvent(NAVIGATE_FINISH) diff --git a/vector/src/main/java/im/vector/app/features/reactions/EmojiReactionPickerActivity.kt b/vector/src/main/java/im/vector/app/features/reactions/EmojiReactionPickerActivity.kt index ecfaf937475..e371aae096c 100644 --- a/vector/src/main/java/im/vector/app/features/reactions/EmojiReactionPickerActivity.kt +++ b/vector/src/main/java/im/vector/app/features/reactions/EmojiReactionPickerActivity.kt @@ -25,6 +25,7 @@ import android.view.MenuInflater import android.view.MenuItem import android.widget.SearchView import androidx.core.view.isVisible +import androidx.lifecycle.lifecycleScope import com.airbnb.mvrx.viewModel import com.google.android.material.tabs.TabLayout import com.jakewharton.rxbinding3.widget.queryTextChanges @@ -36,7 +37,7 @@ import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.databinding.ActivityEmojiReactionPickerBinding import im.vector.app.features.reactions.data.EmojiDataSource import io.reactivex.android.schedulers.AndroidSchedulers - +import kotlinx.coroutines.launch import timber.log.Timber import java.util.concurrent.TimeUnit import javax.inject.Inject @@ -91,17 +92,19 @@ class EmojiReactionPickerActivity : VectorBaseActivity - val s = category.emojis[0] - views.tabs.newTab() - .also { tab -> - tab.text = emojiDataSource.rawData.emojis[s]!!.emoji - tab.contentDescription = category.name - } - .also { tab -> - views.tabs.addTab(tab) - } + lifecycleScope.launch { + val rawData = emojiDataSource.rawData.await() + rawData.categories.forEach { category -> + val s = category.emojis[0] + views.tabs.newTab() + .also { tab -> + tab.text = rawData.emojis[s]!!.emoji + tab.contentDescription = category.name + } + .also { tab -> + views.tabs.addTab(tab) + } + } } views.tabs.addOnTabSelectedListener(tabLayoutSelectionListener) @@ -161,6 +164,12 @@ class EmojiReactionPickerActivity : VectorBaseActivity Timber.e(err) } @@ -170,6 +179,7 @@ class EmojiReactionPickerActivity : VectorBaseActivity() { var reactionClickListener: ReactionClickListener? = null var interactionListener: InteractionListener? = null + + private var rawData: EmojiData = EmojiData(emptyList(), emptyMap(), emptyMap()) private var mRecyclerView: RecyclerView? = null private var currentFirstVisibleSection = 0 @@ -61,6 +62,12 @@ class EmojiRecyclerAdapter @Inject constructor( UNKNOWN } + @SuppressLint("NotifyDataSetChanged") + fun update(emojiData: EmojiData) { + rawData = emojiData + notifyDataSetChanged() + } + private var scrollState = ScrollState.UNKNOWN private var isFastScroll = false @@ -71,10 +78,10 @@ class EmojiRecyclerAdapter @Inject constructor( if (itemPosition != RecyclerView.NO_POSITION) { val sectionNumber = getSectionForAbsoluteIndex(itemPosition) if (!isSection(itemPosition)) { - val sectionMojis = dataSource.rawData.categories[sectionNumber].emojis + val sectionMojis = rawData.categories[sectionNumber].emojis val sectionOffset = getSectionOffset(sectionNumber) val emoji = sectionMojis[itemPosition - sectionOffset] - val item = dataSource.rawData.emojis.getValue(emoji).emoji + val item = rawData.emojis.getValue(emoji).emoji reactionClickListener?.onReactionSelected(item) } } @@ -115,7 +122,7 @@ class EmojiRecyclerAdapter @Inject constructor( } fun scrollToSection(section: Int) { - if (section < 0 || section >= dataSource.rawData.categories.size) { + if (section < 0 || section >= rawData.categories.size) { // ignore return } @@ -149,7 +156,7 @@ class EmojiRecyclerAdapter @Inject constructor( private fun isSection(position: Int): Boolean { var sectionOffset = 1 var lastItemInSection: Int - dataSource.rawData.categories.forEach { category -> + rawData.categories.forEach { category -> lastItemInSection = sectionOffset + category.emojis.size - 1 if (position == sectionOffset - 1) return true sectionOffset = lastItemInSection + 2 @@ -161,7 +168,7 @@ class EmojiRecyclerAdapter @Inject constructor( var sectionOffset = 1 var lastItemInSection: Int var index = 0 - dataSource.rawData.categories.forEach { category -> + rawData.categories.forEach { category -> lastItemInSection = sectionOffset + category.emojis.size - 1 if (position <= lastItemInSection) return index sectionOffset = lastItemInSection + 2 @@ -174,7 +181,7 @@ class EmojiRecyclerAdapter @Inject constructor( // Todo cache this for fast access var sectionOffset = 1 var lastItemInSection: Int - dataSource.rawData.categories.forEachIndexed { index, category -> + rawData.categories.forEachIndexed { index, category -> lastItemInSection = sectionOffset + category.emojis.size - 1 if (section == index) return sectionOffset sectionOffset = lastItemInSection + 2 @@ -186,12 +193,12 @@ class EmojiRecyclerAdapter @Inject constructor( Trace.beginSection("MyAdapter.onBindViewHolder") val sectionNumber = getSectionForAbsoluteIndex(position) if (isSection(position)) { - holder.bind(dataSource.rawData.categories[sectionNumber].name) + holder.bind(rawData.categories[sectionNumber].name) } else { - val sectionMojis = dataSource.rawData.categories[sectionNumber].emojis + val sectionMojis = rawData.categories[sectionNumber].emojis val sectionOffset = getSectionOffset(sectionNumber) val emoji = sectionMojis[position - sectionOffset] - val item = dataSource.rawData.emojis[emoji]!!.emoji + val item = rawData.emojis[emoji]!!.emoji (holder as EmojiViewHolder).data = item if (scrollState != ScrollState.SETTLING || !isFastScroll) { // Log.i("PERF","Bind with draw at position:$position") @@ -220,7 +227,7 @@ class EmojiRecyclerAdapter @Inject constructor( super.onViewRecycled(holder) } - override fun getItemCount() = dataSource.rawData.categories + override fun getItemCount() = rawData.categories .sumOf { emojiCategory -> 1 /* Section */ + emojiCategory.emojis.size } abstract class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { diff --git a/vector/src/main/java/im/vector/app/features/reactions/EmojiSearchResultFragment.kt b/vector/src/main/java/im/vector/app/features/reactions/EmojiSearchResultFragment.kt index bcd74658870..9292ad8fc64 100644 --- a/vector/src/main/java/im/vector/app/features/reactions/EmojiSearchResultFragment.kt +++ b/vector/src/main/java/im/vector/app/features/reactions/EmojiSearchResultFragment.kt @@ -27,7 +27,6 @@ import im.vector.app.core.extensions.configureWith import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.utils.LiveEvent import im.vector.app.databinding.FragmentGenericRecyclerBinding - import javax.inject.Inject class EmojiSearchResultFragment @Inject constructor( diff --git a/vector/src/main/java/im/vector/app/features/reactions/EmojiSearchResultViewModel.kt b/vector/src/main/java/im/vector/app/features/reactions/EmojiSearchResultViewModel.kt index ac7aee797ad..4d1d0adf23c 100644 --- a/vector/src/main/java/im/vector/app/features/reactions/EmojiSearchResultViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/reactions/EmojiSearchResultViewModel.kt @@ -15,17 +15,19 @@ */ package im.vector.app.features.reactions +import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel import im.vector.app.features.reactions.data.EmojiDataSource import im.vector.app.features.reactions.data.EmojiItem +import kotlinx.coroutines.launch data class EmojiSearchResultViewState( val query: String = "", @@ -34,8 +36,8 @@ data class EmojiSearchResultViewState( class EmojiSearchResultViewModel @AssistedInject constructor( @Assisted initialState: EmojiSearchResultViewState, - private val dataSource: EmojiDataSource) - : VectorViewModel(initialState) { + private val dataSource: EmojiDataSource) : + VectorViewModel(initialState) { @AssistedFactory interface Factory { @@ -58,11 +60,14 @@ class EmojiSearchResultViewModel @AssistedInject constructor( } private fun updateQuery(action: EmojiSearchAction.UpdateQuery) { - setState { - copy( - query = action.queryString, - results = dataSource.filterWith(action.queryString) - ) + viewModelScope.launch { + val results = dataSource.filterWith(action.queryString) + setState { + copy( + query = action.queryString, + results = results + ) + } } } } diff --git a/vector/src/main/java/im/vector/app/features/reactions/data/EmojiDataSource.kt b/vector/src/main/java/im/vector/app/features/reactions/data/EmojiDataSource.kt index 96eda22eb99..7218eb993b0 100644 --- a/vector/src/main/java/im/vector/app/features/reactions/data/EmojiDataSource.kt +++ b/vector/src/main/java/im/vector/app/features/reactions/data/EmojiDataSource.kt @@ -20,53 +20,60 @@ import android.graphics.Paint import androidx.core.graphics.PaintCompat import com.squareup.moshi.Moshi import im.vector.app.R +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.CoroutineStart +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.async import javax.inject.Inject import javax.inject.Singleton @Singleton class EmojiDataSource @Inject constructor( + appScope: CoroutineScope, resources: Resources ) { private val paint = Paint() - val rawData = resources.openRawResource(R.raw.emoji_picker_datasource) - .use { input -> - Moshi.Builder() - .build() - .adapter(EmojiData::class.java) - .fromJson(input.bufferedReader().use { it.readText() }) - } - ?.let { parsedRawData -> - // Add key as a keyword, it will solve the issue that ":tada" is not available in completion - // Only add emojis to emojis/categories that can be rendered by the system - parsedRawData.copy( - emojis = mutableMapOf().apply { - parsedRawData.emojis.keys.forEach { key -> - val origin = parsedRawData.emojis[key] ?: return@forEach + val rawData = appScope.async(Dispatchers.IO, CoroutineStart.LAZY) { + resources.openRawResource(R.raw.emoji_picker_datasource) + .use { input -> + Moshi.Builder() + .build() + .adapter(EmojiData::class.java) + .fromJson(input.bufferedReader().use { it.readText() }) + } + ?.let { parsedRawData -> + // Add key as a keyword, it will solve the issue that ":tada" is not available in completion + // Only add emojis to emojis/categories that can be rendered by the system + parsedRawData.copy( + emojis = mutableMapOf().apply { + parsedRawData.emojis.keys.forEach { key -> + val origin = parsedRawData.emojis[key] ?: return@forEach - // Do not add keys containing '_' - if (isEmojiRenderable(origin.emoji)) { - if (origin.keywords.contains(key) || key.contains("_")) { - put(key, origin) - } else { - put(key, origin.copy(keywords = origin.keywords + key)) + // Do not add keys containing '_' + if (isEmojiRenderable(origin.emoji)) { + if (origin.keywords.contains(key) || key.contains("_")) { + put(key, origin) + } else { + put(key, origin.copy(keywords = origin.keywords + key)) + } } } - } - }, - categories = mutableListOf().apply { - parsedRawData.categories.forEach { entry -> - add(EmojiCategory(entry.id, entry.name, mutableListOf().apply { - entry.emojis.forEach { e -> - if (isEmojiRenderable(parsedRawData.emojis[e]!!.emoji)) { - add(e) + }, + categories = mutableListOf().apply { + parsedRawData.categories.forEach { entry -> + add(EmojiCategory(entry.id, entry.name, mutableListOf().apply { + entry.emojis.forEach { e -> + if (isEmojiRenderable(parsedRawData.emojis[e]!!.emoji)) { + add(e) + } } - } - })) + })) + } } - } - ) - } - ?: EmojiData(emptyList(), emptyMap(), emptyMap()) + ) + } + ?: EmojiData(emptyList(), emptyMap(), emptyMap()) + } private val quickReactions = mutableListOf() @@ -74,9 +81,9 @@ class EmojiDataSource @Inject constructor( return PaintCompat.hasGlyph(paint, emoji) } - fun filterWith(query: String): List { + suspend fun filterWith(query: String): List { val words = query.split("\\s".toRegex()) - + val rawData = this.rawData.await() // First add emojis with name matching query, sorted by name return (rawData.emojis.values .asSequence() @@ -87,9 +94,9 @@ class EmojiDataSource @Inject constructor( // Then emojis with keyword matching any of the word in the query, sorted by name rawData.emojis.values .filter { emojiItem -> - words.fold(true, { prev, word -> + words.fold(true) { prev, word -> prev && emojiItem.keywords.any { keyword -> keyword.contains(word, true) } - }) + } } .sortedBy { it.name }) // and ensure they will not be present twice @@ -97,7 +104,7 @@ class EmojiDataSource @Inject constructor( .toList() } - fun getQuickReactions(): List { + suspend fun getQuickReactions(): List { if (quickReactions.isEmpty()) { listOf( "thumbs-up", // 👍 @@ -109,7 +116,7 @@ class EmojiDataSource @Inject constructor( "rocket", // 🚀 "eyes" // 👀 ) - .mapNotNullTo(quickReactions) { rawData.emojis[it] } + .mapNotNullTo(quickReactions) { rawData.await().emojis[it] } } return quickReactions diff --git a/vector/src/main/java/im/vector/app/features/reactions/widget/ReactionButton.kt b/vector/src/main/java/im/vector/app/features/reactions/widget/ReactionButton.kt index d81767cc7ff..c342d6519d6 100644 --- a/vector/src/main/java/im/vector/app/features/reactions/widget/ReactionButton.kt +++ b/vector/src/main/java/im/vector/app/features/reactions/widget/ReactionButton.kt @@ -15,27 +15,18 @@ */ package im.vector.app.features.reactions.widget -import android.animation.Animator -import android.animation.AnimatorListenerAdapter -import android.animation.AnimatorSet -import android.animation.ObjectAnimator import android.content.Context -import android.content.res.TypedArray import android.graphics.drawable.Drawable import android.util.AttributeSet +import android.view.Gravity import android.view.View -import android.view.animation.AccelerateDecelerateInterpolator -import android.view.animation.DecelerateInterpolator -import android.view.animation.OvershootInterpolator -import android.widget.ImageView -import androidx.annotation.ColorInt -import androidx.annotation.ColorRes -import androidx.constraintlayout.widget.ConstraintLayout +import android.widget.LinearLayout import androidx.core.content.ContextCompat import androidx.core.content.withStyledAttributes import im.vector.app.EmojiCompatWrapper import im.vector.app.R import im.vector.app.core.di.HasScreenInjector +import im.vector.app.core.utils.DimensionConverter import im.vector.app.core.utils.TextUtils import im.vector.app.databinding.ReactionButtonBinding import javax.inject.Inject @@ -46,8 +37,8 @@ import javax.inject.Inject */ class ReactionButton @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, - defStyleAttr: Int = 0) - : ConstraintLayout(context, attrs, defStyleAttr), View.OnClickListener, View.OnLongClickListener { + defStyleAttr: Int = 0) : + LinearLayout(context, attrs, defStyleAttr), View.OnClickListener, View.OnLongClickListener { init { if (context is HasScreenInjector) { @@ -55,21 +46,11 @@ class ReactionButton @JvmOverloads constructor(context: Context, } } - companion object { - private val DECCELERATE_INTERPOLATOR = DecelerateInterpolator() - private val ACCELERATE_DECELERATE_INTERPOLATOR = AccelerateDecelerateInterpolator() - private val OVERSHOOT_INTERPOLATOR = OvershootInterpolator(4f) - } - @Inject lateinit var emojiCompatWrapper: EmojiCompatWrapper private val views: ReactionButtonBinding var reactedListener: ReactedListener? = null - private var dotPrimaryColor: Int = 0 - private var dotSecondaryColor: Int = 0 - private var circleStartColor: Int = 0 - private var circleEndColor: Int = 0 var reactionCount = 11 set(value) { @@ -85,50 +66,24 @@ class ReactionButton @JvmOverloads constructor(context: Context, views.reactionText.text = emojiSpanned } - private var animationScaleFactor: Float = 0.toFloat() - private var isChecked: Boolean = false - - private var animatorSet: AnimatorSet? = null - private var onDrawable: Drawable? = null private var offDrawable: Drawable? = null init { inflate(context, R.layout.reaction_button, this) + orientation = HORIZONTAL + minimumHeight = DimensionConverter(context.resources).dpToPx(30) + gravity = Gravity.CENTER views = ReactionButtonBinding.bind(this) views.reactionCount.text = TextUtils.formatCountToShortDecimal(reactionCount) - -// emojiView?.typeface = this.emojiTypeFace ?: Typeface.DEFAULT context.withStyledAttributes(attrs, R.styleable.ReactionButton, defStyleAttr) { onDrawable = ContextCompat.getDrawable(context, R.drawable.reaction_rounded_rect_shape) offDrawable = ContextCompat.getDrawable(context, R.drawable.reaction_rounded_rect_shape_off) - - circleStartColor = getColor(R.styleable.ReactionButton_circle_start_color, 0) - - if (circleStartColor != 0) { - views.circle.startColor = circleStartColor - } - - circleEndColor = getColor(R.styleable.ReactionButton_circle_end_color, 0) - - if (circleEndColor != 0) { - views.circle.endColor = circleEndColor - } - - dotPrimaryColor = getColor(R.styleable.ReactionButton_dots_primary_color, 0) - dotSecondaryColor = getColor(R.styleable.ReactionButton_dots_secondary_color, 0) - - if (dotPrimaryColor != 0 && dotSecondaryColor != 0) { - views.dots.setColors(dotPrimaryColor, dotSecondaryColor) - } - getString(R.styleable.ReactionButton_emoji)?.let { reactionString = it } - reactionCount = getInt(R.styleable.ReactionButton_reaction_count, 0) - val status = getBoolean(R.styleable.ReactionButton_toggled, false) setChecked(status) } @@ -137,12 +92,6 @@ class ReactionButton @JvmOverloads constructor(context: Context, setOnLongClickListener(this) } - private fun getDrawableFromResource(array: TypedArray, styleableIndexId: Int): Drawable? { - val id = array.getResourceId(styleableIndexId, -1) - - return if (-1 != id) ContextCompat.getDrawable(context, id) else null - } - /** * This triggers the entire functionality of the button such as icon changes, * animations, listeners etc. @@ -153,164 +102,25 @@ class ReactionButton @JvmOverloads constructor(context: Context, if (!isEnabled) { return } - isChecked = !isChecked - // icon!!.setImageDrawable(if (isChecked) likeDrawable else unLikeDrawable) background = if (isChecked) onDrawable else offDrawable if (isChecked) { reactedListener?.onReacted(this) - } else { - reactedListener?.onUnReacted(this) - } - - if (animatorSet != null) { - animatorSet!!.cancel() - } - - if (isChecked) { views.reactionText.animate().cancel() views.reactionText.scaleX = 0f views.reactionText.scaleY = 0f - - views.circle.innerCircleRadiusProgress = 0f - views.circle.outerCircleRadiusProgress = 0f - views.dots.currentProgress = 0f - - animatorSet = AnimatorSet() - - val outerCircleAnimator = ObjectAnimator.ofFloat(views.circle, CircleView.OUTER_CIRCLE_RADIUS_PROGRESS, 0.1f, 1f) - outerCircleAnimator.duration = 250 - outerCircleAnimator.interpolator = DECCELERATE_INTERPOLATOR - - val innerCircleAnimator = ObjectAnimator.ofFloat(views.circle, CircleView.INNER_CIRCLE_RADIUS_PROGRESS, 0.1f, 1f) - innerCircleAnimator.duration = 200 - innerCircleAnimator.startDelay = 200 - innerCircleAnimator.interpolator = DECCELERATE_INTERPOLATOR - - val starScaleYAnimator = ObjectAnimator.ofFloat(views.reactionText, ImageView.SCALE_Y, 0.2f, 1f) - starScaleYAnimator.duration = 350 - starScaleYAnimator.startDelay = 250 - starScaleYAnimator.interpolator = OVERSHOOT_INTERPOLATOR - - val starScaleXAnimator = ObjectAnimator.ofFloat(views.reactionText, ImageView.SCALE_X, 0.2f, 1f) - starScaleXAnimator.duration = 350 - starScaleXAnimator.startDelay = 250 - starScaleXAnimator.interpolator = OVERSHOOT_INTERPOLATOR - - val dotsAnimator = ObjectAnimator.ofFloat(views.dots, DotsView.DOTS_PROGRESS, 0f, 1f) - // .ofFloat(views.dots, DotsView.DOTS_PROGRESS, 0, 1f) - dotsAnimator.duration = 900 - dotsAnimator.startDelay = 50 - dotsAnimator.interpolator = ACCELERATE_DECELERATE_INTERPOLATOR - - animatorSet!!.playTogether( - outerCircleAnimator, - innerCircleAnimator, - starScaleYAnimator, - starScaleXAnimator, - dotsAnimator - ) - - animatorSet!!.addListener(object : AnimatorListenerAdapter() { - override fun onAnimationCancel(animation: Animator) { - views.circle.innerCircleRadiusProgress = 0f - views.circle.outerCircleRadiusProgress = 0f - views.dots.currentProgress = 0f - views.reactionText.scaleX = 1f - views.reactionText.scaleY = 1f - } - - override fun onAnimationEnd(animation: Animator) { -// if (animationEndListener != null) { -// // animationEndListener!!.onAnimationEnd(this@ReactionButton) -// } - } - }) - - animatorSet!!.start() + } else { + reactedListener?.onUnReacted(this) } } - /** - * Used to trigger the scale animation that takes places on the - * icon when the button is touched. - * - * @param event - * @return - */ -// override fun onTouchEvent(event: MotionEvent): Boolean { -// if (!isEnabled) -// return true -// -// when (event.action) { -// MotionEvent.ACTION_DOWN -> -// /* -// Commented out this line and moved the animation effect to the action up event due to -// conflicts that were occurring when library is used in sliding type views. -// -// icon.animate().scaleX(0.7f).scaleY(0.7f).setDuration(150).setInterpolator(DECCELERATE_INTERPOLATOR); -// */ -// isPressed = true -// -// MotionEvent.ACTION_MOVE -> { -// val x = event.x -// val y = event.y -// val isInside = x > 0 && x < width && y > 0 && y < height -// if (isPressed != isInside) { -// isPressed = isInside -// } -// } -// -// MotionEvent.ACTION_UP -> { -// views.reactionText!!.animate().scaleX(0.7f).scaleY(0.7f).setDuration(150).interpolator = DECCELERATE_INTERPOLATOR -// views.reactionText!!.animate().scaleX(1f).scaleY(1f).interpolator = DECCELERATE_INTERPOLATOR -// if (isPressed) { -// performClick() -// isPressed = false -// } -// } -// MotionEvent.ACTION_CANCEL -> isPressed = false -// } -// return true -// } - override fun onLongClick(v: View?): Boolean { reactedListener?.onLongClick(this) return reactedListener != null } - /** - * This set sets the colours that are used for the little dots - * that will be exploding once the like button is clicked. - * - * @param primaryColor - * @param secondaryColor - */ - fun setExplodingDotColorsRes(@ColorRes primaryColor: Int, @ColorRes secondaryColor: Int) { - views.dots.setColors(ContextCompat.getColor(context, primaryColor), ContextCompat.getColor(context, secondaryColor)) - } - - fun setExplodingDotColorsInt(@ColorInt primaryColor: Int, @ColorInt secondaryColor: Int) { - views.dots.setColors(primaryColor, secondaryColor) - } - - fun setCircleStartColorRes(@ColorRes circleStartColor: Int) { - this.circleStartColor = ContextCompat.getColor(context, circleStartColor) - views.circle.startColor = this.circleStartColor - } - - fun setCircleStartColorInt(@ColorInt circleStartColor: Int) { - this.circleStartColor = circleStartColor - views.circle.startColor = circleStartColor - } - - fun setCircleEndColorRes(@ColorRes circleEndColor: Int) { - this.circleEndColor = ContextCompat.getColor(context, circleEndColor) - views.circle.endColor = this.circleEndColor - } - /** * Sets the initial state of the button to liked * or unliked. @@ -327,13 +137,6 @@ class ReactionButton @JvmOverloads constructor(context: Context, } } - /** - * Sets the factor by which the dots should be sized. - */ - fun setAnimationScaleFactor(animationScaleFactor: Float) { - this.animationScaleFactor = animationScaleFactor - } - interface ReactedListener { fun onReacted(reactionButton: ReactionButton) fun onUnReacted(reactionButton: ReactionButton) diff --git a/vector/src/main/java/im/vector/app/features/room/RequireActiveMembershipViewModel.kt b/vector/src/main/java/im/vector/app/features/room/RequireActiveMembershipViewModel.kt index c8b00373119..ece953a1852 100644 --- a/vector/src/main/java/im/vector/app/features/room/RequireActiveMembershipViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/room/RequireActiveMembershipViewModel.kt @@ -22,8 +22,8 @@ import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext import com.jakewharton.rxrelay2.BehaviorRelay import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.R import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel @@ -46,8 +46,8 @@ import org.matrix.android.sdk.rx.unwrap class RequireActiveMembershipViewModel @AssistedInject constructor( @Assisted initialState: RequireActiveMembershipViewState, private val stringProvider: StringProvider, - private val session: Session) - : VectorViewModel(initialState) { + private val session: Session) : + VectorViewModel(initialState) { @AssistedFactory interface Factory { diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/ExplicitTermFilter.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/ExplicitTermFilter.kt index 0d1f55485c2..e184e775574 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/ExplicitTermFilter.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/ExplicitTermFilter.kt @@ -39,8 +39,8 @@ class ExplicitTermFilter @Inject constructor( } fun isValid(str: String): Boolean { - return explicitContentRegex.matches(str.replace("\n", " ")).not() + return explicitContentRegex.matches(str.replace("\n", " ")).not() && // Special treatment for "18+" since word boundaries does not work here - && str.contains("18+").not() + str.contains("18+").not() } } diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsController.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsController.kt index 32c87bffd46..a350bff0bbe 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsController.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsController.kt @@ -61,8 +61,8 @@ class PublicRoomsController @Inject constructor(private val stringProvider: Stri unknownRoomItem?.addTo(this) - if ((viewState.hasMore && viewState.asyncPublicRoomsRequest is Success) - || viewState.asyncPublicRoomsRequest is Incomplete) { + if ((viewState.hasMore && viewState.asyncPublicRoomsRequest is Success) || + viewState.asyncPublicRoomsRequest is Incomplete) { loadingItem { // Change id to avoid list to scroll automatically when first results are displayed if (publicRooms.isEmpty()) { diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsFragment.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsFragment.kt index 8214b26fea8..1ff8af1d7d9 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/PublicRoomsFragment.kt @@ -37,7 +37,6 @@ import im.vector.app.databinding.FragmentPublicRoomsBinding import im.vector.app.features.permalink.NavigationInterceptor import im.vector.app.features.permalink.PermalinkHandler import io.reactivex.rxkotlin.subscribeBy - import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.room.model.roomdirectory.PublicRoom import timber.log.Timber diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryActivity.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryActivity.kt index 9a63e81a2f2..02bae82e03c 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryActivity.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/RoomDirectoryActivity.kt @@ -28,8 +28,8 @@ import im.vector.app.core.extensions.addFragmentToBackstack import im.vector.app.core.extensions.popBackstack import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.databinding.ActivitySimpleBinding -import im.vector.app.features.roomdirectory.createroom.CreateRoomFragment import im.vector.app.features.roomdirectory.createroom.CreateRoomArgs +import im.vector.app.features.roomdirectory.createroom.CreateRoomFragment import im.vector.app.features.roomdirectory.picker.RoomDirectoryPickerFragment import javax.inject.Inject diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomActivity.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomActivity.kt index 07dfadb753c..4afda8a0e97 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomActivity.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomActivity.kt @@ -53,7 +53,10 @@ class CreateRoomActivity : VectorBaseActivity(), ToolbarC addFragment( R.id.simpleFragmentContainer, CreateRoomFragment::class.java, - CreateRoomArgs(intent?.getStringExtra(INITIAL_NAME) ?: "") + CreateRoomArgs( + intent?.getStringExtra(INITIAL_NAME) ?: "", + isSpace = intent?.getBooleanExtra(IS_SPACE, false) ?: false + ) ) } } @@ -74,10 +77,12 @@ class CreateRoomActivity : VectorBaseActivity(), ToolbarC companion object { private const val INITIAL_NAME = "INITIAL_NAME" + private const val IS_SPACE = "IS_SPACE" - fun getIntent(context: Context, initialName: String = ""): Intent { + fun getIntent(context: Context, initialName: String = "", isSpace: Boolean = false): Intent { return Intent(context, CreateRoomActivity::class.java).apply { putExtra(INITIAL_NAME, initialName) + putExtra(IS_SPACE, isSpace) } } } diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomController.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomController.kt index 2676096b6b9..7dd9cb4e00e 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomController.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomController.kt @@ -29,6 +29,8 @@ import im.vector.app.features.form.formEditTextItem import im.vector.app.features.form.formEditableAvatarItem import im.vector.app.features.form.formSubmitButtonItem import im.vector.app.features.form.formSwitchItem +import org.matrix.android.sdk.api.MatrixConstants +import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.room.failure.CreateRoomFailure import org.matrix.android.sdk.api.session.room.model.RoomJoinRules import javax.inject.Inject @@ -140,6 +142,7 @@ class CreateRoomController @Inject constructor( value(viewState.aliasLocalPart) suffixText(":" + viewState.homeServerName) prefixText("#") + maxLength(MatrixConstants.maxAliasLocalPartLength(viewState.homeServerName)) hint(host.stringProvider.getString(R.string.room_alias_address_hint)) errorMessage( host.roomAliasErrorFormatter.format( @@ -165,7 +168,8 @@ class CreateRoomController @Inject constructor( host.stringProvider.getString(R.string.create_room_encryption_description) } ) - switchChecked(viewState.isEncrypted) + + switchChecked(viewState.isEncrypted ?: viewState.defaultEncrypted[viewState.roomJoinRules].orFalse()) listener { value -> host.listener?.setIsEncrypted(value) diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomFragment.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomFragment.kt index fdc2bd85620..ac4c9db89f6 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomFragment.kt @@ -38,6 +38,7 @@ import im.vector.app.core.platform.OnBackPressed import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.resources.ColorProvider import im.vector.app.databinding.FragmentCreateRoomBinding +import im.vector.app.features.navigation.Navigator import im.vector.app.features.roomdirectory.RoomDirectorySharedAction import im.vector.app.features.roomdirectory.RoomDirectorySharedActionViewModel import im.vector.app.features.roomprofile.settings.joinrule.RoomJoinRuleBottomSheet @@ -51,11 +52,13 @@ import javax.inject.Inject @Parcelize data class CreateRoomArgs( val initialName: String, - val parentSpaceId: String? = null + val parentSpaceId: String? = null, + val isSpace: Boolean = false ) : Parcelable class CreateRoomFragment @Inject constructor( private val createRoomController: CreateRoomController, + private val createSpaceController: CreateSubSpaceController, val createRoomViewModelFactory: CreateRoomViewModel.Factory, colorProvider: ColorProvider ) : VectorBaseFragment(), @@ -93,6 +96,11 @@ class CreateRoomFragment @Inject constructor( } } + override fun onResume() { + super.onResume() + views.createRoomTitle.text = getString(if (args.isSpace) R.string.create_new_space else R.string.create_new_room) + } + private fun setupRoomJoinRuleSharedActionViewModel() { roomJoinRuleSharedActionViewModel = activityViewModelProvider.get(RoomJoinRuleSharedActionViewModel::class.java) roomJoinRuleSharedActionViewModel @@ -112,18 +120,26 @@ class CreateRoomFragment @Inject constructor( private fun setupWaitingView() { views.waitingView.waitingStatusText.isVisible = true - views.waitingView.waitingStatusText.setText(R.string.create_room_in_progress) + views.waitingView.waitingStatusText.setText( + if (args.isSpace) R.string.create_space_in_progress else R.string.create_room_in_progress + ) } override fun onDestroyView() { views.createRoomForm.cleanup() createRoomController.listener = null + createSpaceController.listener = null super.onDestroyView() } private fun setupRecyclerView() { - views.createRoomForm.configureWith(createRoomController) - createRoomController.listener = this + if (args.isSpace) { + views.createRoomForm.configureWith(createSpaceController) + createSpaceController.listener = this + } else { + views.createRoomForm.configureWith(createRoomController) + createRoomController.listener = this + } } override fun onAvatarDelete() { @@ -147,13 +163,18 @@ class CreateRoomFragment @Inject constructor( } override fun selectVisibility() = withState(viewModel) { state -> - - val allowed = if (state.supportsRestricted) { + // If restricted is supported and the user is in the context of a parent space + // then show restricted option. + val allowed = if (state.supportsRestricted && state.parentSpaceId != null) { listOf(RoomJoinRules.INVITE, RoomJoinRules.PUBLIC, RoomJoinRules.RESTRICTED) } else { listOf(RoomJoinRules.INVITE, RoomJoinRules.PUBLIC) } - RoomJoinRuleBottomSheet.newInstance(state.roomJoinRules, allowed.map { it.toOption(false) }) + RoomJoinRuleBottomSheet.newInstance(state.roomJoinRules, + allowed.map { it.toOption(false) }, + state.isSubSpace, + state.parentSpaceSummary?.displayName + ) .show(childFragmentManager, "RoomJoinRuleBottomSheet") } // override fun setIsPublic(isPublic: Boolean) { @@ -203,12 +224,24 @@ class CreateRoomFragment @Inject constructor( views.waitingView.root.isVisible = async is Loading if (async is Success) { // Navigate to freshly created room - navigator.openRoom(requireActivity(), async()) + if (state.isSubSpace) { + navigator.switchToSpace( + requireContext(), + async(), + Navigator.PostSwitchSpaceAction.None + ) + } else { + navigator.openRoom(requireActivity(), async()) + } sharedActionViewModel.post(RoomDirectorySharedAction.Close) } else { // Populate list with Epoxy - createRoomController.setData(state) + if (args.isSpace) { + createSpaceController.setData(state) + } else { + createRoomController.setData(state) + } } } } diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomViewModel.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomViewModel.kt index fb70003dcf0..14789aabb5f 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomViewModel.kt @@ -36,15 +36,18 @@ import im.vector.app.features.settings.VectorPreferences import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.matrix.android.sdk.api.MatrixPatterns.getDomain +import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.raw.RawService import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities import org.matrix.android.sdk.api.session.room.alias.RoomAliasError import org.matrix.android.sdk.api.session.room.failure.CreateRoomFailure +import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility import org.matrix.android.sdk.api.session.room.model.RoomJoinRules import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesAllowEntry +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.CreateRoomPreset import org.matrix.android.sdk.api.session.room.model.create.RestrictedRoomPreset @@ -53,7 +56,7 @@ import timber.log.Timber class CreateRoomViewModel @AssistedInject constructor(@Assisted private val initialState: CreateRoomViewState, private val session: Session, private val rawService: RawService, - private val vectorPreferences: VectorPreferences + vectorPreferences: VectorPreferences ) : VectorViewModel(initialState) { @AssistedFactory @@ -107,8 +110,13 @@ class CreateRoomViewModel @AssistedInject constructor(@Assisted private val init setState { copy( - isEncrypted = RoomJoinRules.INVITE == roomJoinRules && adminE2EByDefault, - hsAdminHasDisabledE2E = !adminE2EByDefault + hsAdminHasDisabledE2E = !adminE2EByDefault, + defaultEncrypted = mapOf( + RoomJoinRules.INVITE to adminE2EByDefault, + RoomJoinRules.PUBLIC to false, + RoomJoinRules.RESTRICTED to adminE2EByDefault + ) + ) } } @@ -241,6 +249,18 @@ class CreateRoomViewModel @AssistedInject constructor(@Assisted private val init name = state.roomName.takeIf { it.isNotBlank() } topic = state.roomTopic.takeIf { it.isNotBlank() } avatarUri = state.avatarUri + + if (state.isSubSpace) { + // Space-rooms are distinguished from regular messaging rooms by the m.room.type of m.space + roomType = RoomType.SPACE + + // Space-rooms should be created with a power level for events_default of 100, + // to prevent the rooms accidentally/maliciously clogging up with messages from random members of the space. + powerLevelContentOverride = PowerLevelsContent( + eventsDefault = 100 + ) + } + when (state.roomJoinRules) { RoomJoinRules.PUBLIC -> { // Directory visibility @@ -272,7 +292,12 @@ class CreateRoomViewModel @AssistedInject constructor(@Assisted private val init disableFederation = state.disableFederation // Encryption - if (state.isEncrypted) { + val shouldEncrypt = when (state.roomJoinRules) { + // we ignore the isEncrypted for public room as the switch is hidden in this case + RoomJoinRules.PUBLIC -> false + else -> state.isEncrypted ?: state.defaultEncrypted[state.roomJoinRules].orFalse() + } + if (shouldEncrypt) { enableEncryption() } } diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomViewState.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomViewState.kt index 06742ea6908..46076edec6a 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomViewState.kt @@ -28,7 +28,8 @@ data class CreateRoomViewState( val roomName: String = "", val roomTopic: String = "", val roomJoinRules: RoomJoinRules = RoomJoinRules.INVITE, - val isEncrypted: Boolean = false, + val isEncrypted: Boolean? = null, + val defaultEncrypted: Map = emptyMap(), val showAdvanced: Boolean = false, val disableFederation: Boolean = false, val homeServerName: String = "", @@ -37,19 +38,21 @@ data class CreateRoomViewState( val parentSpaceId: String?, val parentSpaceSummary: RoomSummary? = null, val supportsRestricted: Boolean = false, - val aliasLocalPart: String? = null + val aliasLocalPart: String? = null, + val isSubSpace: Boolean = false ) : MvRxState { constructor(args: CreateRoomArgs) : this( roomName = args.initialName, - parentSpaceId = args.parentSpaceId + parentSpaceId = args.parentSpaceId, + isSubSpace = args.isSpace ) /** * Return true if there is not important input from user */ - fun isEmpty() = avatarUri == null - && roomName.isEmpty() - && roomTopic.isEmpty() - && aliasLocalPart.isNullOrEmpty() + fun isEmpty() = avatarUri == null && + roomName.isEmpty() && + roomTopic.isEmpty() && + aliasLocalPart.isNullOrEmpty() } diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateSubSpaceController.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateSubSpaceController.kt new file mode 100644 index 00000000000..26ea2f30a3b --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateSubSpaceController.kt @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.roomdirectory.createroom + +import com.airbnb.epoxy.TypedEpoxyController +import com.airbnb.mvrx.Fail +import com.airbnb.mvrx.Loading +import im.vector.app.R +import im.vector.app.core.epoxy.profiles.buildProfileAction +import im.vector.app.core.resources.StringProvider +import im.vector.app.core.ui.list.genericPillItem +import im.vector.app.features.discovery.settingsSectionTitleItem +import im.vector.app.features.form.formEditTextItem +import im.vector.app.features.form.formEditableSquareAvatarItem +import im.vector.app.features.form.formMultiLineEditTextItem +import im.vector.app.features.form.formSubmitButtonItem +import org.matrix.android.sdk.api.MatrixConstants +import org.matrix.android.sdk.api.session.room.failure.CreateRoomFailure +import org.matrix.android.sdk.api.session.room.model.RoomJoinRules +import javax.inject.Inject + +class CreateSubSpaceController @Inject constructor( + private val stringProvider: StringProvider, + private val roomAliasErrorFormatter: RoomAliasErrorFormatter +) : TypedEpoxyController() { + + var listener: CreateRoomController.Listener? = null + + override fun buildModels(viewState: CreateRoomViewState) { + // display the form + buildForm(viewState, viewState.asyncCreateRoomRequest !is Loading) + } + + private fun buildForm(data: CreateRoomViewState, enableFormElement: Boolean) { + val host = this + + genericPillItem { + id("beta") + imageRes(R.drawable.ic_beta_pill) + tintIcon(false) + text(host.stringProvider.getString(R.string.space_add_space_to_any_space_you_manage)) + } + + formEditableSquareAvatarItem { + id("avatar") + enabled(enableFormElement) + imageUri(data.avatarUri) + clickListener { host.listener?.onAvatarChange() } + deleteListener { host.listener?.onAvatarDelete() } + } + + formEditTextItem { + id("name") + enabled(enableFormElement) + enabled(true) + value(data.roomName) + hint(host.stringProvider.getString(R.string.create_room_name_hint)) + onTextChange { text -> + host.listener?.onNameChange(text) + } + } + + if (data.roomJoinRules == RoomJoinRules.PUBLIC) { + formEditTextItem { + id("alias") + enabled(enableFormElement) + value(data.aliasLocalPart) + hint(host.stringProvider.getString(R.string.create_space_alias_hint)) + suffixText(":" + data.homeServerName) + prefixText("#") + maxLength(MatrixConstants.maxAliasLocalPartLength(data.homeServerName)) + errorMessage( + host.roomAliasErrorFormatter.format( + (((data.asyncCreateRoomRequest as? Fail)?.error) as? CreateRoomFailure.AliasError)?.aliasError) + ) + onTextChange { value -> + host.listener?.setAliasLocalPart(value) + } + } + } + + formMultiLineEditTextItem { + id("topic") + enabled(enableFormElement) + value(data.roomTopic) + hint(host.stringProvider.getString(R.string.create_space_topic_hint)) + textSizeSp(16) + onTextChange { text -> + host.listener?.onTopicChange(text) + } + } + + settingsSectionTitleItem { + id("visibility") + titleResId(R.string.room_settings_space_access_title) + } + + when (data.roomJoinRules) { + RoomJoinRules.INVITE -> { + buildProfileAction( + id = "joinRule", + title = stringProvider.getString(R.string.room_settings_room_access_private_title), + subtitle = stringProvider.getString(R.string.room_settings_room_access_private_description), + divider = false, + editable = true, + action = { host.listener?.selectVisibility() } + ) + } + RoomJoinRules.PUBLIC -> { + buildProfileAction( + id = "joinRule", + title = stringProvider.getString(R.string.room_settings_room_access_public_title), + subtitle = stringProvider.getString(R.string.room_settings_room_access_public_description), + divider = false, + editable = true, + action = { host.listener?.selectVisibility() } + ) + } + RoomJoinRules.RESTRICTED -> { + buildProfileAction( + id = "joinRule", + title = stringProvider.getString(R.string.room_settings_room_access_restricted_title), + subtitle = stringProvider.getString(R.string.room_create_member_of_space_name_can_join, data.parentSpaceSummary?.displayName), + divider = false, + editable = true, + action = { host.listener?.selectVisibility() } + ) + } + else -> { + // not yet supported + } + } + + formSubmitButtonItem { + id("submit") + enabled(enableFormElement && data.roomName.isNullOrBlank().not()) + buttonTitleId(R.string.create_room_action_create) + buttonClickListener { host.listener?.submit() } + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryItem.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryItem.kt index db1ba95e9ed..e898bb22305 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryItem.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryItem.kt @@ -60,9 +60,11 @@ abstract class RoomDirectoryItem : VectorEpoxyModel() // Avatar GlideApp.with(holder.avatarView) .load(directoryAvatarUrl) - .apply { + .let { if (!includeAllNetworks) { - placeholder(R.drawable.network_matrix) + it.placeholder(R.drawable.network_matrix) + } else { + it } } .into(holder.avatarView) diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerController.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerController.kt index d8db38609cd..08e044630d8 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerController.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/picker/RoomDirectoryPickerController.kt @@ -160,8 +160,8 @@ class RoomDirectoryPickerController @Inject constructor( } private fun getErrorMessage(error: Throwable): String { - return if (error is Failure.ServerError - && error.httpCode == HttpsURLConnection.HTTP_INTERNAL_ERROR /* 500 */) { + return if (error is Failure.ServerError && + error.httpCode == HttpsURLConnection.HTTP_INTERNAL_ERROR /* 500 */) { stringProvider.getString(R.string.directory_add_a_new_server_error) } else { errorFormatter.toHumanReadable(error) diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewModel.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewModel.kt index a559b0554d8..04c4ea18bff 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewModel.kt @@ -44,8 +44,8 @@ import org.matrix.android.sdk.rx.rx import timber.log.Timber class RoomPreviewViewModel @AssistedInject constructor(@Assisted private val initialState: RoomPreviewViewState, - private val session: Session) - : VectorViewModel(initialState) { + private val session: Session) : + VectorViewModel(initialState) { @AssistedFactory interface Factory { diff --git a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewState.kt b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewState.kt index 75286ea24f5..7af6e757add 100644 --- a/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roomdirectory/roompreview/RoomPreviewViewState.kt @@ -62,7 +62,7 @@ data class RoomPreviewViewState( roomType = args.roomType ) - fun matrixItem() : MatrixItem { + fun matrixItem(): MatrixItem { return if (roomType == RoomType.SPACE) MatrixItem.SpaceItem(roomId, roomName ?: roomAlias, avatarUrl) else MatrixItem.RoomItem(roomId, roomName ?: roomAlias, avatarUrl) } diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileActivity.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileActivity.kt index 37ad2741ca7..72122ff7fef 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileActivity.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileActivity.kt @@ -20,9 +20,9 @@ package im.vector.app.features.roommemberprofile import android.content.Context import android.content.Intent import android.widget.Toast -import com.google.android.material.appbar.MaterialToolbar import com.airbnb.mvrx.MvRx import com.airbnb.mvrx.viewModel +import com.google.android.material.appbar.MaterialToolbar import im.vector.app.R import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.addFragment diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt index 9ea9f69383a..08eab292a83 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt @@ -45,8 +45,8 @@ class RoomMemberProfileController @Inject constructor( fun onJumpToReadReceiptClicked() fun onMentionClicked() fun onEditPowerLevel(currentRole: Role) - fun onKickClicked() - fun onBanClicked(isUserBanned: Boolean) + fun onKickClicked(isSpace: Boolean) + fun onBanClicked(isSpace: Boolean, isUserBanned: Boolean) fun onCancelInviteClicked() fun onInviteClicked() } @@ -85,7 +85,9 @@ class RoomMemberProfileController @Inject constructor( } private fun buildRoomMemberActions(state: RoomMemberProfileViewState) { - buildSecuritySection(state) + if (!state.isSpace) { + buildSecuritySection(state) + } buildMoreSection(state) buildAdminSection(state) } @@ -181,7 +183,7 @@ class RoomMemberProfileController @Inject constructor( action = { callback?.onOpenDmClicked() } ) - if (state.hasReadReceipt) { + if (!state.isSpace && state.hasReadReceipt) { buildProfileAction( id = "read_receipt", editable = false, @@ -191,16 +193,18 @@ class RoomMemberProfileController @Inject constructor( } val ignoreActionTitle = state.buildIgnoreActionTitle() - - buildProfileAction( - id = "mention", - title = stringProvider.getString(R.string.room_participants_action_mention), - editable = false, - divider = ignoreActionTitle != null, - action = { callback?.onMentionClicked() } - ) + if (!state.isSpace) { + buildProfileAction( + id = "mention", + title = stringProvider.getString(R.string.room_participants_action_mention), + editable = false, + divider = ignoreActionTitle != null, + action = { callback?.onMentionClicked() } + ) + } val canInvite = state.actionPermissions.canInvite + if (canInvite && (membership == Membership.LEAVE || membership == Membership.KNOCK)) { buildProfileAction( id = "invite", @@ -261,7 +265,7 @@ class RoomMemberProfileController @Inject constructor( divider = canBan, destructive = true, title = stringProvider.getString(R.string.room_participants_action_kick), - action = { callback?.onKickClicked() } + action = { callback?.onKickClicked(state.isSpace) } ) } Membership.INVITE -> { @@ -288,7 +292,7 @@ class RoomMemberProfileController @Inject constructor( editable = false, destructive = true, title = banActionTitle, - action = { callback?.onBanClicked(membership == Membership.BAN) } + action = { callback?.onBanClicked(state.isSpace, membership == Membership.BAN) } ) } } diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt index 0adc1531c94..d8e967fd27c 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt @@ -47,6 +47,7 @@ import im.vector.app.databinding.DialogShareQrCodeBinding import im.vector.app.databinding.FragmentMatrixProfileBinding import im.vector.app.databinding.ViewStubRoomMemberProfileHeaderBinding import im.vector.app.features.crypto.verification.VerificationBottomSheet +import im.vector.app.features.displayname.getBestName import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.room.detail.RoomDetailPendingAction import im.vector.app.features.home.room.detail.RoomDetailPendingActionStore @@ -305,16 +306,16 @@ class RoomMemberProfileFragment @Inject constructor( val views = DialogShareQrCodeBinding.bind(view) views.itemShareQrCodeImage.setData(permalink) MaterialAlertDialogBuilder(requireContext()) - .setView(view) - .setNeutralButton(R.string.ok, null) - .setPositiveButton(R.string.share_by_text) { _, _ -> - startSharePlainTextIntent( - fragment = this, - activityResultLauncher = null, - chooserTitle = null, - text = permalink - ) - }.show() + .setView(view) + .setNeutralButton(R.string.ok, null) + .setPositiveButton(R.string.share_by_text) { _, _ -> + startSharePlainTextIntent( + fragment = this, + activityResultLauncher = null, + chooserTitle = null, + text = permalink + ) + }.show() } private fun onAvatarClicked(view: View, userMatrixItem: MatrixItem) { @@ -327,12 +328,13 @@ class RoomMemberProfileFragment @Inject constructor( } } - override fun onKickClicked() { + override fun onKickClicked(isSpace: Boolean) { ConfirmationDialogBuilder .show( activity = requireActivity(), askForReason = true, - confirmationRes = R.string.room_participants_kick_prompt_msg, + confirmationRes = if (isSpace) R.string.space_participants_kick_prompt_msg + else R.string.room_participants_kick_prompt_msg, positiveRes = R.string.room_participants_action_kick, reasonHintRes = R.string.room_participants_kick_reason, titleRes = R.string.room_participants_kick_title @@ -341,16 +343,18 @@ class RoomMemberProfileFragment @Inject constructor( } } - override fun onBanClicked(isUserBanned: Boolean) { + override fun onBanClicked(isSpace: Boolean, isUserBanned: Boolean) { val titleRes: Int val positiveButtonRes: Int val confirmationRes: Int if (isUserBanned) { - confirmationRes = R.string.room_participants_unban_prompt_msg + confirmationRes = if (isSpace) R.string.space_participants_unban_prompt_msg + else R.string.room_participants_unban_prompt_msg titleRes = R.string.room_participants_unban_title positiveButtonRes = R.string.room_participants_action_unban } else { - confirmationRes = R.string.room_participants_ban_prompt_msg + confirmationRes = if (isSpace) R.string.space_participants_ban_prompt_msg + else R.string.room_participants_ban_prompt_msg titleRes = R.string.room_participants_ban_title positiveButtonRes = R.string.room_participants_action_ban } diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt index 6978a247475..dbddad87ca7 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewModel.kt @@ -26,12 +26,13 @@ import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.R import im.vector.app.core.mvrx.runCatchingToAsync import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider +import im.vector.app.features.displayname.getBestName import im.vector.app.features.powerlevel.PowerLevelsObservableFactory import io.reactivex.Observable import io.reactivex.functions.BiFunction @@ -48,6 +49,7 @@ import org.matrix.android.sdk.api.session.room.members.roomMemberQueryParams 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.RoomSummary +import org.matrix.android.sdk.api.session.room.model.RoomType 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.util.MatrixItem @@ -58,8 +60,8 @@ import org.matrix.android.sdk.rx.unwrap class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private val initialState: RoomMemberProfileViewState, private val stringProvider: StringProvider, - private val session: Session) - : VectorViewModel(initialState) { + private val session: Session) : + VectorViewModel(initialState) { @AssistedFactory interface Factory { @@ -86,7 +88,8 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v copy( isMine = session.myUserId == this.userId, userMatrixItem = room?.getRoomMember(initialState.userId)?.toMatrixItem()?.let { Success(it) } ?: Uninitialized, - hasReadReceipt = room?.getUserReadReceipt(initialState.userId) != null + hasReadReceipt = room?.getUserReadReceipt(initialState.userId) != null, + isSpace = room?.roomSummary()?.roomType == RoomType.SPACE ) } observeIgnoredState() diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewState.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewState.kt index f943a5cf08e..5c2751f0dcc 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewState.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileViewState.kt @@ -28,6 +28,7 @@ import org.matrix.android.sdk.api.util.MatrixItem data class RoomMemberProfileViewState( val userId: String, val roomId: String?, + val isSpace: Boolean = false, val showAsMember: Boolean = false, val isMine: Boolean = false, val isIgnored: Async = Uninitialized, diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt index 7d31a5c811f..386e0c98af9 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt @@ -23,8 +23,8 @@ import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.core.di.HasScreenInjector import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel @@ -46,8 +46,8 @@ data class DeviceListViewState( class DeviceListBottomSheetViewModel @AssistedInject constructor(@Assisted private val initialState: DeviceListViewState, @Assisted private val args: DeviceListBottomSheet.Args, - private val session: Session) - : VectorViewModel(initialState) { + private val session: Session) : + VectorViewModel(initialState) { @AssistedFactory interface Factory { diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListEpoxyController.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListEpoxyController.kt index ca2141ccd2e..0325cb132e3 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListEpoxyController.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListEpoxyController.kt @@ -39,8 +39,8 @@ import javax.inject.Inject class DeviceListEpoxyController @Inject constructor(private val stringProvider: StringProvider, private val colorProvider: ColorProvider, private val dimensionConverter: DimensionConverter, - private val vectorPreferences: VectorPreferences) - : TypedEpoxyController() { + private val vectorPreferences: VectorPreferences) : + TypedEpoxyController() { interface InteractionListener { fun onDeviceSelected(device: CryptoDeviceInfo) diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListFragment.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListFragment.kt index fd770ca5309..dbf1f195956 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceListFragment.kt @@ -27,7 +27,6 @@ import im.vector.app.core.extensions.configureWith import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.utils.DimensionConverter import im.vector.app.databinding.BottomSheetGenericListBinding - import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo import javax.inject.Inject diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceTrustInfoActionFragment.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceTrustInfoActionFragment.kt index 65dfa75b650..f82ecb6ddff 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceTrustInfoActionFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceTrustInfoActionFragment.kt @@ -27,7 +27,6 @@ import im.vector.app.core.extensions.configureWith import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.utils.DimensionConverter import im.vector.app.databinding.BottomSheetGenericListBinding - import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo import javax.inject.Inject diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceTrustInfoEpoxyController.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceTrustInfoEpoxyController.kt index b8711372aff..bce219a711f 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceTrustInfoEpoxyController.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/devices/DeviceTrustInfoEpoxyController.kt @@ -34,8 +34,8 @@ import javax.inject.Inject class DeviceTrustInfoEpoxyController @Inject constructor(private val stringProvider: StringProvider, private val colorProvider: ColorProvider, private val dimensionConverter: DimensionConverter, - private val vectorPreferences: VectorPreferences) - : TypedEpoxyController() { + private val vectorPreferences: VectorPreferences) : + TypedEpoxyController() { interface InteractionListener { fun onVerifyManually(device: CryptoDeviceInfo) diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/powerlevel/EditPowerLevelDialogs.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/powerlevel/EditPowerLevelDialogs.kt index 9cd4bb87bd7..e6b898c2b91 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/powerlevel/EditPowerLevelDialogs.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/powerlevel/EditPowerLevelDialogs.kt @@ -25,7 +25,6 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder import im.vector.app.R import im.vector.app.core.extensions.hideKeyboard import im.vector.app.databinding.DialogEditPowerLevelBinding - import org.matrix.android.sdk.api.session.room.powerlevels.Role object EditPowerLevelDialogs { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt index d28878283ff..ec9beda9d6c 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileActivity.kt @@ -20,9 +20,9 @@ package im.vector.app.features.roomprofile import android.content.Context import android.content.Intent import android.widget.Toast -import com.google.android.material.appbar.MaterialToolbar import com.airbnb.mvrx.MvRx import com.airbnb.mvrx.viewModel +import com.google.android.material.appbar.MaterialToolbar import im.vector.app.R import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.addFragment @@ -35,12 +35,12 @@ import im.vector.app.features.home.room.detail.RoomDetailPendingActionStore import im.vector.app.features.room.RequireActiveMembershipViewEvents import im.vector.app.features.room.RequireActiveMembershipViewModel import im.vector.app.features.room.RequireActiveMembershipViewState +import im.vector.app.features.roomprofile.alias.RoomAliasFragment import im.vector.app.features.roomprofile.banned.RoomBannedMemberListFragment import im.vector.app.features.roomprofile.members.RoomMemberListFragment -import im.vector.app.features.roomprofile.settings.RoomSettingsFragment -import im.vector.app.features.roomprofile.alias.RoomAliasFragment import im.vector.app.features.roomprofile.notifications.RoomNotificationSettingsFragment import im.vector.app.features.roomprofile.permissions.RoomPermissionsFragment +import im.vector.app.features.roomprofile.settings.RoomSettingsFragment import im.vector.app.features.roomprofile.uploads.RoomUploadsFragment import javax.inject.Inject diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt index be026894b6f..b237faa17d9 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt @@ -94,11 +94,11 @@ class RoomProfileController @Inject constructor( // Upgrade warning val roomVersion = data.roomCreateContent()?.roomVersion - if (data.canUpgradeRoom - && !data.isTombstoned - && roomVersion != null - && data.isUsingUnstableRoomVersion - && data.recommendedRoomVersion != null) { + if (data.canUpgradeRoom && + !data.isTombstoned && + roomVersion != null && + data.isUsingUnstableRoomVersion && + data.recommendedRoomVersion != null) { genericFooterItem { id("version_warning") text(host.stringProvider.getString(R.string.room_using_unstable_room_version, roomVersion)) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt index a14bb61606f..a1c252d3562 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasController.kt @@ -36,6 +36,7 @@ import im.vector.app.features.discovery.settingsInfoItem import im.vector.app.features.form.formEditTextItem import im.vector.app.features.form.formSwitchItem import im.vector.app.features.roomdirectory.createroom.RoomAliasErrorFormatter +import org.matrix.android.sdk.api.MatrixConstants import org.matrix.android.sdk.api.session.room.alias.RoomAliasError import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility import org.matrix.android.sdk.api.session.room.model.RoomType @@ -174,6 +175,7 @@ class RoomAliasController @Inject constructor( formEditTextItem { id("publishManuallyEdit") value(data.publishManuallyState.value) + maxLength(MatrixConstants.ALIAS_MAX_LENGTH) hint(host.stringProvider.getString(R.string.room_alias_address_hint)) inputType(InputType.TYPE_CLASS_TEXT) onTextChange { text -> @@ -253,6 +255,7 @@ class RoomAliasController @Inject constructor( value(data.newLocalAliasState.value) suffixText(":" + data.homeServerName) prefixText("#") + maxLength(MatrixConstants.maxAliasLocalPartLength(data.homeServerName)) hint(host.stringProvider.getString(R.string.room_alias_address_hint)) errorMessage(host.roomAliasErrorFormatter.format((data.newLocalAliasState.asyncRequest as? Fail)?.error as? RoomAliasError)) onTextChange { value -> diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt index 36dbf7bf8c1..0b144bea9f7 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasFragment.kt @@ -38,7 +38,6 @@ import im.vector.app.features.roomprofile.RoomProfileArgs import im.vector.app.features.roomprofile.alias.detail.RoomAliasBottomSheet import im.vector.app.features.roomprofile.alias.detail.RoomAliasBottomSheetSharedAction import im.vector.app.features.roomprofile.alias.detail.RoomAliasBottomSheetSharedActionViewModel - import org.matrix.android.sdk.api.session.room.alias.RoomAliasError import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility import org.matrix.android.sdk.api.util.toMatrixItem diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt index aa9981997c1..bb9a5c39d98 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/RoomAliasViewModel.kt @@ -43,8 +43,8 @@ import org.matrix.android.sdk.rx.rx import org.matrix.android.sdk.rx.unwrap class RoomAliasViewModel @AssistedInject constructor(@Assisted initialState: RoomAliasViewState, - private val session: Session) - : VectorViewModel(initialState) { + private val session: Session) : + VectorViewModel(initialState) { @AssistedFactory interface Factory { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetSharedAction.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetSharedAction.kt index 13909c401f0..d7cb923603b 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetSharedAction.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetSharedAction.kt @@ -24,8 +24,8 @@ import im.vector.app.core.platform.VectorSharedAction sealed class RoomAliasBottomSheetSharedAction( @StringRes val titleRes: Int, @DrawableRes val iconResId: Int = 0, - val destructive: Boolean = false) - : VectorSharedAction { + val destructive: Boolean = false) : + VectorSharedAction { data class ShareAlias(val matrixTo: String) : RoomAliasBottomSheetSharedAction( R.string.share, diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetViewModel.kt index e762b520254..20294915466 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/alias/detail/RoomAliasBottomSheetViewModel.kt @@ -19,8 +19,8 @@ import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.core.platform.EmptyAction import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListFragment.kt index 807954438f9..72f43639ed0 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListFragment.kt @@ -34,7 +34,6 @@ import im.vector.app.core.utils.toast import im.vector.app.databinding.FragmentRoomSettingGenericBinding import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.roomprofile.RoomProfileArgs - import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary import org.matrix.android.sdk.api.util.toMatrixItem import javax.inject.Inject diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt index 1b64261fcb9..b5c10ec2fa3 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/banned/RoomBannedMemberListViewModel.kt @@ -21,8 +21,8 @@ import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.R import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel @@ -44,8 +44,8 @@ import org.matrix.android.sdk.rx.unwrap class RoomBannedMemberListViewModel @AssistedInject constructor(@Assisted initialState: RoomBannedMemberListViewState, private val stringProvider: StringProvider, - private val session: Session) - : VectorViewModel(initialState) { + private val session: Session) : + VectorViewModel(initialState) { @AssistedFactory interface Factory { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListFragment.kt index b8d0842c226..4337e8767ac 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListFragment.kt @@ -34,7 +34,6 @@ import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.databinding.FragmentRoomMemberListBinding import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.roomprofile.RoomProfileArgs - import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt index 5c6ff484033..1e3a97a027c 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewModel.kt @@ -51,8 +51,8 @@ import timber.log.Timber class RoomMemberListViewModel @AssistedInject constructor(@Assisted initialState: RoomMemberListViewState, private val roomMemberSummaryComparator: RoomMemberSummaryComparator, - private val session: Session) - : VectorViewModel(initialState) { + private val session: Session) : + VectorViewModel(initialState) { @AssistedFactory interface Factory { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberSummaryFilter.kt b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberSummaryFilter.kt index bd9fb7b9419..449b8663d6e 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberSummaryFilter.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberSummaryFilter.kt @@ -31,9 +31,9 @@ class RoomMemberSummaryFilter @Inject constructor() : Predicate get() { - if ((roomSummary()?.isEncrypted == true && notificationState() == RoomNotificationState.MENTIONS_ONLY) - || notificationState() == RoomNotificationState.ALL_MESSAGES) { + if ((roomSummary()?.isEncrypted == true && notificationState() == RoomNotificationState.MENTIONS_ONLY) || + notificationState() == RoomNotificationState.ALL_MESSAGES) { /** if in an encrypted room, mentions notifications are not supported so show "All Messages" as selected. * Also in the new settings there is no notion of notifications without sound so it maps to noisy also */ @@ -49,6 +49,7 @@ val RoomNotificationSettingsViewState.notificationStateMapped: Async(initialState) { + private val session: Session) : + VectorViewModel(initialState) { @AssistedFactory interface Factory { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt index 1265e7e5ee1..5b0943906ae 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/RoomSettingsViewModel.kt @@ -47,8 +47,8 @@ import org.matrix.android.sdk.rx.unwrap class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: RoomSettingsViewState, private val vectorPreferences: VectorPreferences, - private val session: Session) - : VectorViewModel(initialState) { + private val session: Session) : + VectorViewModel(initialState) { @AssistedFactory interface Factory { @@ -112,11 +112,11 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: val summary = asyncSummary() setState { copy( - showSaveAction = avatarAction !is RoomSettingsViewState.AvatarAction.None - || summary?.name != newName - || summary?.topic != newTopic - || (newHistoryVisibility != null && newHistoryVisibility != currentHistoryVisibility) - || newJoinRule.hasChanged() + showSaveAction = avatarAction !is RoomSettingsViewState.AvatarAction.None || + summary?.name != newName || + summary?.topic != newTopic || + (newHistoryVisibility != null && newHistoryVisibility != currentHistoryVisibility) || + newJoinRule.hasChanged() ) } } @@ -146,8 +146,8 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: canChangeHistoryVisibility = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_HISTORY_VISIBILITY), canChangeJoinRule = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, - EventType.STATE_ROOM_JOIN_RULES) - && powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, + EventType.STATE_ROOM_JOIN_RULES) && + powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_GUEST_ACCESS), canAddChildren = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_SPACE_CHILD) @@ -312,6 +312,7 @@ class RoomSettingsViewModel @AssistedInject constructor(@Assisted initialState: _viewEvents.post(RoomSettingsViewEvents.Failure(it)) } ) + .disposeOnClear() } private fun postLoading(isLoading: Boolean) { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilitySharedActionViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilitySharedActionViewModel.kt index 4f1bdca1941..c4f48929846 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilitySharedActionViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilitySharedActionViewModel.kt @@ -19,5 +19,5 @@ package im.vector.app.features.roomprofile.settings.historyvisibility import im.vector.app.core.platform.VectorSharedActionViewModel import javax.inject.Inject -class RoomHistoryVisibilitySharedActionViewModel @Inject constructor() - : VectorSharedActionViewModel() +class RoomHistoryVisibilitySharedActionViewModel @Inject constructor() : + VectorSharedActionViewModel() diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityViewModel.kt index c2a8ae967f4..5580156918c 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/historyvisibility/RoomHistoryVisibilityViewModel.kt @@ -18,5 +18,5 @@ package im.vector.app.features.roomprofile.settings.historyvisibility import im.vector.app.core.ui.bottomsheet.BottomSheetGenericViewModel -class RoomHistoryVisibilityViewModel(initialState: RoomHistoryVisibilityState) - : BottomSheetGenericViewModel(initialState) +class RoomHistoryVisibilityViewModel(initialState: RoomHistoryVisibilityState) : + BottomSheetGenericViewModel(initialState) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleBottomSheet.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleBottomSheet.kt index f4c7eecf8f0..f0f8193cc53 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleBottomSheet.kt @@ -39,7 +39,9 @@ fun RoomJoinRules.toOption(needUpgrade: Boolean) = JoinRulesOptionSupport(this, @Parcelize data class RoomJoinRuleBottomSheetArgs( val currentRoomJoinRule: RoomJoinRules, - val allowedJoinedRules: List + val allowedJoinedRules: List, + val isSpace: Boolean = false, + val parentSpaceName: String? ) : Parcelable class RoomJoinRuleBottomSheet : BottomSheetGeneric() { @@ -73,11 +75,13 @@ class RoomJoinRuleBottomSheet : BottomSheetGeneric = listOf( RoomJoinRules.INVITE, RoomJoinRules.PUBLIC - ).map { it.toOption(true) } + ).map { it.toOption(true) }, + isSpace: Boolean = false, + parentSpaceName: String? = null ): RoomJoinRuleBottomSheet { return RoomJoinRuleBottomSheet().apply { setArguments( - RoomJoinRuleBottomSheetArgs(currentRoomJoinRule, allowedJoinedRules) + RoomJoinRuleBottomSheetArgs(currentRoomJoinRule, allowedJoinedRules, isSpace, parentSpaceName) ) } } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleController.kt index edeb6e10999..2552a2568c5 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleController.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleController.kt @@ -20,8 +20,6 @@ import im.vector.app.R import im.vector.app.core.resources.DrawableProvider import im.vector.app.core.resources.StringProvider import im.vector.app.core.ui.bottomsheet.BottomSheetGenericController -import me.gujun.android.span.image -import me.gujun.android.span.span import org.matrix.android.sdk.api.session.room.model.RoomJoinRules import javax.inject.Inject @@ -30,7 +28,11 @@ class RoomJoinRuleController @Inject constructor( private val drawableProvider: DrawableProvider ) : BottomSheetGenericController() { - override fun getTitle() = stringProvider.getString(R.string.room_settings_room_access_rules_pref_dialog_title) + override fun getTitle() = + stringProvider.getString( + // generic title for both room and space + R.string.room_settings_access_rules_pref_dialog_title + ) override fun getActions(state: RoomJoinRuleState): List { return listOf( @@ -42,21 +44,21 @@ class RoomJoinRuleController @Inject constructor( ), RoomJoinRuleRadioAction( roomJoinRule = RoomJoinRules.PUBLIC, - description = stringProvider.getString(R.string.room_settings_room_access_public_description), + description = stringProvider.getString( + if (state.isSpace) R.string.room_settings_space_access_public_description + else R.string.room_settings_room_access_public_description + ), title = stringProvider.getString(R.string.room_settings_room_access_public_title), isSelected = state.currentRoomJoinRule == RoomJoinRules.PUBLIC ), RoomJoinRuleRadioAction( roomJoinRule = RoomJoinRules.RESTRICTED, - description = stringProvider.getString(R.string.room_settings_room_access_restricted_description), - title = span { - +stringProvider.getString(R.string.room_settings_room_access_restricted_title) - +" " - image( - drawableProvider.getDrawable(R.drawable.ic_beta_pill)!!, - "bottom" - ) + description = if (state.parentSpaceName != null) { + stringProvider.getString(R.string.room_create_member_of_space_name_can_join, state.parentSpaceName) + } else { + stringProvider.getString(R.string.room_settings_room_access_restricted_description) }, + title = stringProvider.getString(R.string.room_settings_room_access_restricted_title), isSelected = state.currentRoomJoinRule == RoomJoinRules.RESTRICTED ) ).filter { state.allowedJoinedRules.map { it.rule }.contains(it.roomJoinRule) } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleSharedActionViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleSharedActionViewModel.kt index a7df2cb475d..971d71e0df9 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleSharedActionViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleSharedActionViewModel.kt @@ -19,5 +19,5 @@ package im.vector.app.features.roomprofile.settings.joinrule import im.vector.app.core.platform.VectorSharedActionViewModel import javax.inject.Inject -class RoomJoinRuleSharedActionViewModel @Inject constructor() - : VectorSharedActionViewModel() +class RoomJoinRuleSharedActionViewModel @Inject constructor() : + VectorSharedActionViewModel() diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleState.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleState.kt index dd818a4631d..dcf115cc4b3 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleState.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleState.kt @@ -24,11 +24,15 @@ data class RoomJoinRuleState( val currentRoomJoinRule: RoomJoinRules = RoomJoinRules.INVITE, val allowedJoinedRules: List = listOf(RoomJoinRules.INVITE, RoomJoinRules.PUBLIC).map { it.toOption(true) }, - val currentGuestAccess: GuestAccess? = null + val currentGuestAccess: GuestAccess? = null, + val isSpace: Boolean = false, + val parentSpaceName: String? ) : BottomSheetGenericState() { constructor(args: RoomJoinRuleBottomSheetArgs) : this( currentRoomJoinRule = args.currentRoomJoinRule, - allowedJoinedRules = args.allowedJoinedRules + allowedJoinedRules = args.allowedJoinedRules, + isSpace = args.isSpace, + parentSpaceName = args.parentSpaceName ) } diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleViewModel.kt index 4305bfa72d5..1ff374bf5bb 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/RoomJoinRuleViewModel.kt @@ -18,5 +18,5 @@ package im.vector.app.features.roomprofile.settings.joinrule import im.vector.app.core.ui.bottomsheet.BottomSheetGenericViewModel -class RoomJoinRuleViewModel(initialState: RoomJoinRuleState) - : BottomSheetGenericViewModel(initialState) +class RoomJoinRuleViewModel(initialState: RoomJoinRuleState) : + BottomSheetGenericViewModel(initialState) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/SpaceJoinRuleItem.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/SpaceJoinRuleItem.kt index 9110f9b32e3..382ef1c545e 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/SpaceJoinRuleItem.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/SpaceJoinRuleItem.kt @@ -28,6 +28,7 @@ import im.vector.app.core.epoxy.ClickListener import im.vector.app.core.epoxy.VectorEpoxyHolder import im.vector.app.core.epoxy.VectorEpoxyModel import im.vector.app.core.epoxy.onClick +import im.vector.app.core.extensions.setAttributeTintedImageResource import im.vector.app.core.utils.DebouncedClickListener import im.vector.app.features.home.AvatarRenderer import org.matrix.android.sdk.api.util.MatrixItem @@ -57,7 +58,7 @@ abstract class SpaceJoinRuleItem : VectorEpoxyModel() holder.upgradeRequiredButton.setOnClickListener(DebouncedClickListener(listener)) if (selected) { - holder.radioImage.setImageDrawable(ContextCompat.getDrawable(holder.view.context, R.drawable.ic_radio_on)) + holder.radioImage.setAttributeTintedImageResource(R.drawable.ic_radio_on, R.attr.colorPrimary) holder.radioImage.contentDescription = holder.view.context.getString(R.string.a11y_checked) } else { holder.radioImage.setImageDrawable(ContextCompat.getDrawable(holder.view.context, R.drawable.ic_radio_off)) diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/advanced/RoomJoinRuleChooseRestrictedViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/advanced/RoomJoinRuleChooseRestrictedViewModel.kt index eaa435f8532..9a9df700f8c 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/advanced/RoomJoinRuleChooseRestrictedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/settings/joinrule/advanced/RoomJoinRuleChooseRestrictedViewModel.kt @@ -35,6 +35,7 @@ import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider import im.vector.app.core.utils.styleMatchingText +import im.vector.app.features.displayname.getBestName import im.vector.app.features.roomprofile.settings.joinrule.toOption import im.vector.app.features.settings.VectorPreferences import kotlinx.coroutines.launch @@ -78,9 +79,9 @@ class RoomJoinRuleChooseRestrictedViewModel @AssistedInject constructor( val unknownAllowedOrRooms = mutableListOf() initialAllowList.orEmpty().forEach { entry -> val summary = entry.roomId?.let { session.getRoomSummary(it) } - if (summary == null // it's not known by me - || summary.roomType != RoomType.SPACE // it's not a space - || !roomSummary.flattenParentIds.contains(summary.roomId) // it's not a parent space + if (summary == null || // it's not known by me + summary.roomType != RoomType.SPACE || // it's not a space + !roomSummary.flattenParentIds.contains(summary.roomId) // it's not a parent space ) { (summary?.toMatrixItem() ?: entry.roomId?.let { MatrixItem.RoomItem(it, null, null) })?.let { unknownAllowedOrRooms.add(it) @@ -104,8 +105,8 @@ class RoomJoinRuleChooseRestrictedViewModel @AssistedInject constructor( // server is not really checking that, just to be sure let's check val restrictedSupportedByThisVersion = homeServerCapabilities .isFeatureSupported(HomeServerCapabilities.ROOM_CAP_RESTRICTED, room.getRoomVersion()) - if (safeRule == RoomJoinRules.RESTRICTED - && !restrictedSupportedByThisVersion) { + if (safeRule == RoomJoinRules.RESTRICTED && + !restrictedSupportedByThisVersion) { safeRule = RoomJoinRules.INVITE } @@ -392,8 +393,7 @@ class RoomJoinRuleChooseRestrictedViewModel @AssistedInject constructor( companion object : MvRxViewModelFactory { - override fun create(viewModelContext: ViewModelContext, state: RoomJoinRuleChooseRestrictedState) - : RoomJoinRuleChooseRestrictedViewModel? { + override fun create(viewModelContext: ViewModelContext, state: RoomJoinRuleChooseRestrictedState): RoomJoinRuleChooseRestrictedViewModel? { val factory = when (viewModelContext) { is FragmentViewModelContext -> viewModelContext.fragment as? Factory is ActivityViewModelContext -> viewModelContext.activity as? Factory diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsFragment.kt index 2141b6bf278..f079daf262c 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsFragment.kt @@ -38,7 +38,6 @@ import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.notifications.NotificationUtils import im.vector.app.features.roomprofile.RoomProfileArgs import kotlinx.coroutines.launch - import org.matrix.android.sdk.api.util.toMatrixItem import javax.inject.Inject diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt index 1d6b0568168..dfa3204d074 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/RoomUploadsViewModel.kt @@ -25,8 +25,8 @@ import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import kotlinx.coroutines.launch @@ -91,8 +91,8 @@ class RoomUploadsViewModel @AssistedInject constructor( val groupedUploadEvents = result.uploadEvents .groupBy { - it.contentWithAttachmentContent.msgType == MessageType.MSGTYPE_IMAGE - || it.contentWithAttachmentContent.msgType == MessageType.MSGTYPE_VIDEO + it.contentWithAttachmentContent.msgType == MessageType.MSGTYPE_IMAGE || + it.contentWithAttachmentContent.msgType == MessageType.MSGTYPE_VIDEO } setState { diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/files/RoomUploadsFilesFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/files/RoomUploadsFilesFragment.kt index 972c606867b..1739378761b 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/files/RoomUploadsFilesFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/files/RoomUploadsFilesFragment.kt @@ -35,7 +35,6 @@ import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.databinding.FragmentGenericStateViewRecyclerBinding import im.vector.app.features.roomprofile.uploads.RoomUploadsAction import im.vector.app.features.roomprofile.uploads.RoomUploadsViewModel - import org.matrix.android.sdk.api.session.room.uploads.UploadEvent import javax.inject.Inject diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/media/RoomUploadsMediaFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/media/RoomUploadsMediaFragment.kt index a7f7dbfe98f..eb4337cffa9 100644 --- a/vector/src/main/java/im/vector/app/features/roomprofile/uploads/media/RoomUploadsMediaFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roomprofile/uploads/media/RoomUploadsMediaFragment.kt @@ -46,7 +46,6 @@ import im.vector.app.features.roomprofile.uploads.RoomUploadsAction import im.vector.app.features.roomprofile.uploads.RoomUploadsFragment import im.vector.app.features.roomprofile.uploads.RoomUploadsViewModel import im.vector.app.features.roomprofile.uploads.RoomUploadsViewState - import org.matrix.android.sdk.api.session.room.model.message.MessageImageContent import org.matrix.android.sdk.api.session.room.model.message.MessageVideoContent import org.matrix.android.sdk.api.session.room.model.message.getFileUrl diff --git a/vector/src/main/java/im/vector/app/features/session/SessionListener.kt b/vector/src/main/java/im/vector/app/features/session/SessionListener.kt index d07e26d82d5..c1ee0b527e4 100644 --- a/vector/src/main/java/im/vector/app/features/session/SessionListener.kt +++ b/vector/src/main/java/im/vector/app/features/session/SessionListener.kt @@ -20,8 +20,8 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import im.vector.app.core.extensions.postLiveEvent import im.vector.app.core.utils.LiveEvent -import kotlinx.coroutines.cancelChildren import im.vector.app.features.call.vectorCallService +import kotlinx.coroutines.cancelChildren import kotlinx.coroutines.launch import org.matrix.android.sdk.api.failure.GlobalError import org.matrix.android.sdk.api.session.Session diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorDataStore.kt b/vector/src/main/java/im/vector/app/features/settings/VectorDataStore.kt new file mode 100644 index 00000000000..74b3794b2c3 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/VectorDataStore.kt @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.settings + +import android.content.Context +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.edit +import androidx.datastore.preferences.core.intPreferencesKey +import androidx.datastore.preferences.preferencesDataStore +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import javax.inject.Inject + +private val Context.dataStore: DataStore by preferencesDataStore(name = "vector_settings") + +class VectorDataStore @Inject constructor( + private val context: Context +) { + + private val pushCounter = intPreferencesKey("push_counter") + + val pushCounterFlow: Flow = context.dataStore.data.map { preferences -> + preferences[pushCounter] ?: 0 + } + + suspend fun incrementPushCounter() { + context.dataStore.edit { settings -> + val currentCounterValue = settings[pushCounter] ?: 0 + settings[pushCounter] = currentCounterValue + 1 + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt b/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt index 259c3662fcb..a5df2ad1d45 100755 --- a/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt @@ -117,6 +117,7 @@ class VectorPreferences @Inject constructor(private val context: Context) { // notifications const val SETTINGS_ENABLE_ALL_NOTIF_PREFERENCE_KEY = "SETTINGS_ENABLE_ALL_NOTIF_PREFERENCE_KEY" const val SETTINGS_ENABLE_THIS_DEVICE_PREFERENCE_KEY = "SETTINGS_ENABLE_THIS_DEVICE_PREFERENCE_KEY" + const val SETTINGS_EMAIL_NOTIFICATION_CATEGORY_PREFERENCE_KEY = "SETTINGS_EMAIL_NOTIFICATION_CATEGORY_PREFERENCE_KEY" // public static final String SETTINGS_TURN_SCREEN_ON_PREFERENCE_KEY = "SETTINGS_TURN_SCREEN_ON_PREFERENCE_KEY"; const val SETTINGS_SYSTEM_CALL_NOTIFICATION_PREFERENCE_KEY = "SETTINGS_SYSTEM_CALL_NOTIFICATION_PREFERENCE_KEY" @@ -159,6 +160,7 @@ class VectorPreferences @Inject constructor(private val context: Context) { private const val SETTINGS_LABS_SHOW_HIDDEN_EVENTS_PREFERENCE_KEY = "SETTINGS_LABS_SHOW_HIDDEN_EVENTS_PREFERENCE_KEY" private const val SETTINGS_LABS_ENABLE_SWIPE_TO_REPLY = "SETTINGS_LABS_ENABLE_SWIPE_TO_REPLY" private const val SETTINGS_DEVELOPER_MODE_FAIL_FAST_PREFERENCE_KEY = "SETTINGS_DEVELOPER_MODE_FAIL_FAST_PREFERENCE_KEY" + private const val SETTINGS_DEVELOPER_MODE_SHOW_INFO_ON_SCREEN_KEY = "SETTINGS_DEVELOPER_MODE_SHOW_INFO_ON_SCREEN_KEY" // SETTINGS_LABS_HIDE_TECHNICAL_E2E_ERRORS private const val SETTINGS_LABS_SHOW_COMPLETE_HISTORY_IN_ENCRYPTED_ROOM = "SETTINGS_LABS_SHOW_COMPLETE_HISTORY_IN_ENCRYPTED_ROOM" @@ -312,6 +314,10 @@ class VectorPreferences @Inject constructor(private val context: Context) { return defaultPrefs.getBoolean(SETTINGS_DEVELOPER_MODE_PREFERENCE_KEY, false) } + fun developerShowDebugInfo(): Boolean { + return developerMode() && defaultPrefs.getBoolean(SETTINGS_DEVELOPER_MODE_SHOW_INFO_ON_SCREEN_KEY, false) + } + fun shouldShowHiddenEvents(): Boolean { return developerMode() && defaultPrefs.getBoolean(SETTINGS_LABS_SHOW_HIDDEN_EVENTS_PREFERENCE_KEY, false) } @@ -926,8 +932,8 @@ class VectorPreferences @Inject constructor(private val context: Context) { * Return true if Pin code is disabled, or if user set the settings to see full notification content */ fun useCompleteNotificationFormat(): Boolean { - return !useFlagPinCode() - || defaultPrefs.getBoolean(SETTINGS_SECURITY_USE_COMPLETE_NOTIFICATIONS_FLAG, true) + return !useFlagPinCode() || + defaultPrefs.getBoolean(SETTINGS_SECURITY_USE_COMPLETE_NOTIFICATIONS_FLAG, true) } fun backgroundSyncTimeOut(): Int { diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsActivity.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsActivity.kt index 45463131988..298551af2a4 100755 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsActivity.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsActivity.kt @@ -29,7 +29,7 @@ import im.vector.app.databinding.ActivityVectorSettingsBinding import im.vector.app.features.discovery.DiscoverySettingsFragment import im.vector.app.features.settings.devices.VectorSettingsDevicesFragment import im.vector.app.features.settings.notifications.VectorSettingsNotificationPreferenceFragment - +import im.vector.app.features.settings.threepids.ThreePidsSettingsFragment import org.matrix.android.sdk.api.failure.GlobalError import org.matrix.android.sdk.api.session.Session import timber.log.Timber @@ -136,6 +136,10 @@ class VectorSettingsActivity : VectorBaseActivity return keyToHighlight } + override fun navigateToEmailAndPhoneNumbers() { + navigateTo(ThreePidsSettingsFragment::class.java) + } + override fun handleInvalidToken(globalError: GlobalError.InvalidToken) { if (ignoreInvalidTokenError) { Timber.w("Ignoring invalid token global error") diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsAdvancedSettingsFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsAdvancedSettingsFragment.kt index 80c8dfa77de..68ce4e691ca 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsAdvancedSettingsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsAdvancedSettingsFragment.kt @@ -20,6 +20,7 @@ import androidx.preference.Preference import androidx.preference.SeekBarPreference import im.vector.app.R import im.vector.app.core.platform.VectorBaseActivity +import im.vector.app.core.preference.VectorPreferenceCategory import im.vector.app.core.preference.VectorSwitchPreference import im.vector.app.features.rageshake.RageShake @@ -72,7 +73,7 @@ class VectorSettingsAdvancedSettingsFragment : VectorSettingsBaseFragment() { true } } else { - findPreference("SETTINGS_RAGE_SHAKE_CATEGORY_KEY")!!.isVisible = false + findPreference("SETTINGS_RAGE_SHAKE_CATEGORY_KEY")!!.isVisible = false } } } diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsFragmentInteractionListener.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsFragmentInteractionListener.kt index b815ce653d3..ddfcc93287a 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsFragmentInteractionListener.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsFragmentInteractionListener.kt @@ -20,4 +20,6 @@ interface VectorSettingsFragmentInteractionListener { fun requestHighlightPreferenceKeyOnResume(key: String?) fun requestedKeyToHighlight(): String? + + fun navigateToEmailAndPhoneNumbers() } diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsPreferencesFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsPreferencesFragment.kt index 212147e628b..2a3ea799a58 100644 --- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsPreferencesFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsPreferencesFragment.kt @@ -142,7 +142,7 @@ class VectorSettingsPreferencesFragment @Inject constructor( // Take photo or video updateTakePhotoOrVideoPreferenceSummary() takePhotoOrVideoPreference.onPreferenceClickListener = Preference.OnPreferenceClickListener { - PhotoOrVideoDialog(requireActivity(), vectorPreferences).showForSettings(object: PhotoOrVideoDialog.PhotoOrVideoDialogSettingsListener { + PhotoOrVideoDialog(requireActivity(), vectorPreferences).showForSettings(object : PhotoOrVideoDialog.PhotoOrVideoDialogSettingsListener { override fun onUpdated() { updateTakePhotoOrVideoPreferenceSummary() } diff --git a/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountAction.kt b/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountAction.kt index bf62800d6a4..0662afc7001 100644 --- a/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountAction.kt +++ b/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountAction.kt @@ -21,7 +21,7 @@ import im.vector.app.core.platform.VectorViewModelAction sealed class DeactivateAccountAction : VectorViewModelAction { data class DeactivateAccount(val eraseAllData: Boolean) : DeactivateAccountAction() - object SsoAuthDone: DeactivateAccountAction() - data class PasswordAuthDone(val password: String): DeactivateAccountAction() - object ReAuthCancelled: DeactivateAccountAction() + object SsoAuthDone : DeactivateAccountAction() + data class PasswordAuthDone(val password: String) : DeactivateAccountAction() + object ReAuthCancelled : DeactivateAccountAction() } diff --git a/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountFragment.kt b/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountFragment.kt index 2cc80bfa237..4e599e81fbb 100644 --- a/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountFragment.kt @@ -34,7 +34,6 @@ import im.vector.app.features.MainActivityArgs import im.vector.app.features.auth.ReAuthActivity import im.vector.app.features.settings.VectorSettingsActivity import org.matrix.android.sdk.api.auth.data.LoginFlowTypes - import javax.inject.Inject class DeactivateAccountFragment @Inject constructor( diff --git a/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountViewModel.kt index 80c64220c03..91c62235055 100644 --- a/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/account/deactivation/DeactivateAccountViewModel.kt @@ -27,14 +27,14 @@ import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.features.auth.ReAuthActivity import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.auth.UIABaseAuth import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor +import org.matrix.android.sdk.api.auth.UserPasswordAuth +import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse import org.matrix.android.sdk.api.failure.isInvalidUIAAuth import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse import org.matrix.android.sdk.internal.crypto.crosssigning.fromBase64 import org.matrix.android.sdk.internal.crypto.model.rest.DefaultBaseAuth -import org.matrix.android.sdk.api.auth.UIABaseAuth -import org.matrix.android.sdk.api.auth.UserPasswordAuth import timber.log.Timber import kotlin.coroutines.Continuation import kotlin.coroutines.resume @@ -45,8 +45,8 @@ data class DeactivateAccountViewState( ) : MvRxState class DeactivateAccountViewModel @AssistedInject constructor(@Assisted private val initialState: DeactivateAccountViewState, - private val session: Session) - : VectorViewModel(initialState) { + private val session: Session) : + VectorViewModel(initialState) { @AssistedFactory interface Factory { diff --git a/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsAction.kt b/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsAction.kt index 735c456ff91..6c2fc7ecc21 100644 --- a/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsAction.kt +++ b/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsAction.kt @@ -19,8 +19,8 @@ package im.vector.app.features.settings.crosssigning import im.vector.app.core.platform.VectorViewModelAction sealed class CrossSigningSettingsAction : VectorViewModelAction { - object InitializeCrossSigning: CrossSigningSettingsAction() - object SsoAuthDone: CrossSigningSettingsAction() - data class PasswordAuthDone(val password: String): CrossSigningSettingsAction() - object ReAuthCancelled: CrossSigningSettingsAction() + object InitializeCrossSigning : CrossSigningSettingsAction() + object SsoAuthDone : CrossSigningSettingsAction() + data class PasswordAuthDone(val password: String) : CrossSigningSettingsAction() + object ReAuthCancelled : CrossSigningSettingsAction() } diff --git a/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsFragment.kt b/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsFragment.kt index 17fbc333b5f..d60d9138d7c 100644 --- a/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsFragment.kt @@ -35,7 +35,6 @@ import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.databinding.FragmentGenericRecyclerBinding import im.vector.app.features.auth.ReAuthActivity import org.matrix.android.sdk.api.auth.data.LoginFlowTypes - import javax.inject.Inject /** diff --git a/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewModel.kt index 8bdf97b6ec9..1bd498d1abf 100644 --- a/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/crosssigning/CrossSigningSettingsViewModel.kt @@ -102,8 +102,8 @@ class CrossSigningSettingsViewModel @AssistedInject constructor( errCode: String?, promise: Continuation) { Timber.d("## UIA : initializeCrossSigning UIA") - if (flowResponse.nextUncompletedStage() == LoginFlowTypes.PASSWORD - && reAuthHelper.data != null && errCode == null) { + if (flowResponse.nextUncompletedStage() == LoginFlowTypes.PASSWORD && + reAuthHelper.data != null && errCode == null) { UserPasswordAuth( session = null, user = session.myUserId, diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetController.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetController.kt index d6ef31abf2a..c109920cd67 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetController.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetController.kt @@ -33,8 +33,8 @@ import javax.inject.Inject class DeviceVerificationInfoBottomSheetController @Inject constructor( private val stringProvider: StringProvider, - private val colorProvider: ColorProvider) - : TypedEpoxyController() { + private val colorProvider: ColorProvider) : + TypedEpoxyController() { var callback: Callback? = null diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetViewModel.kt index ee5b0a60929..5f903cf49d7 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DeviceVerificationInfoBottomSheetViewModel.kt @@ -20,8 +20,8 @@ import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.core.platform.EmptyAction import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel @@ -91,8 +91,8 @@ class DeviceVerificationInfoBottomSheetViewModel @AssistedInject constructor(@As companion object : MvRxViewModelFactory { @JvmStatic - override fun create(viewModelContext: ViewModelContext, state: DeviceVerificationInfoBottomSheetViewState) - : DeviceVerificationInfoBottomSheetViewModel? { + override fun create(viewModelContext: ViewModelContext, state: DeviceVerificationInfoBottomSheetViewState): + DeviceVerificationInfoBottomSheetViewModel? { val fragment: DeviceVerificationInfoBottomSheet = (viewModelContext as FragmentViewModelContext).fragment() val args = viewModelContext.args() return fragment.deviceVerificationInfoViewModelFactory.create(state, args.deviceId) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesAction.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesAction.kt index 46a476c2701..e402982d97d 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesAction.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesAction.kt @@ -22,6 +22,7 @@ import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo sealed class DevicesAction : VectorViewModelAction { object Refresh : DevicesAction() data class Delete(val deviceId: String) : DevicesAction() + // data class Password(val password: String) : DevicesAction() data class Rename(val deviceId: String, val newName: String) : DevicesAction() @@ -31,7 +32,7 @@ sealed class DevicesAction : VectorViewModelAction { object CompleteSecurity : DevicesAction() data class MarkAsManuallyVerified(val cryptoDeviceInfo: CryptoDeviceInfo) : DevicesAction() - object SsoAuthDone: DevicesAction() - data class PasswordAuthDone(val password: String): DevicesAction() - object ReAuthCancelled: DevicesAction() + object SsoAuthDone : DevicesAction() + data class PasswordAuthDone(val password: String) : DevicesAction() + object ReAuthCancelled : DevicesAction() } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewEvents.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewEvents.kt index 8535c698a73..9e1f83582ff 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewEvents.kt @@ -18,8 +18,8 @@ package im.vector.app.features.settings.devices import im.vector.app.core.platform.VectorViewEvents -import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse +import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo @@ -28,6 +28,7 @@ import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo */ sealed class DevicesViewEvents : VectorViewEvents { data class Loading(val message: CharSequence? = null) : DevicesViewEvents() + // object HideLoading : DevicesViewEvents() data class Failure(val throwable: Throwable) : DevicesViewEvents() diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt index c48b08e8060..30342dbf2ae 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt @@ -40,23 +40,23 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.NoOpMatrixCallback +import org.matrix.android.sdk.api.auth.UIABaseAuth import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor +import org.matrix.android.sdk.api.auth.UserPasswordAuth import org.matrix.android.sdk.api.auth.data.LoginFlowTypes +import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse +import org.matrix.android.sdk.api.auth.registration.nextUncompletedStage import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod import org.matrix.android.sdk.api.session.crypto.verification.VerificationService import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState -import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse -import org.matrix.android.sdk.api.auth.registration.nextUncompletedStage import org.matrix.android.sdk.internal.crypto.crosssigning.DeviceTrustLevel import org.matrix.android.sdk.internal.crypto.crosssigning.fromBase64 import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo import org.matrix.android.sdk.internal.crypto.model.rest.DefaultBaseAuth import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo -import org.matrix.android.sdk.api.auth.UIABaseAuth -import org.matrix.android.sdk.api.auth.UserPasswordAuth import org.matrix.android.sdk.internal.util.awaitCallback import org.matrix.android.sdk.rx.rx import timber.log.Timber diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/VectorSettingsDevicesFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/VectorSettingsDevicesFragment.kt index 5af886b265b..62923d4f3da 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/VectorSettingsDevicesFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/VectorSettingsDevicesFragment.kt @@ -40,7 +40,6 @@ import im.vector.app.databinding.FragmentGenericRecyclerBinding import im.vector.app.features.auth.ReAuthActivity import im.vector.app.features.crypto.verification.VerificationBottomSheet import org.matrix.android.sdk.api.auth.data.LoginFlowTypes - import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo import javax.inject.Inject diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataFragment.kt index a55032e44a4..570da6875a4 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataFragment.kt @@ -31,7 +31,6 @@ import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.resources.ColorProvider import im.vector.app.core.utils.createJSonViewerStyleProvider import im.vector.app.databinding.FragmentGenericRecyclerBinding - import org.billcarsonfr.jsonviewer.JSonViewerDialog import org.matrix.android.sdk.api.session.accountdata.UserAccountDataEvent import org.matrix.android.sdk.internal.di.MoshiProvider diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataViewModel.kt index d50caea5792..7e5d690d8e0 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devtools/AccountDataViewModel.kt @@ -24,8 +24,8 @@ import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel @@ -39,8 +39,8 @@ data class AccountDataViewState( ) : MvRxState class AccountDataViewModel @AssistedInject constructor(@Assisted initialState: AccountDataViewState, - private val session: Session) - : VectorViewModel(initialState) { + private val session: Session) : + VectorViewModel(initialState) { init { session.rx().liveUserAccountData(emptySet()) diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingEventsPaperTrailFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingEventsPaperTrailFragment.kt index 6a0b13fe870..7325288c551 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingEventsPaperTrailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingEventsPaperTrailFragment.kt @@ -29,7 +29,6 @@ import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.resources.ColorProvider import im.vector.app.core.utils.createJSonViewerStyleProvider import im.vector.app.databinding.FragmentGenericRecyclerBinding - import org.billcarsonfr.jsonviewer.JSonViewerDialog import org.matrix.android.sdk.api.session.events.model.Event import javax.inject.Inject diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingEventsPaperTrailViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingEventsPaperTrailViewModel.kt index 325538ee5e3..6d95255e70f 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingEventsPaperTrailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devtools/GossipingEventsPaperTrailViewModel.kt @@ -25,8 +25,8 @@ import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.core.platform.EmptyAction import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel @@ -39,8 +39,8 @@ data class GossipingEventsPaperTrailState( ) : MvRxState class GossipingEventsPaperTrailViewModel @AssistedInject constructor(@Assisted initialState: GossipingEventsPaperTrailState, - private val session: Session) - : VectorViewModel(initialState) { + private val session: Session) : + VectorViewModel(initialState) { init { refresh() diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/IncomingKeyRequestListFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/IncomingKeyRequestListFragment.kt index 91e770babfb..8bf89d975c9 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devtools/IncomingKeyRequestListFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devtools/IncomingKeyRequestListFragment.kt @@ -27,7 +27,6 @@ import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.configureWith import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.databinding.FragmentGenericRecyclerBinding - import javax.inject.Inject class IncomingKeyRequestListFragment @Inject constructor( diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestListViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestListViewModel.kt index c0a791233f7..88cae8fac29 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestListViewModel.kt @@ -25,8 +25,8 @@ import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.core.platform.EmptyAction import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel @@ -42,8 +42,8 @@ data class KeyRequestListViewState( ) : MvRxState class KeyRequestListViewModel @AssistedInject constructor(@Assisted initialState: KeyRequestListViewState, - private val session: Session) - : VectorViewModel(initialState) { + private val session: Session) : + VectorViewModel(initialState) { init { refresh() diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestViewModel.kt index e7a56ef9df8..f51c90ec5f4 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestViewModel.kt @@ -28,8 +28,8 @@ import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewEvents import im.vector.app.core.platform.VectorViewModel @@ -52,8 +52,8 @@ data class KeyRequestViewState( class KeyRequestViewModel @AssistedInject constructor( @Assisted initialState: KeyRequestViewState, - private val session: Session) - : VectorViewModel(initialState) { + private val session: Session) : + VectorViewModel(initialState) { @AssistedFactory interface Factory { diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestsFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestsFragment.kt index 7bee0e283c6..0b3d8812f1f 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devtools/KeyRequestsFragment.kt @@ -38,7 +38,6 @@ import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.utils.selectTxtFileToWrite import im.vector.app.databinding.FragmentDevtoolKeyrequestsBinding - import org.matrix.android.sdk.api.extensions.tryOrNull import javax.inject.Inject diff --git a/vector/src/main/java/im/vector/app/features/settings/devtools/OutgoingKeyRequestListFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devtools/OutgoingKeyRequestListFragment.kt index 77cddfff8cf..0cbca2f38ae 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devtools/OutgoingKeyRequestListFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devtools/OutgoingKeyRequestListFragment.kt @@ -27,7 +27,6 @@ import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.configureWith import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.databinding.FragmentGenericRecyclerBinding - import javax.inject.Inject class OutgoingKeyRequestListFragment @Inject constructor( val viewModelFactory: KeyRequestListViewModel.Factory, diff --git a/vector/src/main/java/im/vector/app/features/settings/ignored/IgnoredUsersViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/ignored/IgnoredUsersViewModel.kt index aa00f71542b..6238885528a 100644 --- a/vector/src/main/java/im/vector/app/features/settings/ignored/IgnoredUsersViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/ignored/IgnoredUsersViewModel.kt @@ -27,8 +27,8 @@ import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.VectorViewModelAction import kotlinx.coroutines.launch @@ -46,8 +46,8 @@ sealed class IgnoredUsersAction : VectorViewModelAction { } class IgnoredUsersViewModel @AssistedInject constructor(@Assisted initialState: IgnoredUsersViewState, - private val session: Session) - : VectorViewModel(initialState) { + private val session: Session) : + VectorViewModel(initialState) { @AssistedFactory interface Factory { diff --git a/vector/src/main/java/im/vector/app/features/settings/ignored/VectorSettingsIgnoredUsersFragment.kt b/vector/src/main/java/im/vector/app/features/settings/ignored/VectorSettingsIgnoredUsersFragment.kt index fe09008cdd1..1526ac0e69b 100644 --- a/vector/src/main/java/im/vector/app/features/settings/ignored/VectorSettingsIgnoredUsersFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/ignored/VectorSettingsIgnoredUsersFragment.kt @@ -33,7 +33,6 @@ import im.vector.app.core.extensions.configureWith import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.databinding.FragmentGenericRecyclerBinding - import javax.inject.Inject class VectorSettingsIgnoredUsersFragment @Inject constructor( diff --git a/vector/src/main/java/im/vector/app/features/settings/locale/LocalePickerController.kt b/vector/src/main/java/im/vector/app/features/settings/locale/LocalePickerController.kt index 0d178bddea2..4e1c62a4ec3 100644 --- a/vector/src/main/java/im/vector/app/features/settings/locale/LocalePickerController.kt +++ b/vector/src/main/java/im/vector/app/features/settings/locale/LocalePickerController.kt @@ -37,7 +37,6 @@ class LocalePickerController @Inject constructor( var listener: Listener? = null - @ExperimentalStdlibApi override fun buildModels(data: LocalePickerViewState?) { val list = data?.locales ?: return val host = this diff --git a/vector/src/main/java/im/vector/app/features/settings/locale/LocalePickerFragment.kt b/vector/src/main/java/im/vector/app/features/settings/locale/LocalePickerFragment.kt index 45fa6b735f5..7368bec397d 100644 --- a/vector/src/main/java/im/vector/app/features/settings/locale/LocalePickerFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/locale/LocalePickerFragment.kt @@ -30,7 +30,6 @@ import im.vector.app.core.extensions.exhaustive import im.vector.app.core.extensions.restart import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.databinding.FragmentLocalePickerBinding - import java.util.Locale import javax.inject.Inject diff --git a/vector/src/main/java/im/vector/app/features/settings/locale/LocalePickerViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/locale/LocalePickerViewModel.kt index 2e59b0ef7d9..d0077eec664 100644 --- a/vector/src/main/java/im/vector/app/features/settings/locale/LocalePickerViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/locale/LocalePickerViewModel.kt @@ -23,8 +23,8 @@ import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import im.vector.app.features.configuration.VectorConfiguration diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/NotificationIndex.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/NotificationIndex.kt index 29d316bb76c..793b94db1db 100644 --- a/vector/src/main/java/im/vector/app/features/settings/notifications/NotificationIndex.kt +++ b/vector/src/main/java/im/vector/app/features/settings/notifications/NotificationIndex.kt @@ -44,11 +44,11 @@ val PushRule.notificationIndex: NotificationIndex? get() = */ private fun ruleMatches(rule: PushRule, targetRule: PushRule): Boolean { // Rules match if both are disabled, or if both are enabled and their highlight/sound/notify actions match up. - return (!rule.enabled && !targetRule.enabled) - || (rule.enabled - && targetRule.enabled - && rule.getHighlight() == targetRule.getHighlight() - && rule.getNotificationSound() == targetRule.getNotificationSound() - && rule.shouldNotify() == targetRule.shouldNotify() - && rule.shouldNotNotify() == targetRule.shouldNotNotify()) + return (!rule.enabled && !targetRule.enabled) || + (rule.enabled && + targetRule.enabled && + rule.getHighlight() == targetRule.getHighlight() && + rule.getNotificationSound() == targetRule.getNotificationSound() && + rule.shouldNotify() == targetRule.shouldNotify() && + rule.shouldNotNotify() == targetRule.shouldNotNotify()) } diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsAdvancedNotificationPreferenceFragment.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsAdvancedNotificationPreferenceFragment.kt index 93a788f528e..3b4aef929d7 100644 --- a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsAdvancedNotificationPreferenceFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsAdvancedNotificationPreferenceFragment.kt @@ -27,8 +27,8 @@ import org.matrix.android.sdk.api.pushrules.RuleIds import org.matrix.android.sdk.api.pushrules.rest.PushRuleAndKind import javax.inject.Inject -class VectorSettingsAdvancedNotificationPreferenceFragment @Inject constructor() - : VectorSettingsBaseFragment() { +class VectorSettingsAdvancedNotificationPreferenceFragment @Inject constructor() : + VectorSettingsBaseFragment() { override var titleRes: Int = R.string.settings_notification_advanced diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsDefaultNotificationPreferenceFragment.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsDefaultNotificationPreferenceFragment.kt index 3fc6293e6b3..840e8ccde01 100644 --- a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsDefaultNotificationPreferenceFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsDefaultNotificationPreferenceFragment.kt @@ -20,8 +20,8 @@ import im.vector.app.R import im.vector.app.core.preference.VectorPreferenceCategory import org.matrix.android.sdk.api.pushrules.RuleIds -class VectorSettingsDefaultNotificationPreferenceFragment - : VectorSettingsPushRuleNotificationPreferenceFragment() { +class VectorSettingsDefaultNotificationPreferenceFragment : + VectorSettingsPushRuleNotificationPreferenceFragment() { override var titleRes: Int = R.string.settings_notification_default diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsKeywordAndMentionsNotificationPreferenceFragment.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsKeywordAndMentionsNotificationPreferenceFragment.kt index 59ed727191d..fb1a357c303 100644 --- a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsKeywordAndMentionsNotificationPreferenceFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsKeywordAndMentionsNotificationPreferenceFragment.kt @@ -33,8 +33,8 @@ import org.matrix.android.sdk.api.pushrules.RuleKind import org.matrix.android.sdk.api.pushrules.rest.PushRule import org.matrix.android.sdk.api.pushrules.toJson -class VectorSettingsKeywordAndMentionsNotificationPreferenceFragment - : VectorSettingsPushRuleNotificationPreferenceFragment() { +class VectorSettingsKeywordAndMentionsNotificationPreferenceFragment : + VectorSettingsPushRuleNotificationPreferenceFragment() { override var titleRes: Int = R.string.settings_notification_mentions_and_keywords @@ -88,7 +88,7 @@ class VectorSettingsKeywordAndMentionsNotificationPreferenceFragment false } - editKeywordPreference.listener = object: KeywordPreference.Listener { + editKeywordPreference.listener = object : KeywordPreference.Listener { override fun onFocusDidChange(hasFocus: Boolean) { keywordsHasFocus = true } diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt index 40f575c8539..098d1b2caab 100644 --- a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationPreferenceFragment.kt @@ -23,7 +23,10 @@ import android.media.RingtoneManager import android.net.Uri import android.os.Parcelable import android.widget.Toast +import androidx.lifecycle.LiveData +import androidx.lifecycle.distinctUntilChanged import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.map import androidx.preference.Preference import androidx.preference.SwitchPreference import im.vector.app.R @@ -43,10 +46,14 @@ import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.VectorSettingsBaseFragment import im.vector.app.features.settings.VectorSettingsFragmentInteractionListener import im.vector.app.push.fcm.FcmHelper +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.pushrules.RuleIds import org.matrix.android.sdk.api.pushrules.RuleKind +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.identity.ThreePid +import org.matrix.android.sdk.api.session.pushers.Pusher import javax.inject.Inject // Referenced in vector_settings_preferences_root.xml @@ -116,11 +123,51 @@ class VectorSettingsNotificationPreferenceFragment @Inject constructor( } } + bindEmailNotifications() refreshBackgroundSyncPrefs() handleSystemPreference() } + private fun bindEmailNotifications() { + val initialEmails = session.getEmailsWithPushInformation() + bindEmailNotificationCategory(initialEmails) + session.getEmailsWithPushInformationLive().observe(this) { emails -> + if (initialEmails != emails) { + bindEmailNotificationCategory(emails) + } + } + } + + private fun bindEmailNotificationCategory(emails: List>) { + findPreference(VectorPreferences.SETTINGS_EMAIL_NOTIFICATION_CATEGORY_PREFERENCE_KEY)?.let { category -> + category.removeAll() + if (emails.isEmpty()) { + val vectorPreference = VectorPreference(requireContext()) + vectorPreference.title = resources.getString(R.string.settings_notification_emails_no_emails) + category.addPreference(vectorPreference) + vectorPreference.setOnPreferenceClickListener { + interactionListener?.navigateToEmailAndPhoneNumbers() + true + } + } else { + emails.forEach { (emailPid, isEnabled) -> + val pref = VectorSwitchPreference(requireContext()) + pref.title = resources.getString(R.string.settings_notification_emails_enable_for_email, emailPid.email) + pref.isChecked = isEnabled + pref.setTransactionalSwitchChangeListener(lifecycleScope) { isChecked -> + if (isChecked) { + pushManager.registerEmailForPush(emailPid.email) + } else { + pushManager.unregisterEmailPusher(emailPid.email) + } + } + category.addPreference(pref) + } + } + } + } + private val batteryStartForActivityResult = registerStartForActivityResult { // Noop } @@ -343,3 +390,43 @@ class VectorSettingsNotificationPreferenceFragment @Inject constructor( } } } + +private fun SwitchPreference.setTransactionalSwitchChangeListener(scope: CoroutineScope, transaction: suspend (Boolean) -> Unit) { + setOnPreferenceChangeListener { switchPreference, isChecked -> + require(switchPreference is SwitchPreference) + val originalState = switchPreference.isChecked + scope.launch { + try { + transaction(isChecked as Boolean) + } catch (failure: Throwable) { + switchPreference.isChecked = originalState + Toast.makeText(switchPreference.context, R.string.unknown_error, Toast.LENGTH_SHORT).show() + } + } + true + } +} + +/** + * Fetches the current users 3pid emails and pairs them with their enabled state. + * If no pusher is available for a given email we can infer that push is not registered for the email. + * @return a list of ThreePid emails paired with the email notification enabled state. true if email notifications are enabled, false if not. + * @see ThreePid.Email + */ +private fun Session.getEmailsWithPushInformation(): List> { + val emailPushers = getPushers().filter { it.kind == Pusher.KIND_EMAIL } + return getThreePids() + .filterIsInstance() + .map { it to emailPushers.any { pusher -> pusher.pushKey == it.email } } +} + +private fun Session.getEmailsWithPushInformationLive(): LiveData>> { + return getThreePidsLive(refreshData = false) + .distinctUntilChanged() + .map { threePids -> + val emailPushers = getPushers().filter { it.kind == Pusher.KIND_EMAIL } + threePids + .filterIsInstance() + .map { it to emailPushers.any { pusher -> pusher.pushKey == it.email } } + } +} diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationsTroubleshootFragment.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationsTroubleshootFragment.kt index 6e47079afa3..1a830dc0c1a 100644 --- a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationsTroubleshootFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsNotificationsTroubleshootFragment.kt @@ -40,7 +40,6 @@ import im.vector.app.features.settings.VectorSettingsFragmentInteractionListener import im.vector.app.features.settings.troubleshoot.NotificationTroubleshootTestManager import im.vector.app.features.settings.troubleshoot.TroubleshootTest import im.vector.app.push.fcm.NotificationTroubleshootTestManagerFactory - import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.extensions.tryOrNull import javax.inject.Inject diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsOtherNotificationPreferenceFragment.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsOtherNotificationPreferenceFragment.kt index 2e083a7d659..71f8f0920ac 100644 --- a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsOtherNotificationPreferenceFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsOtherNotificationPreferenceFragment.kt @@ -20,8 +20,8 @@ import im.vector.app.R import im.vector.app.core.preference.VectorPreferenceCategory import org.matrix.android.sdk.api.pushrules.RuleIds -class VectorSettingsOtherNotificationPreferenceFragment - : VectorSettingsPushRuleNotificationPreferenceFragment() { +class VectorSettingsOtherNotificationPreferenceFragment : + VectorSettingsPushRuleNotificationPreferenceFragment() { override var titleRes: Int = R.string.settings_notification_other diff --git a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsPushRuleNotificationPreferenceFragment.kt b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsPushRuleNotificationPreferenceFragment.kt index dbf33f8fb3c..26ee2fc6012 100644 --- a/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsPushRuleNotificationPreferenceFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/notifications/VectorSettingsPushRuleNotificationPreferenceFragment.kt @@ -24,8 +24,8 @@ import kotlinx.coroutines.launch import org.matrix.android.sdk.api.pushrules.RuleKind import org.matrix.android.sdk.api.pushrules.rest.PushRuleAndKind -abstract class VectorSettingsPushRuleNotificationPreferenceFragment - : VectorSettingsBaseFragment() { +abstract class VectorSettingsPushRuleNotificationPreferenceFragment : + VectorSettingsBaseFragment() { abstract val prefKeyToPushRuleId: Map diff --git a/vector/src/main/java/im/vector/app/features/settings/push/PushGateWayController.kt b/vector/src/main/java/im/vector/app/features/settings/push/PushGateWayController.kt index 679f4068320..6cb19b13c56 100644 --- a/vector/src/main/java/im/vector/app/features/settings/push/PushGateWayController.kt +++ b/vector/src/main/java/im/vector/app/features/settings/push/PushGateWayController.kt @@ -26,6 +26,8 @@ class PushGateWayController @Inject constructor( private val stringProvider: StringProvider ) : TypedEpoxyController() { + var interactionListener: PushGatewayItemInteractions? = null + override fun buildModels(data: PushGatewayViewState?) { val host = this data?.pushGateways?.invoke()?.let { pushers -> @@ -39,6 +41,9 @@ class PushGateWayController @Inject constructor( pushGatewayItem { id("${it.pushKey}_${it.appId}") pusher(it) + host.interactionListener?.let { + interactions(it) + } } } } diff --git a/vector/src/main/java/im/vector/app/features/settings/push/PushGatewayAction.kt b/vector/src/main/java/im/vector/app/features/settings/push/PushGatewayAction.kt index 566a068a7dd..034b0b5ac72 100644 --- a/vector/src/main/java/im/vector/app/features/settings/push/PushGatewayAction.kt +++ b/vector/src/main/java/im/vector/app/features/settings/push/PushGatewayAction.kt @@ -17,7 +17,9 @@ package im.vector.app.features.settings.push import im.vector.app.core.platform.VectorViewModelAction +import org.matrix.android.sdk.api.session.pushers.Pusher sealed class PushGatewayAction : VectorViewModelAction { object Refresh : PushGatewayAction() + data class RemovePusher(val pusher: Pusher) : PushGatewayAction() } diff --git a/vector/src/main/java/im/vector/app/features/settings/push/PushGatewayItem.kt b/vector/src/main/java/im/vector/app/features/settings/push/PushGatewayItem.kt index dc66e1983be..04aa2747d79 100644 --- a/vector/src/main/java/im/vector/app/features/settings/push/PushGatewayItem.kt +++ b/vector/src/main/java/im/vector/app/features/settings/push/PushGatewayItem.kt @@ -16,12 +16,14 @@ package im.vector.app.features.settings.push +import android.view.View import android.widget.TextView import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyModelClass import com.airbnb.epoxy.EpoxyModelWithHolder import im.vector.app.R import im.vector.app.core.epoxy.VectorEpoxyHolder +import im.vector.app.core.extensions.setTextOrHide import org.matrix.android.sdk.api.session.pushers.Pusher @EpoxyModelClass(layout = R.layout.item_pushgateway) @@ -30,33 +32,45 @@ abstract class PushGatewayItem : EpoxyModelWithHolder() @EpoxyAttribute lateinit var pusher: Pusher + @EpoxyAttribute + lateinit var interactions: PushGatewayItemInteractions + override fun bind(holder: Holder) { super.bind(holder) holder.kind.text = when (pusher.kind) { - // TODO Create const - "http" -> "Http Pusher" - "mail" -> "Email Pusher" - else -> pusher.kind + Pusher.KIND_HTTP -> "Http Pusher" + Pusher.KIND_EMAIL -> "Email Pusher" + else -> pusher.kind } holder.appId.text = pusher.appId holder.pushKey.text = pusher.pushKey holder.appName.text = pusher.appDisplayName - holder.url.text = pusher.data.url - holder.format.text = pusher.data.format + holder.url.setTextOrHide(pusher.data.url, hideWhenBlank = true, holder.urlTitle) + holder.format.setTextOrHide(pusher.data.format, hideWhenBlank = true, holder.formatTitle) holder.deviceName.text = pusher.deviceDisplayName + holder.removeButton.setOnClickListener { + interactions.onRemovePushTapped(pusher) + } } class Holder : VectorEpoxyHolder() { val kind by bind(R.id.pushGatewayKind) val pushKey by bind(R.id.pushGatewayKeyValue) val deviceName by bind(R.id.pushGatewayDeviceNameValue) + val formatTitle by bind(R.id.pushGatewayFormat) val format by bind(R.id.pushGatewayFormatValue) + val urlTitle by bind(R.id.pushGatewayURL) val url by bind(R.id.pushGatewayURLValue) val appName by bind(R.id.pushGatewayAppNameValue) val appId by bind(R.id.pushGatewayAppIdValue) + val removeButton by bind(R.id.pushGatewayDeleteButton) } } +interface PushGatewayItemInteractions { + fun onRemovePushTapped(pusher: Pusher) +} + // // abstract class ReactionInfoSimpleItem : EpoxyModelWithHolder() { diff --git a/vector/src/main/java/im/vector/app/features/settings/push/PushGatewayViewEvents.kt b/vector/src/main/java/im/vector/app/features/settings/push/PushGatewayViewEvents.kt new file mode 100644 index 00000000000..380a5df8760 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/push/PushGatewayViewEvents.kt @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.settings.push + +import im.vector.app.core.platform.VectorViewEvents + +sealed class PushGatewayViewEvents : VectorViewEvents { + data class RemovePusherFailed(val cause: Throwable) : PushGatewayViewEvents() +} diff --git a/vector/src/main/java/im/vector/app/features/settings/push/PushGatewaysFragment.kt b/vector/src/main/java/im/vector/app/features/settings/push/PushGatewaysFragment.kt index be2457397d0..0801e781972 100644 --- a/vector/src/main/java/im/vector/app/features/settings/push/PushGatewaysFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/push/PushGatewaysFragment.kt @@ -24,12 +24,14 @@ import android.view.ViewGroup import androidx.appcompat.app.AppCompatActivity import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState +import com.google.android.material.dialog.MaterialAlertDialogBuilder import im.vector.app.R import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.configureWith +import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.databinding.FragmentGenericRecyclerBinding - +import org.matrix.android.sdk.api.session.pushers.Pusher import javax.inject.Inject // Referenced in vector_settings_notifications.xml @@ -64,7 +66,21 @@ class PushGatewaysFragment @Inject constructor( override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + epoxyController.interactionListener = object : PushGatewayItemInteractions { + override fun onRemovePushTapped(pusher: Pusher) = viewModel.handle(PushGatewayAction.RemovePusher(pusher)) + } views.genericRecyclerView.configureWith(epoxyController, dividerDrawable = R.drawable.divider_horizontal) + viewModel.observeViewEvents { + when (it) { + is PushGatewayViewEvents.RemovePusherFailed -> { + MaterialAlertDialogBuilder(requireContext()) + .setTitle(R.string.dialog_title_error) + .setMessage(errorFormatter.toHumanReadable(it.cause)) + .setPositiveButton(android.R.string.ok, null) + .show() + } + }.exhaustive + } } override fun onDestroyView() { diff --git a/vector/src/main/java/im/vector/app/features/settings/push/PushGatewaysViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/push/PushGatewaysViewModel.kt index 7981d71ce17..bcfb68387b4 100644 --- a/vector/src/main/java/im/vector/app/features/settings/push/PushGatewaysViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/push/PushGatewaysViewModel.kt @@ -16,6 +16,7 @@ package im.vector.app.features.settings.push +import androidx.lifecycle.viewModelScope import com.airbnb.mvrx.Async import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MvRxState @@ -23,11 +24,11 @@ import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.core.extensions.exhaustive -import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel +import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.pushers.Pusher import org.matrix.android.sdk.rx.RxSession @@ -37,8 +38,8 @@ data class PushGatewayViewState( ) : MvRxState class PushGatewaysViewModel @AssistedInject constructor(@Assisted initialState: PushGatewayViewState, - private val session: Session) - : VectorViewModel(initialState) { + private val session: Session) : + VectorViewModel(initialState) { @AssistedFactory interface Factory { @@ -70,10 +71,21 @@ class PushGatewaysViewModel @AssistedInject constructor(@Assisted initialState: override fun handle(action: PushGatewayAction) { when (action) { - is PushGatewayAction.Refresh -> handleRefresh() + is PushGatewayAction.Refresh -> handleRefresh() + is PushGatewayAction.RemovePusher -> removePusher(action.pusher) }.exhaustive } + private fun removePusher(pusher: Pusher) { + viewModelScope.launch { + kotlin.runCatching { + session.removePusher(pusher) + }.onFailure { + _viewEvents.post(PushGatewayViewEvents.RemovePusherFailed(it)) + } + } + } + private fun handleRefresh() { session.refreshPushers() } diff --git a/vector/src/main/java/im/vector/app/features/settings/push/PushRulesFragment.kt b/vector/src/main/java/im/vector/app/features/settings/push/PushRulesFragment.kt index d9dbdcc8d24..666f27272b0 100644 --- a/vector/src/main/java/im/vector/app/features/settings/push/PushRulesFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/push/PushRulesFragment.kt @@ -27,7 +27,6 @@ import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.configureWith import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.databinding.FragmentGenericRecyclerBinding - import javax.inject.Inject // Referenced in vector_settings_notifications.xml diff --git a/vector/src/main/java/im/vector/app/features/settings/push/PushRulesViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/push/PushRulesViewModel.kt index 64ddc275ebe..645ddbb8f73 100644 --- a/vector/src/main/java/im/vector/app/features/settings/push/PushRulesViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/push/PushRulesViewModel.kt @@ -28,8 +28,8 @@ data class PushRulesViewState( val rules: List = emptyList() ) : MvRxState -class PushRulesViewModel(initialState: PushRulesViewState) - : VectorViewModel(initialState) { +class PushRulesViewModel(initialState: PushRulesViewState) : + VectorViewModel(initialState) { companion object : MvRxViewModelFactory { diff --git a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsController.kt b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsController.kt index f9cf3bf5a3f..cdc40185aa0 100644 --- a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsController.kt +++ b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsController.kt @@ -295,9 +295,9 @@ class ThreePidsSettingsController @Inject constructor( val failure = (data.msisdnValidationRequests[threePid.value] as? Fail)?.error ?: return null // Wrong code? // See https://github.com/matrix-org/synapse/issues/8218 - return if (failure is Failure.ServerError - && failure.httpCode == 400 - && failure.error.code == MatrixError.M_UNKNOWN) { + return if (failure is Failure.ServerError && + failure.httpCode == 400 && + failure.error.code == MatrixError.M_UNKNOWN) { stringProvider.getString(R.string.settings_text_message_sent_wrong_code) } else { errorFormatter.toHumanReadable(failure) diff --git a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewEvents.kt b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewEvents.kt index 0346fd137e5..94ea8d24c63 100644 --- a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewEvents.kt @@ -21,6 +21,7 @@ import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse sealed class ThreePidsSettingsViewEvents : VectorViewEvents { data class Failure(val throwable: Throwable) : ThreePidsSettingsViewEvents() + // object RequestPassword : ThreePidsSettingsViewEvents() data class RequestReAuth(val registrationFlowResponse: RegistrationFlowResponse, val lastErrorCode: String?) : ThreePidsSettingsViewEvents() } diff --git a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewModel.kt index ac565e72a1a..358436a7fc3 100644 --- a/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/threepids/ThreePidsSettingsViewModel.kt @@ -33,14 +33,14 @@ import im.vector.app.core.resources.StringProvider import im.vector.app.core.utils.ReadOnceTrue import im.vector.app.features.auth.ReAuthActivity import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.auth.UIABaseAuth import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor +import org.matrix.android.sdk.api.auth.UserPasswordAuth import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.identity.ThreePid import org.matrix.android.sdk.internal.crypto.crosssigning.fromBase64 import org.matrix.android.sdk.internal.crypto.model.rest.DefaultBaseAuth -import org.matrix.android.sdk.api.auth.UIABaseAuth -import org.matrix.android.sdk.api.auth.UserPasswordAuth import org.matrix.android.sdk.rx.rx import timber.log.Timber import kotlin.coroutines.Continuation diff --git a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/NotificationTroubleshootRecyclerViewAdapter.kt b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/NotificationTroubleshootRecyclerViewAdapter.kt index 70e5bdff10e..8f0b2cfe742 100644 --- a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/NotificationTroubleshootRecyclerViewAdapter.kt +++ b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/NotificationTroubleshootRecyclerViewAdapter.kt @@ -24,8 +24,8 @@ import im.vector.app.R import im.vector.app.databinding.ItemNotificationTroubleshootBinding import im.vector.app.features.themes.ThemeUtils -class NotificationTroubleshootRecyclerViewAdapter(val tests: ArrayList) - : RecyclerView.Adapter() { +class NotificationTroubleshootRecyclerViewAdapter(val tests: ArrayList) : + RecyclerView.Adapter() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val inflater = LayoutInflater.from(parent.context) diff --git a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestAccountSettings.kt b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestAccountSettings.kt index dad35fd13f2..9d06e1724c6 100644 --- a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestAccountSettings.kt +++ b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestAccountSettings.kt @@ -33,8 +33,8 @@ import javax.inject.Inject * Check that the main pushRule (RULE_ID_DISABLE_ALL) is correctly setup */ class TestAccountSettings @Inject constructor(private val stringProvider: StringProvider, - private val activeSessionHolder: ActiveSessionHolder) - : TroubleshootTest(R.string.settings_troubleshoot_test_account_settings_title) { + private val activeSessionHolder: ActiveSessionHolder) : + TroubleshootTest(R.string.settings_troubleshoot_test_account_settings_title) { override fun perform(activityResultLauncher: ActivityResultLauncher) { val session = activeSessionHolder.getSafeActiveSession() ?: return diff --git a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestDeviceSettings.kt b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestDeviceSettings.kt index 0d661e8b16e..f9fc7e80e3f 100644 --- a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestDeviceSettings.kt +++ b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestDeviceSettings.kt @@ -26,8 +26,8 @@ import javax.inject.Inject * Checks if notifications are enable in the system settings for this app. */ class TestDeviceSettings @Inject constructor(private val vectorPreferences: VectorPreferences, - private val stringProvider: StringProvider) - : TroubleshootTest(R.string.settings_troubleshoot_test_device_settings_title) { + private val stringProvider: StringProvider) : + TroubleshootTest(R.string.settings_troubleshoot_test_device_settings_title) { override fun perform(activityResultLauncher: ActivityResultLauncher) { if (vectorPreferences.areNotificationEnabledForDevice()) { diff --git a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestNotification.kt b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestNotification.kt index 6f25ecfe399..412916c2013 100644 --- a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestNotification.kt +++ b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestNotification.kt @@ -29,8 +29,8 @@ import javax.inject.Inject */ class TestNotification @Inject constructor(private val context: Context, private val notificationUtils: NotificationUtils, - private val stringProvider: StringProvider) - : TroubleshootTest(R.string.settings_troubleshoot_test_notification_title) { + private val stringProvider: StringProvider) : + TroubleshootTest(R.string.settings_troubleshoot_test_notification_title) { override fun perform(activityResultLauncher: ActivityResultLauncher) { // Display the notification right now diff --git a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestPushRulesSettings.kt b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestPushRulesSettings.kt index eea5705b7aa..79e4377a5c8 100644 --- a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestPushRulesSettings.kt +++ b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestPushRulesSettings.kt @@ -26,8 +26,8 @@ import org.matrix.android.sdk.api.pushrules.getActions import javax.inject.Inject class TestPushRulesSettings @Inject constructor(private val activeSessionHolder: ActiveSessionHolder, - private val stringProvider: StringProvider) - : TroubleshootTest(R.string.settings_troubleshoot_test_bing_settings_title) { + private val stringProvider: StringProvider) : + TroubleshootTest(R.string.settings_troubleshoot_test_bing_settings_title) { private val testedRules = listOf(RuleIds.RULE_ID_CONTAIN_DISPLAY_NAME, diff --git a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestSystemSettings.kt b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestSystemSettings.kt index ee652288be2..d4f089b9436 100644 --- a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestSystemSettings.kt +++ b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestSystemSettings.kt @@ -28,8 +28,8 @@ import javax.inject.Inject * Checks if notifications are enable in the system settings for this app. */ class TestSystemSettings @Inject constructor(private val context: Context, - private val stringProvider: StringProvider) - : TroubleshootTest(R.string.settings_troubleshoot_test_system_settings_title) { + private val stringProvider: StringProvider) : + TroubleshootTest(R.string.settings_troubleshoot_test_system_settings_title) { override fun perform(activityResultLauncher: ActivityResultLauncher) { if (NotificationManagerCompat.from(context).areNotificationsEnabled()) { @@ -40,7 +40,6 @@ class TestSystemSettings @Inject constructor(private val context: Context, description = stringProvider.getString(R.string.settings_troubleshoot_test_system_settings_failed) quickFix = object : TroubleshootQuickFix(R.string.open_settings) { override fun doFix() { - if (manager?.diagStatus == TestStatus.RUNNING) return // wait before all is finished startNotificationSettingsIntent(context, activityResultLauncher) } } diff --git a/vector/src/main/java/im/vector/app/features/share/IncomingShareFragment.kt b/vector/src/main/java/im/vector/app/features/share/IncomingShareFragment.kt index 313bf2d2b74..2132ba79896 100644 --- a/vector/src/main/java/im/vector/app/features/share/IncomingShareFragment.kt +++ b/vector/src/main/java/im/vector/app/features/share/IncomingShareFragment.kt @@ -41,7 +41,6 @@ import im.vector.app.databinding.FragmentIncomingShareBinding import im.vector.app.features.attachments.AttachmentsHelper import im.vector.app.features.attachments.preview.AttachmentsPreviewActivity import im.vector.app.features.attachments.preview.AttachmentsPreviewArgs - import org.matrix.android.sdk.api.session.content.ContentAttachmentData import org.matrix.android.sdk.api.session.room.model.RoomSummary import javax.inject.Inject diff --git a/vector/src/main/java/im/vector/app/features/share/IncomingShareViewModel.kt b/vector/src/main/java/im/vector/app/features/share/IncomingShareViewModel.kt index 4d211d11de8..da1b20b4d1c 100644 --- a/vector/src/main/java/im/vector/app/features/share/IncomingShareViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/share/IncomingShareViewModel.kt @@ -21,8 +21,8 @@ import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext import com.jakewharton.rxrelay2.BehaviorRelay import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.core.extensions.exhaustive import im.vector.app.core.extensions.toggle import im.vector.app.core.platform.VectorViewModel @@ -40,8 +40,8 @@ import java.util.concurrent.TimeUnit class IncomingShareViewModel @AssistedInject constructor( @Assisted initialState: IncomingShareViewState, private val session: Session, - private val breadcrumbsRoomComparator: BreadcrumbsRoomComparator) - : VectorViewModel(initialState) { + private val breadcrumbsRoomComparator: BreadcrumbsRoomComparator) : + VectorViewModel(initialState) { @AssistedFactory interface Factory { diff --git a/vector/src/main/java/im/vector/app/features/signout/hard/SignedOutActivity.kt b/vector/src/main/java/im/vector/app/features/signout/hard/SignedOutActivity.kt index 08bd84994bf..6f05a73f133 100644 --- a/vector/src/main/java/im/vector/app/features/signout/hard/SignedOutActivity.kt +++ b/vector/src/main/java/im/vector/app/features/signout/hard/SignedOutActivity.kt @@ -23,7 +23,6 @@ import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.databinding.ActivitySignedOutBinding import im.vector.app.features.MainActivity import im.vector.app.features.MainActivityArgs - import org.matrix.android.sdk.api.failure.GlobalError import timber.log.Timber diff --git a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutActivity.kt b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutActivity.kt index 8c725cd81a8..72b9a278e22 100644 --- a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutActivity.kt +++ b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutActivity.kt @@ -30,7 +30,6 @@ import im.vector.app.core.extensions.replaceFragment import im.vector.app.features.MainActivity import im.vector.app.features.MainActivityArgs import im.vector.app.features.login.LoginActivity - import org.matrix.android.sdk.api.failure.GlobalError import org.matrix.android.sdk.api.session.Session import timber.log.Timber diff --git a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutActivity2.kt b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutActivity2.kt index 5db82c0631c..3689bff0c77 100644 --- a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutActivity2.kt +++ b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutActivity2.kt @@ -30,7 +30,6 @@ import im.vector.app.core.extensions.replaceFragment import im.vector.app.features.MainActivity import im.vector.app.features.MainActivityArgs import im.vector.app.features.login2.LoginActivity2 - import org.matrix.android.sdk.api.failure.GlobalError import org.matrix.android.sdk.api.session.Session import timber.log.Timber diff --git a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutFragment.kt b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutFragment.kt index 48cf7b12bc6..2aa7f151722 100644 --- a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutFragment.kt +++ b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutFragment.kt @@ -32,7 +32,6 @@ import im.vector.app.features.login.AbstractLoginFragment import im.vector.app.features.login.LoginAction import im.vector.app.features.login.LoginMode import im.vector.app.features.login.LoginViewEvents - import javax.inject.Inject /** diff --git a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewModel.kt b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewModel.kt index f49527bd1d8..552d279fad4 100644 --- a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewModel.kt @@ -103,11 +103,11 @@ class SoftLogoutViewModel @AssistedInject constructor( val loginMode = when { // SSO login is taken first - data.supportedLoginTypes.contains(LoginFlowTypes.SSO) - && data.supportedLoginTypes.contains(LoginFlowTypes.PASSWORD) -> LoginMode.SsoAndPassword(data.ssoIdentityProviders) - data.supportedLoginTypes.contains(LoginFlowTypes.SSO) -> LoginMode.Sso(data.ssoIdentityProviders) - data.supportedLoginTypes.contains(LoginFlowTypes.PASSWORD) -> LoginMode.Password - else -> LoginMode.Unsupported + data.supportedLoginTypes.contains(LoginFlowTypes.SSO) && + data.supportedLoginTypes.contains(LoginFlowTypes.PASSWORD) -> LoginMode.SsoAndPassword(data.ssoIdentityProviders) + data.supportedLoginTypes.contains(LoginFlowTypes.SSO) -> LoginMode.Sso(data.ssoIdentityProviders) + data.supportedLoginTypes.contains(LoginFlowTypes.PASSWORD) -> LoginMode.Password + else -> LoginMode.Unsupported } setState { diff --git a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewState.kt b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewState.kt index ccc186aba73..4beb47c4a4f 100644 --- a/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewState.kt +++ b/vector/src/main/java/im/vector/app/features/signout/soft/SoftLogoutViewState.kt @@ -35,8 +35,8 @@ data class SoftLogoutViewState( ) : MvRxState { fun isLoading(): Boolean { - return asyncLoginAction is Loading + return asyncLoginAction is Loading || // Keep loading when it is success because of the delay to switch to the next Activity - || asyncLoginAction is Success + asyncLoginAction is Success } } diff --git a/vector/src/main/java/im/vector/app/features/spaces/InviteRoomSpaceChooserBottomSheet.kt b/vector/src/main/java/im/vector/app/features/spaces/InviteRoomSpaceChooserBottomSheet.kt index bc2a9c604b3..cf7871bc99a 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/InviteRoomSpaceChooserBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/InviteRoomSpaceChooserBottomSheet.kt @@ -89,8 +89,7 @@ class InviteRoomSpaceChooserBottomSheet : VectorBaseBottomSheetDialogFragment() { + + val settingsViewModel: SpaceMenuViewModel by parentFragmentViewModel() + + override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetLeaveSpaceBinding { + return BottomSheetLeaveSpaceBinding.inflate(inflater, container, false) + } + + @Inject lateinit var colorProvider: ColorProvider + @Inject lateinit var errorFormatter: ErrorFormatter + + override fun injectWith(injector: ScreenComponent) { + injector.inject(this) + } + + @Parcelize + data class Args( + val spaceId: String + ) : Parcelable + + override val showExpanded = true + + private val spaceArgs: SpaceBottomSheetSettingsArgs by args() + + private val cherryPickLeaveActivityResult = registerStartForActivityResult { activityResult -> + if (activityResult.resultCode == Activity.RESULT_OK) { + // nothing actually? + } else { + // move back to default + settingsViewModel.handle(SpaceLeaveViewAction.SetAutoLeaveAll) + } + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + views.autoLeaveRadioGroup.checkedChanges() + .observeOn(AndroidSchedulers.mainThread()) + .subscribe { + when (it) { + views.leaveAll.id -> { + settingsViewModel.handle(SpaceLeaveViewAction.SetAutoLeaveAll) + } + views.leaveNone.id -> { + settingsViewModel.handle(SpaceLeaveViewAction.SetAutoLeaveNone) + } + views.leaveSelected.id -> { + settingsViewModel.handle(SpaceLeaveViewAction.SetAutoLeaveSelected) + // launch dedicated activity + cherryPickLeaveActivityResult.launch( + SpaceLeaveAdvancedActivity.newIntent(requireContext(), spaceArgs.spaceId) + ) + } + } + } + .disposeOnDestroyView() + + views.leaveButton.debouncedClicks { + settingsViewModel.handle(SpaceLeaveViewAction.LeaveSpace) + } + + views.cancelButton.debouncedClicks { + dismiss() + } + } + + override fun invalidate() = withState(settingsViewModel) { state -> + super.invalidate() + + val spaceSummary = state.spaceSummary ?: return@withState + val bestName = spaceSummary.toMatrixItem().getBestName() + val commonText = getString(R.string.space_leave_prompt_msg_with_name, bestName) + .toSpannable().styleMatchingText(bestName, Typeface.BOLD) + + val warningMessage: CharSequence = if (spaceSummary.otherMemberIds.isEmpty()) { + span { + +commonText + +"\n\n" + span(getString(R.string.space_leave_prompt_msg_only_you)) { + textColor = colorProvider.getColorFromAttribute(R.attr.colorError) + } + } + } else if (state.isLastAdmin) { + span { + +commonText + +"\n\n" + span(getString(R.string.space_leave_prompt_msg_as_admin)) { + textColor = colorProvider.getColorFromAttribute(R.attr.colorError) + } + } + } else if (!spaceSummary.isPublic) { + span { + +commonText + +"\n\n" + span(getString(R.string.space_leave_prompt_msg_private)) { + textColor = colorProvider.getColorFromAttribute(R.attr.colorError) + } + } + } else { + commonText + } + + views.bottomLeaveSpaceWarningText.setTextOrHide(warningMessage) + + views.inlineErrorText.setTextOrHide(null) + if (state.leavingState is Loading) { + views.leaveButton.isInvisible = true + views.cancelButton.isInvisible = true + views.leaveProgress.isVisible = true + } else { + views.leaveButton.isInvisible = false + views.cancelButton.isInvisible = false + views.leaveProgress.isVisible = false + if (state.leavingState is Fail) { + views.inlineErrorText.setTextOrHide(errorFormatter.toHumanReadable(state.leavingState.error)) + } + } + + val hasChildren = (spaceSummary.spaceChildren?.size ?: 0) > 0 + if (hasChildren) { + views.autoLeaveRadioGroup.isVisible = true + when (state.leaveMode) { + SpaceMenuState.LeaveMode.LEAVE_ALL -> { + views.autoLeaveRadioGroup.check(views.leaveAll.id) + } + SpaceMenuState.LeaveMode.LEAVE_NONE -> { + views.autoLeaveRadioGroup.check(views.leaveNone.id) + } + SpaceMenuState.LeaveMode.LEAVE_SELECTED -> { + views.autoLeaveRadioGroup.check(views.leaveSelected.id) + } + } + } else { + views.autoLeaveRadioGroup.isVisible = false + } + } + + companion object { + + fun newInstance(spaceId: String): LeaveSpaceBottomSheet { + return LeaveSpaceBottomSheet().apply { + setArguments(SpaceBottomSheetSettingsArgs(spaceId)) + } + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceCreationActivity.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceCreationActivity.kt index a02755a1551..344559bc816 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpaceCreationActivity.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceCreationActivity.kt @@ -31,6 +31,7 @@ import im.vector.app.core.platform.SimpleFragmentActivity import im.vector.app.features.spaces.create.ChoosePrivateSpaceTypeFragment import im.vector.app.features.spaces.create.ChooseSpaceTypeFragment import im.vector.app.features.spaces.create.CreateSpaceAction +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.create.CreateSpaceEvents @@ -55,18 +56,21 @@ class SpaceCreationActivity : SimpleFragmentActivity(), CreateSpaceViewModel.Fac super.onCreate(savedInstanceState) if (isFirstCreation()) { when (withState(viewModel) { it.step }) { - CreateSpaceState.Step.ChooseType -> { + CreateSpaceState.Step.ChooseType -> { navigateToFragment(ChooseSpaceTypeFragment::class.java) } - CreateSpaceState.Step.SetDetails -> { + CreateSpaceState.Step.SetDetails -> { navigateToFragment(ChooseSpaceTypeFragment::class.java) } - CreateSpaceState.Step.AddRooms -> { + CreateSpaceState.Step.AddRooms -> { navigateToFragment(CreateSpaceDefaultRoomsFragment::class.java) } - CreateSpaceState.Step.ChoosePrivateType -> { + CreateSpaceState.Step.ChoosePrivateType -> { navigateToFragment(ChoosePrivateSpaceTypeFragment::class.java) } + CreateSpaceState.Step.AddEmailsOrInvites -> { + navigateToFragment(CreateSpaceAdd3pidInvitesFragment::class.java) + } } } } @@ -92,6 +96,9 @@ class SpaceCreationActivity : SimpleFragmentActivity(), CreateSpaceViewModel.Fac CreateSpaceEvents.NavigateToAddRooms -> { navigateToFragment(CreateSpaceDefaultRoomsFragment::class.java) } + CreateSpaceEvents.NavigateToAdd3Pid -> { + navigateToFragment(CreateSpaceAdd3pidInvitesFragment::class.java) + } CreateSpaceEvents.NavigateToChoosePrivateType -> { navigateToFragment(ChoosePrivateSpaceTypeFragment::class.java) } @@ -143,6 +150,7 @@ class SpaceCreationActivity : SimpleFragmentActivity(), CreateSpaceViewModel.Fac if (state.spaceType == SpaceType.Public) R.string.your_public_space else R.string.your_private_space } + CreateSpaceState.Step.AddEmailsOrInvites, CreateSpaceState.Step.ChoosePrivateType -> R.string.your_private_space } supportActionBar?.let { diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceExploreActivity.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceExploreActivity.kt index dbe92d4d93c..cee945e202e 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpaceExploreActivity.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceExploreActivity.kt @@ -29,6 +29,7 @@ import im.vector.app.core.extensions.commitTransaction import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.databinding.ActivitySimpleBinding import im.vector.app.features.matrixto.MatrixToBottomSheet +import im.vector.app.features.navigation.Navigator import im.vector.app.features.spaces.explore.SpaceDirectoryArgs import im.vector.app.features.spaces.explore.SpaceDirectoryFragment import im.vector.app.features.spaces.explore.SpaceDirectoryState @@ -51,18 +52,18 @@ class SpaceExploreActivity : VectorBaseActivity(), SpaceD val sharedViewModel: SpaceDirectoryViewModel by viewModel() private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() { - override fun onFragmentAttached(fm: FragmentManager, f: Fragment, context: Context) { + override fun onFragmentResumed(fm: FragmentManager, f: Fragment) { if (f is MatrixToBottomSheet) { f.interactionListener = this@SpaceExploreActivity } - super.onFragmentAttached(fm, f, context) + super.onFragmentResumed(fm, f) } - override fun onFragmentDetached(fm: FragmentManager, f: Fragment) { + override fun onFragmentPaused(fm: FragmentManager, f: Fragment) { if (f is MatrixToBottomSheet) { f.interactionListener = null } - super.onFragmentDetached(fm, f) + super.onFragmentPaused(fm, f) } } @@ -86,14 +87,14 @@ class SpaceExploreActivity : VectorBaseActivity(), SpaceD sharedViewModel.observeViewEvents { when (it) { - SpaceDirectoryViewEvents.Dismiss -> { + SpaceDirectoryViewEvents.Dismiss -> { finish() } - is SpaceDirectoryViewEvents.NavigateToRoom -> { + is SpaceDirectoryViewEvents.NavigateToRoom -> { navigator.openRoom(this, it.roomId) } is SpaceDirectoryViewEvents.NavigateToMxToBottomSheet -> { - MatrixToBottomSheet.withLink(it.link, this).show(supportFragmentManager, "ShowChild") + MatrixToBottomSheet.withLink(it.link).show(supportFragmentManager, "ShowChild") } } } @@ -115,7 +116,11 @@ class SpaceExploreActivity : VectorBaseActivity(), SpaceD override fun create(initialState: SpaceDirectoryState): SpaceDirectoryViewModel = spaceDirectoryViewModelFactory.create(initialState) - override fun navigateToRoom(roomId: String) { + override fun mxToBottomSheetNavigateToRoom(roomId: String) { navigator.openRoom(this, roomId) } + + override fun mxToBottomSheetSwitchToSpace(spaceId: String) { + navigator.switchToSpace(this, spaceId, Navigator.PostSwitchSpaceAction.None) + } } diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceLeaveViewAction.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceLeaveViewAction.kt new file mode 100644 index 00000000000..0d17dddca90 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceLeaveViewAction.kt @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.spaces + +import im.vector.app.core.platform.VectorViewModelAction + +sealed class SpaceLeaveViewAction : VectorViewModelAction { + object SetAutoLeaveAll : SpaceLeaveViewAction() + object SetAutoLeaveNone : SpaceLeaveViewAction() + object SetAutoLeaveSelected : SpaceLeaveViewAction() + object LeaveSpace : SpaceLeaveViewAction() +} diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceListAction.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceListAction.kt index 12d4b40f427..5bff2b733f2 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpaceListAction.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceListAction.kt @@ -26,7 +26,7 @@ sealed class SpaceListAction : VectorViewModelAction { data class LeaveSpace(val spaceSummary: RoomSummary) : SpaceListAction() data class ToggleExpand(val spaceSummary: RoomSummary) : SpaceListAction() object AddSpace : SpaceListAction() - data class MoveSpace(val spaceId: String, val delta : Int) : SpaceListAction() + data class MoveSpace(val spaceId: String, val delta: Int) : SpaceListAction() data class OnStartDragging(val spaceId: String, val expanded: Boolean) : SpaceListAction() data class OnEndDragging(val spaceId: String, val expanded: Boolean) : SpaceListAction() diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceListViewState.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceListViewState.kt index 7482f4881e9..795713152a8 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpaceListViewState.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceListViewState.kt @@ -26,7 +26,7 @@ import org.matrix.android.sdk.api.session.room.summary.RoomAggregateNotification import org.matrix.android.sdk.api.util.MatrixItem data class SpaceListViewState( - val myMxItem : Async = Uninitialized, + val myMxItem: Async = Uninitialized, val asyncSpaces: Async> = Uninitialized, val selectedGroupingMethod: RoomGroupingMethod = RoomGroupingMethod.BySpace(null), val rootSpacesOrdered: List? = null, @@ -34,5 +34,5 @@ data class SpaceListViewState( val spaceOrderLocalEchos: Map? = null, val legacyGroups: List? = null, val expandedStates: Map = emptyMap(), - val homeAggregateCount : RoomAggregateNotificationCount = RoomAggregateNotificationCount(0, 0) + val homeAggregateCount: RoomAggregateNotificationCount = RoomAggregateNotificationCount(0, 0) ) : MvRxState diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuState.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuState.kt new file mode 100644 index 00000000000..395fcc9df1a --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuState.kt @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.spaces + +import com.airbnb.mvrx.Async +import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.Uninitialized +import org.matrix.android.sdk.api.session.room.model.RoomSummary + +data class SpaceMenuState( + val spaceId: String, + val spaceSummary: RoomSummary? = null, + val canEditSettings: Boolean = false, + val canInvite: Boolean = false, + val canAddChild: Boolean = false, + val isLastAdmin: Boolean = false, + val leaveMode: LeaveMode = LeaveMode.LEAVE_NONE, + val leavingState: Async = Uninitialized +) : MvRxState { + constructor(args: SpaceBottomSheetSettingsArgs) : this(spaceId = args.spaceId) + + enum class LeaveMode { + LEAVE_ALL, LEAVE_NONE, LEAVE_SELECTED + } +} diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt new file mode 100644 index 00000000000..24ca2189425 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceMenuViewModel.kt @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.spaces + +import com.airbnb.mvrx.ActivityViewModelContext +import com.airbnb.mvrx.Fail +import com.airbnb.mvrx.FragmentViewModelContext +import com.airbnb.mvrx.Loading +import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.Success +import com.airbnb.mvrx.Uninitialized +import com.airbnb.mvrx.ViewModelContext +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import im.vector.app.AppStateHandler +import im.vector.app.core.platform.EmptyViewEvents +import im.vector.app.core.platform.VectorViewModel +import im.vector.app.features.powerlevel.PowerLevelsObservableFactory +import im.vector.app.features.session.coroutineScope +import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.query.ActiveSpaceFilter +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.room.model.Membership +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.rx.rx +import timber.log.Timber + +class SpaceMenuViewModel @AssistedInject constructor( + @Assisted val initialState: SpaceMenuState, + val session: Session, + val appStateHandler: AppStateHandler +) : VectorViewModel(initialState) { + + @AssistedFactory + interface Factory { + fun create(initialState: SpaceMenuState): SpaceMenuViewModel + } + + companion object : MvRxViewModelFactory { + + @JvmStatic + override fun create(viewModelContext: ViewModelContext, state: SpaceMenuState): SpaceMenuViewModel? { + val factory = when (viewModelContext) { + is FragmentViewModelContext -> viewModelContext.fragment as? Factory + is ActivityViewModelContext -> viewModelContext.activity as? Factory + } + return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface") + } + } + + init { + val roomSummary = session.getRoomSummary(initialState.spaceId) + + setState { + copy(spaceSummary = roomSummary) + } + + session.getRoom(initialState.spaceId)?.let { room -> + + room.rx().liveRoomSummary().subscribe { + it.getOrNull()?.let { + if (it.membership == Membership.LEAVE) { + setState { copy(leavingState = Success(Unit)) } + if (appStateHandler.safeActiveSpaceId() == initialState.spaceId) { + // switch to home? + appStateHandler.setCurrentSpace(null, session) + } + } + } + }.disposeOnClear() + + PowerLevelsObservableFactory(room) + .createObservable() + .subscribe { + val powerLevelsHelper = PowerLevelsHelper(it) + + val canInvite = powerLevelsHelper.isUserAbleToInvite(session.myUserId) + val canAddChild = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_SPACE_CHILD) + + val canChangeAvatar = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_AVATAR) + val canChangeName = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_NAME) + val canChangeTopic = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_TOPIC) + + val isAdmin = powerLevelsHelper.getUserRole(session.myUserId) is Role.Admin + val otherAdminCount = roomSummary?.otherMemberIds + ?.map { powerLevelsHelper.getUserRole(it) } + ?.count { it is Role.Admin } + ?: 0 + val isLastAdmin = isAdmin && otherAdminCount == 0 + + setState { + copy( + canEditSettings = canChangeAvatar || canChangeName || canChangeTopic, + canInvite = canInvite, + canAddChild = canAddChild, + isLastAdmin = isLastAdmin + ) + } + } + .disposeOnClear() + } + } + + override fun handle(action: SpaceLeaveViewAction) { + when (action) { + SpaceLeaveViewAction.SetAutoLeaveAll -> setState { + copy(leaveMode = SpaceMenuState.LeaveMode.LEAVE_ALL, leavingState = Uninitialized) + } + SpaceLeaveViewAction.SetAutoLeaveNone -> setState { + copy(leaveMode = SpaceMenuState.LeaveMode.LEAVE_NONE, leavingState = Uninitialized) + } + SpaceLeaveViewAction.SetAutoLeaveSelected -> setState { + copy(leaveMode = SpaceMenuState.LeaveMode.LEAVE_SELECTED, leavingState = Uninitialized) + } + SpaceLeaveViewAction.LeaveSpace -> handleLeaveSpace() + } + } + + private fun handleLeaveSpace() = withState { state -> + + setState { copy(leavingState = Loading()) } + + session.coroutineScope.launch { + try { + if (state.leaveMode == SpaceMenuState.LeaveMode.LEAVE_NONE) { + session.getRoom(initialState.spaceId)?.leave(null) + } else if (state.leaveMode == SpaceMenuState.LeaveMode.LEAVE_ALL) { + // need to find all child rooms that i have joined + + session.getRoomSummaries( + roomSummaryQueryParams { + excludeType = null + activeSpaceFilter = ActiveSpaceFilter.ActiveSpace(initialState.spaceId) + memberships = listOf(Membership.JOIN) + } + ).forEach { + try { + session.getRoom(it.roomId)?.leave(null) + } catch (failure: Throwable) { + // silently ignore? + Timber.e(failure, "Fail to leave sub rooms/spaces") + } + } + session.getRoom(initialState.spaceId)?.leave(null) + } + + // We observe the membership and to dismiss when we have remote echo of leaving + } catch (failure: Throwable) { + setState { copy(leavingState = Fail(failure)) } + } + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/spaces/SpaceSettingsMenuBottomSheet.kt b/vector/src/main/java/im/vector/app/features/spaces/SpaceSettingsMenuBottomSheet.kt index 37c5088123b..040f1f90577 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/SpaceSettingsMenuBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/SpaceSettingsMenuBottomSheet.kt @@ -22,35 +22,23 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.core.view.isVisible +import com.airbnb.mvrx.Success import com.airbnb.mvrx.args -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import im.vector.app.R -import im.vector.app.core.di.ActiveSessionHolder +import com.airbnb.mvrx.fragmentViewModel +import com.airbnb.mvrx.withState import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.setTextOrHide import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment -import im.vector.app.core.resources.ColorProvider import im.vector.app.databinding.BottomSheetSpaceSettingsBinding import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.navigation.Navigator -import im.vector.app.features.powerlevel.PowerLevelsObservableFactory import im.vector.app.features.rageshake.BugReporter -import im.vector.app.features.rageshake.ReportType import im.vector.app.features.roomprofile.RoomProfileActivity -import im.vector.app.features.session.coroutineScope -import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.spaces.manage.ManageType import im.vector.app.features.spaces.manage.SpaceManageActivity -import io.reactivex.android.schedulers.AndroidSchedulers -import kotlinx.coroutines.launch import kotlinx.parcelize.Parcelize -import me.gujun.android.span.span import org.matrix.android.sdk.api.extensions.orFalse -import org.matrix.android.sdk.api.session.events.model.EventType -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.util.toMatrixItem -import timber.log.Timber import javax.inject.Inject @Parcelize @@ -58,15 +46,12 @@ data class SpaceBottomSheetSettingsArgs( val spaceId: String ) : Parcelable -// XXX make proper view model before leaving beta -class SpaceSettingsMenuBottomSheet : VectorBaseBottomSheetDialogFragment() { +class SpaceSettingsMenuBottomSheet : VectorBaseBottomSheetDialogFragment(), SpaceMenuViewModel.Factory { @Inject lateinit var navigator: Navigator - @Inject lateinit var activeSessionHolder: ActiveSessionHolder @Inject lateinit var avatarRenderer: AvatarRenderer - @Inject lateinit var vectorPreferences: VectorPreferences @Inject lateinit var bugReporter: BugReporter - @Inject lateinit var colorProvider: ColorProvider + @Inject lateinit var viewModelFactory: SpaceMenuViewModel.Factory private val spaceArgs: SpaceBottomSheetSettingsArgs by args() @@ -74,6 +59,8 @@ class SpaceSettingsMenuBottomSheet : VectorBaseBottomSheetDialogFragment - val powerLevelsHelper = PowerLevelsHelper(powerLevelContent) - val canInvite = powerLevelsHelper.isUserAbleToInvite(session.myUserId) - val canAddChild = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_SPACE_CHILD) - - val canChangeAvatar = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_AVATAR) - val canChangeName = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_NAME) - val canChangeTopic = powerLevelsHelper.isUserAllowedToSend(session.myUserId, true, EventType.STATE_ROOM_TOPIC) - - views.spaceSettings.isVisible = canChangeAvatar || canChangeName || canChangeTopic - - views.invitePeople.isVisible = canInvite || roomSummary?.isPublic.orFalse() - views.addRooms.isVisible = canAddChild - - val isAdmin = powerLevelsHelper.getUserRole(session.myUserId) is Role.Admin - val otherAdminCount = roomSummary?.otherMemberIds - ?.map { powerLevelsHelper.getUserRole(it) } - ?.count { it is Role.Admin } - ?: 0 - isLastAdmin = isAdmin && otherAdminCount == 0 - }.disposeOnDestroyView() - - views.spaceBetaTag.debouncedClicks { - bugReporter.openBugReportScreen(requireActivity(), ReportType.SPACE_BETA_FEEDBACK) - } - views.invitePeople.views.bottomSheetActionClickableZone.debouncedClicks { dismiss() interactionListener?.onShareSpaceSelected(spaceArgs.spaceId) @@ -153,41 +101,34 @@ class SpaceSettingsMenuBottomSheet : VectorBaseBottomSheetDialogFragment + super.invalidate() + + if (state.leavingState is Success) { + dismiss() + } - MaterialAlertDialogBuilder(requireContext(), R.style.ThemeOverlay_Vector_MaterialAlertDialog_Destructive) - .setMessage(warningMessage) - .setTitle(getString(R.string.space_leave_prompt_msg)) - .setPositiveButton(R.string.leave) { _, _ -> - session.coroutineScope.launch { - try { - session.getRoom(spaceArgs.spaceId)?.leave(null) - } catch (failure: Throwable) { - Timber.e(failure, "Failed to leave space") - } - } - dismiss() - } - .setNegativeButton(R.string.cancel, null) - .show() + state.spaceSummary?.toMatrixItem()?.let { + avatarRenderer.render(it, views.spaceAvatarImageView) } + views.spaceNameView.text = state.spaceSummary?.displayName + views.spaceDescription.setTextOrHide(state.spaceSummary?.topic?.takeIf { it.isNotEmpty() }) + + views.spaceSettings.isVisible = state.canEditSettings + + views.invitePeople.isVisible = state.canInvite || state.spaceSummary?.isPublic.orFalse() + views.addRooms.isVisible = state.canAddChild + views.addSpaces.isVisible = state.canAddChild } companion object { @@ -198,4 +139,8 @@ class SpaceSettingsMenuBottomSheet : VectorBaseBottomSheetDialogFragment() { - - override fun getBinding(inflater: LayoutInflater, container: ViewGroup?) = - BottomSheetSpaceCreatePrivateWarningBinding.inflate(inflater, container, false) - - override val showExpanded = true - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - views.continueButton.debouncedClicks { - setFragmentResult(REQUEST_KEY, Bundle.EMPTY) - dismiss() - } - } - - companion object { - const val REQUEST_KEY = "BetaWarningBottomSheet" - } -} diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/ChoosePrivateSpaceTypeFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/create/ChoosePrivateSpaceTypeFragment.kt index 4031b56c1d8..4f079551eb9 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/ChoosePrivateSpaceTypeFragment.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/ChoosePrivateSpaceTypeFragment.kt @@ -20,7 +20,6 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.fragment.app.setFragmentResultListener import com.airbnb.mvrx.activityViewModel import im.vector.app.R import im.vector.app.core.epoxy.onClick @@ -39,13 +38,6 @@ class ChoosePrivateSpaceTypeFragment @Inject constructor( override fun getBinding(inflater: LayoutInflater, container: ViewGroup?) = FragmentSpaceCreateChoosePrivateModelBinding.inflate(layoutInflater, container, false) - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setFragmentResultListener(BetaWarningBottomSheet.REQUEST_KEY) { _, _ -> - sharedViewModel.handle(CreateSpaceAction.SetSpaceTopology(SpaceTopology.MeAndTeammates)) - } - } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -54,7 +46,7 @@ class ChoosePrivateSpaceTypeFragment @Inject constructor( } views.teammatesButton.onClick { - BetaWarningBottomSheet().show(parentFragmentManager, "warning") + sharedViewModel.handle(CreateSpaceAction.SetSpaceTopology(SpaceTopology.MeAndTeammates)) } sharedViewModel.subscribe { state -> diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceAction.kt b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceAction.kt index 1f0ed6428fb..e2eaa5784fd 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceAction.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceAction.kt @@ -28,6 +28,8 @@ sealed class CreateSpaceAction : VectorViewModelAction { object OnBackPressed : CreateSpaceAction() object NextFromDetails : CreateSpaceAction() object NextFromDefaultRooms : CreateSpaceAction() + object NextFromAdd3pid : CreateSpaceAction() data class DefaultRoomNameChanged(val index: Int, val name: String) : CreateSpaceAction() + data class DefaultInvite3pidChanged(val index: Int, val email: String) : CreateSpaceAction() data class SetSpaceTopology(val topology: SpaceTopology) : CreateSpaceAction() } diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceAdd3pidInvitesFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceAdd3pidInvitesFragment.kt new file mode 100644 index 00000000000..6dc3ad8c213 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceAdd3pidInvitesFragment.kt @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.spaces.create + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.airbnb.mvrx.activityViewModel +import im.vector.app.R +import im.vector.app.core.extensions.cleanup +import im.vector.app.core.extensions.configureWith +import im.vector.app.core.extensions.hideKeyboard +import im.vector.app.core.platform.OnBackPressed +import im.vector.app.core.platform.VectorBaseFragment +import im.vector.app.databinding.FragmentSpaceCreateGenericEpoxyFormBinding +import im.vector.app.features.settings.VectorSettingsActivity +import javax.inject.Inject + +class CreateSpaceAdd3pidInvitesFragment @Inject constructor( + private val epoxyController: SpaceAdd3pidEpoxyController +) : VectorBaseFragment(), + SpaceAdd3pidEpoxyController.Listener, + OnBackPressed { + + private val sharedViewModel: CreateSpaceViewModel by activityViewModel() + + override fun onBackPressed(toolbarButton: Boolean): Boolean { + sharedViewModel.handle(CreateSpaceAction.OnBackPressed) + return true + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + views.recyclerView.configureWith(epoxyController) + epoxyController.listener = this + + sharedViewModel.subscribe(this) { + invalidateState(it) + } + + views.nextButton.setText(R.string.next_pf) + views.nextButton.debouncedClicks { + view.hideKeyboard() + sharedViewModel.handle(CreateSpaceAction.NextFromAdd3pid) + } + } + + private fun invalidateState(it: CreateSpaceState) { + epoxyController.setData(it) + val noEmails = it.default3pidInvite?.all { it.value.isNullOrBlank() } ?: true + views.nextButton.text = if (noEmails) { + getString(R.string.skip_for_now) + } else { + getString(R.string.next_pf) + } + } + + override fun onDestroyView() { + views.recyclerView.cleanup() + epoxyController.listener = null + super.onDestroyView() + } + + override fun getBinding(inflater: LayoutInflater, container: ViewGroup?) = + FragmentSpaceCreateGenericEpoxyFormBinding.inflate(layoutInflater, container, false) + + override fun on3pidChange(index: Int, newName: String) { + sharedViewModel.handle(CreateSpaceAction.DefaultInvite3pidChanged(index, newName)) + } + + override fun onNoIdentityServer() { + navigator.openSettings( + requireContext(), + VectorSettingsActivity.EXTRA_DIRECT_ACCESS_DISCOVERY_SETTINGS + ) + } +} diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceEvents.kt b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceEvents.kt index 073531353fa..eeb2ca30fff 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceEvents.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceEvents.kt @@ -22,6 +22,7 @@ sealed class CreateSpaceEvents : VectorViewEvents { object NavigateToDetails : CreateSpaceEvents() object NavigateToChooseType : CreateSpaceEvents() object NavigateToAddRooms : CreateSpaceEvents() + object NavigateToAdd3Pid : CreateSpaceEvents() object NavigateToChoosePrivateType : CreateSpaceEvents() object Dismiss : CreateSpaceEvents() data class FinishSuccess(val spaceId: String, val defaultRoomId: String?, val topology: SpaceTopology?) : CreateSpaceEvents() diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceState.kt b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceState.kt index 39a69e837b6..ab7fb0cc251 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceState.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceState.kt @@ -28,19 +28,23 @@ data class CreateSpaceState( val step: Step = Step.ChooseType, val spaceType: SpaceType? = null, val spaceTopology: SpaceTopology? = null, - val homeServerName: String? = null, + val homeServerName: String = "", val aliasLocalPart: String? = null, val aliasManuallyModified: Boolean = false, val aliasVerificationTask: Async = Uninitialized, val nameInlineError: String? = null, val defaultRooms: Map? = null, - val creationResult: Async = Uninitialized + val default3pidInvite: Map? = null, + val emailValidationResult: Map? = null, + val creationResult: Async = Uninitialized, + val canInviteByMail: Boolean = false ) : MvRxState { enum class Step { ChooseType, SetDetails, AddRooms, - ChoosePrivateType + ChoosePrivateType, + AddEmailsOrInvites } } diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModel.kt index 2537a3a5926..4495ee31a10 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModel.kt @@ -31,6 +31,7 @@ import dagger.assisted.AssistedInject import im.vector.app.R import im.vector.app.core.error.ErrorFormatter import im.vector.app.core.extensions.exhaustive +import im.vector.app.core.extensions.isEmail import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider import kotlinx.coroutines.Dispatchers @@ -38,6 +39,7 @@ import kotlinx.coroutines.launch import org.matrix.android.sdk.api.MatrixPatterns import org.matrix.android.sdk.api.MatrixPatterns.getDomain import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.identity.IdentityServiceListener import org.matrix.android.sdk.api.session.room.AliasAvailabilityResult import org.matrix.android.sdk.api.session.room.failure.CreateRoomFailure @@ -49,12 +51,28 @@ class CreateSpaceViewModel @AssistedInject constructor( private val errorFormatter: ErrorFormatter ) : VectorViewModel(initialState) { + private val identityService = session.identityService() + + private val identityServerManagerListener = object : IdentityServiceListener { + override fun onIdentityServerChange() { + val identityServerUrl = identityService.getCurrentIdentityServerUrl() + setState { + copy( + canInviteByMail = identityServerUrl != null + ) + } + } + } + init { + val identityServerUrl = identityService.getCurrentIdentityServerUrl() setState { copy( - homeServerName = session.myUserId.getDomain() + homeServerName = session.myUserId.getDomain(), + canInviteByMail = identityServerUrl != null ) } + startListenToIdentityManager() } @AssistedFactory @@ -62,6 +80,19 @@ class CreateSpaceViewModel @AssistedInject constructor( fun create(initialState: CreateSpaceState): CreateSpaceViewModel } + private fun startListenToIdentityManager() { + identityService.addListener(identityServerManagerListener) + } + + private fun stopListenToIdentityManager() { + identityService.removeListener(identityServerManagerListener) + } + + override fun onCleared() { + stopListenToIdentityManager() + super.onCleared() + } + companion object : MvRxViewModelFactory { override fun create(viewModelContext: ViewModelContext, state: CreateSpaceState): CreateSpaceViewModel? { @@ -84,7 +115,7 @@ class CreateSpaceViewModel @AssistedInject constructor( override fun handle(action: CreateSpaceAction) { when (action) { - is CreateSpaceAction.SetRoomType -> { + is CreateSpaceAction.SetRoomType -> { setState { copy( step = CreateSpaceState.Step.SetDetails, @@ -93,7 +124,7 @@ class CreateSpaceViewModel @AssistedInject constructor( } _viewEvents.post(CreateSpaceEvents.NavigateToDetails) } - is CreateSpaceAction.NameChanged -> { + is CreateSpaceAction.NameChanged -> { setState { if (aliasManuallyModified) { copy( @@ -103,7 +134,7 @@ class CreateSpaceViewModel @AssistedInject constructor( ) } else { val tentativeAlias = - MatrixPatterns.candidateAliasFromRoomName(action.name) + MatrixPatterns.candidateAliasFromRoomName(action.name, homeServerName) copy( nameInlineError = null, name = action.name, @@ -113,14 +144,14 @@ class CreateSpaceViewModel @AssistedInject constructor( } } } - is CreateSpaceAction.TopicChanged -> { + is CreateSpaceAction.TopicChanged -> { setState { copy( topic = action.topic ) } } - is CreateSpaceAction.SpaceAliasChanged -> { + is CreateSpaceAction.SpaceAliasChanged -> { // This called only when the alias is change manually // not when programmatically changed via a change on name setState { @@ -131,28 +162,43 @@ class CreateSpaceViewModel @AssistedInject constructor( ) } } - CreateSpaceAction.OnBackPressed -> { + CreateSpaceAction.OnBackPressed -> { handleBackNavigation() } - CreateSpaceAction.NextFromDetails -> { + CreateSpaceAction.NextFromDetails -> { handleNextFromDetails() } - CreateSpaceAction.NextFromDefaultRooms -> { + CreateSpaceAction.NextFromDefaultRooms -> { handleNextFromDefaultRooms() } - is CreateSpaceAction.DefaultRoomNameChanged -> { + CreateSpaceAction.NextFromAdd3pid -> { + handleNextFrom3pid() + } + is CreateSpaceAction.DefaultRoomNameChanged -> { setState { copy( - defaultRooms = (defaultRooms ?: emptyMap()).toMutableMap().apply { + defaultRooms = defaultRooms.orEmpty().toMutableMap().apply { this[action.index] = action.name } ) } } - is CreateSpaceAction.SetAvatar -> { + is CreateSpaceAction.DefaultInvite3pidChanged -> { + setState { + copy( + default3pidInvite = default3pidInvite.orEmpty().toMutableMap().apply { + this[action.index] = action.email + }, + emailValidationResult = emailValidationResult.orEmpty().toMutableMap().apply { + this.remove(action.index) + } + ) + } + } + is CreateSpaceAction.SetAvatar -> { setState { copy(avatarUri = action.uri) } } - is CreateSpaceAction.SetSpaceTopology -> { + is CreateSpaceAction.SetSpaceTopology -> { handleSetTopology(action) } }.exhaustive @@ -173,20 +219,20 @@ class CreateSpaceViewModel @AssistedInject constructor( setState { copy( spaceTopology = SpaceTopology.MeAndTeammates, - step = CreateSpaceState.Step.AddRooms + step = CreateSpaceState.Step.AddEmailsOrInvites ) } - _viewEvents.post(CreateSpaceEvents.NavigateToAddRooms) + _viewEvents.post(CreateSpaceEvents.NavigateToAdd3Pid) } } } private fun handleBackNavigation() = withState { state -> when (state.step) { - CreateSpaceState.Step.ChooseType -> { + CreateSpaceState.Step.ChooseType -> { _viewEvents.post(CreateSpaceEvents.Dismiss) } - CreateSpaceState.Step.SetDetails -> { + CreateSpaceState.Step.SetDetails -> { setState { copy( step = CreateSpaceState.Step.ChooseType, @@ -196,15 +242,15 @@ class CreateSpaceViewModel @AssistedInject constructor( } _viewEvents.post(CreateSpaceEvents.NavigateToChooseType) } - CreateSpaceState.Step.AddRooms -> { + CreateSpaceState.Step.AddRooms -> { if (state.spaceType == SpaceType.Private && state.spaceTopology == SpaceTopology.MeAndTeammates) { setState { copy( spaceTopology = null, - step = CreateSpaceState.Step.ChoosePrivateType + step = CreateSpaceState.Step.AddEmailsOrInvites ) } - _viewEvents.post(CreateSpaceEvents.NavigateToChoosePrivateType) + _viewEvents.post(CreateSpaceEvents.NavigateToAdd3Pid) } else { setState { copy( @@ -214,7 +260,7 @@ class CreateSpaceViewModel @AssistedInject constructor( _viewEvents.post(CreateSpaceEvents.NavigateToDetails) } } - CreateSpaceState.Step.ChoosePrivateType -> { + CreateSpaceState.Step.ChoosePrivateType -> { setState { copy( step = CreateSpaceState.Step.SetDetails @@ -222,6 +268,36 @@ class CreateSpaceViewModel @AssistedInject constructor( } _viewEvents.post(CreateSpaceEvents.NavigateToDetails) } + CreateSpaceState.Step.AddEmailsOrInvites -> { + setState { + copy( + step = CreateSpaceState.Step.ChoosePrivateType + ) + } + _viewEvents.post(CreateSpaceEvents.NavigateToChoosePrivateType) + } + } + } + + private fun handleNextFrom3pid() = withState { state -> + // check if emails are valid + val emailValidation = state.default3pidInvite?.mapValues { + val email = it.value + email.isNullOrEmpty() || email.isEmail() + } + if (emailValidation?.all { it.value } != false) { + setState { + copy( + step = CreateSpaceState.Step.AddRooms + ) + } + _viewEvents.post(CreateSpaceEvents.NavigateToAddRooms) + } else { + setState { + copy( + emailValidationResult = emailValidation + ) + } } } @@ -296,12 +372,18 @@ class CreateSpaceViewModel @AssistedInject constructor( defaultRooms = state.defaultRooms ?.entries ?.sortedBy { it.key } - ?.mapNotNull { it.value } ?: emptyList(), - spaceAlias = alias + ?.mapNotNull { it.value } + .orEmpty(), + spaceAlias = alias, + defaultEmailToInvite = state.default3pidInvite + ?.values + ?.mapNotNull { it.takeIf { it?.isEmail() == true } } + ?.takeIf { state.spaceTopology == SpaceTopology.MeAndTeammates } + .orEmpty() ) ) when (result) { - is CreateSpaceTaskResult.Success -> { + is CreateSpaceTaskResult.Success -> { setState { copy(creationResult = Success(result.spaceId)) } diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModelTask.kt b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModelTask.kt index c68b8a0b9ba..00b4b64296f 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModelTask.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/CreateSpaceViewModelTask.kt @@ -25,12 +25,18 @@ import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.raw.RawService import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities +import org.matrix.android.sdk.api.session.identity.ThreePid import org.matrix.android.sdk.api.session.room.failure.CreateRoomFailure +import org.matrix.android.sdk.api.session.room.model.GuestAccess +import org.matrix.android.sdk.api.session.room.model.PowerLevelsContent import org.matrix.android.sdk.api.session.room.model.RoomDirectoryVisibility +import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility import org.matrix.android.sdk.api.session.room.model.RoomJoinRulesAllowEntry import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams import org.matrix.android.sdk.api.session.room.model.create.CreateRoomPreset import org.matrix.android.sdk.api.session.room.model.create.RestrictedRoomPreset +import org.matrix.android.sdk.api.session.room.powerlevels.Role +import org.matrix.android.sdk.api.session.space.CreateSpaceParams import timber.log.Timber import javax.inject.Inject @@ -49,7 +55,8 @@ data class CreateSpaceTaskParams( val spaceAvatar: Uri? = null, val spaceAlias: String? = null, val isPublic: Boolean, - val defaultRooms: List = emptyList() + val defaultRooms: List = emptyList(), + val defaultEmailToInvite: List = emptyList() ) class CreateSpaceViewModelTask @Inject constructor( @@ -60,13 +67,31 @@ class CreateSpaceViewModelTask @Inject constructor( override suspend fun execute(params: CreateSpaceTaskParams): CreateSpaceTaskResult { val spaceID = try { - session.spaceService().createSpace( - params.spaceName, - params.spaceTopic, - params.spaceAvatar, - params.isPublic, - params.spaceAlias - ) + session.spaceService().createSpace(CreateSpaceParams().apply { + this.name = params.spaceName + this.topic = params.spaceTopic + this.avatarUri = params.spaceAvatar + if (params.isPublic) { + this.roomAliasName = params.spaceAlias + this.powerLevelContentOverride = (powerLevelContentOverride ?: PowerLevelsContent()).copy( + invite = Role.Default.value + ) + this.preset = CreateRoomPreset.PRESET_PUBLIC_CHAT + this.historyVisibility = RoomHistoryVisibility.WORLD_READABLE + this.guestAccess = GuestAccess.CanJoin + } else { + this.preset = CreateRoomPreset.PRESET_PRIVATE_CHAT + visibility = RoomDirectoryVisibility.PRIVATE + this.invite3pids.addAll( + params.defaultEmailToInvite.map { + ThreePid.Email(it) + } + ) + this.powerLevelContentOverride = (powerLevelContentOverride ?: PowerLevelsContent()).copy( + invite = Role.Moderator.value + ) + } + }) } catch (failure: Throwable) { return CreateSpaceTaskResult.FailedToCreateSpace(failure) } diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/SpaceAdd3pidEpoxyController.kt b/vector/src/main/java/im/vector/app/features/spaces/create/SpaceAdd3pidEpoxyController.kt new file mode 100644 index 00000000000..05d8a78b300 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/spaces/create/SpaceAdd3pidEpoxyController.kt @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.spaces.create + +import android.text.InputType +import com.airbnb.epoxy.TypedEpoxyController +import com.google.android.material.textfield.TextInputLayout +import im.vector.app.R +import im.vector.app.core.resources.ColorProvider +import im.vector.app.core.resources.StringProvider +import im.vector.app.core.ui.list.ItemStyle +import im.vector.app.core.ui.list.genericButtonItem +import im.vector.app.core.ui.list.genericFooterItem +import im.vector.app.core.ui.list.genericPillItem +import im.vector.app.features.form.formEditTextItem +import javax.inject.Inject + +class SpaceAdd3pidEpoxyController @Inject constructor( + private val stringProvider: StringProvider, + private val colorProvider: ColorProvider +) : TypedEpoxyController() { + + var listener: Listener? = null + + override fun buildModels(data: CreateSpaceState?) { + val host = this + data ?: return + genericFooterItem { + id("info_help_header") + style(ItemStyle.TITLE) + text(host.stringProvider.getString(R.string.create_spaces_invite_public_header)) + textColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_primary)) + } + genericFooterItem { + id("info_help_desc") + text(host.stringProvider.getString(R.string.create_spaces_invite_public_header_desc, data.name ?: "")) + textColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_secondary)) + } + + if (data.canInviteByMail) { + buildEmailFields(data, host) + } else { + genericPillItem { + id("no_IDS") + imageRes(R.drawable.ic_baseline_perm_contact_calendar_24) + text(host.stringProvider.getString(R.string.create_space_identity_server_info_none)) + } + genericButtonItem { + id("Discover_Settings") + text(host.stringProvider.getString(R.string.open_discovery_settings)) + textColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary)) + buttonClickAction { + host.listener?.onNoIdentityServer() + } + } + } + } + + private fun buildEmailFields(data: CreateSpaceState, host: SpaceAdd3pidEpoxyController) { + for (index in 0..2) { + val mail = data.default3pidInvite?.get(index) + formEditTextItem { + id("3pid$index") + enabled(true) + value(mail) + hint(host.stringProvider.getString(R.string.medium_email)) + inputType(InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS) + endIconMode(TextInputLayout.END_ICON_CLEAR_TEXT) + errorMessage( + if (data.emailValidationResult?.get(index) == false) { + host.stringProvider.getString(R.string.does_not_look_like_valid_email) + } else null + ) + onTextChange { text -> + host.listener?.on3pidChange(index, text) + } + } + } + } + + interface Listener { + fun on3pidChange(index: Int, newName: String) + fun onNoIdentityServer() + } +} diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/SpaceDetailEpoxyController.kt b/vector/src/main/java/im/vector/app/features/spaces/create/SpaceDetailEpoxyController.kt index 27c08d1f6fb..14b0db2cd17 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/SpaceDetailEpoxyController.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/SpaceDetailEpoxyController.kt @@ -27,6 +27,7 @@ import im.vector.app.features.form.formEditableSquareAvatarItem import im.vector.app.features.form.formMultiLineEditTextItem import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.roomdirectory.createroom.RoomAliasErrorFormatter +import org.matrix.android.sdk.api.MatrixConstants import org.matrix.android.sdk.api.session.room.alias.RoomAliasError import org.matrix.android.sdk.api.util.MatrixItem import javax.inject.Inject @@ -94,6 +95,7 @@ class SpaceDetailEpoxyController @Inject constructor( hint(host.stringProvider.getString(R.string.create_space_alias_hint)) suffixText(":" + data.homeServerName) prefixText("#") + maxLength(MatrixConstants.maxAliasLocalPartLength(data.homeServerName)) onFocusChange { hasFocus -> host.aliasTextIsFocused = hasFocus } diff --git a/vector/src/main/java/im/vector/app/features/spaces/create/WizardButtonView.kt b/vector/src/main/java/im/vector/app/features/spaces/create/WizardButtonView.kt index ade2b4b00c0..971d61dd9c3 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/create/WizardButtonView.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/create/WizardButtonView.kt @@ -29,12 +29,12 @@ import im.vector.app.R import im.vector.app.core.extensions.setTextOrHide import im.vector.app.databinding.ViewSpaceTypeButtonBinding -class WizardButtonView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) - : ConstraintLayout(context, attrs, defStyle) { +class WizardButtonView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) : + ConstraintLayout(context, attrs, defStyle) { private val views: ViewSpaceTypeButtonBinding - var title: String? = null + var title: CharSequence? = null set(value) { if (value != title) { field = value diff --git a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryFragment.kt index 63813042b21..3af66cbb6b4 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryFragment.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/explore/SpaceDirectoryFragment.kt @@ -44,6 +44,7 @@ import im.vector.app.features.home.room.detail.timeline.TimelineEventController import im.vector.app.features.matrixto.SpaceCardRenderer import im.vector.app.features.permalink.PermalinkHandler import im.vector.app.features.spaces.manage.ManageType +import im.vector.app.features.spaces.manage.SpaceAddRoomSpaceChooserBottomSheet import im.vector.app.features.spaces.manage.SpaceManageActivity import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.schedulers.Schedulers @@ -75,6 +76,27 @@ class SpaceDirectoryFragment @Inject constructor( private val viewModel by activityViewModel(SpaceDirectoryViewModel::class) private val epoxyVisibilityTracker = EpoxyVisibilityTracker() + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + childFragmentManager.setFragmentResultListener(SpaceAddRoomSpaceChooserBottomSheet.REQUEST_KEY, this) { _, bundle -> + + bundle.getString(SpaceAddRoomSpaceChooserBottomSheet.BUNDLE_KEY_ACTION)?.let { action -> + val spaceId = withState(viewModel) { it.spaceId } + when (action) { + SpaceAddRoomSpaceChooserBottomSheet.ACTION_ADD_ROOMS -> { + addExistingRoomActivityResult.launch(SpaceManageActivity.newIntent(requireContext(), spaceId, ManageType.AddRooms)) + } + SpaceAddRoomSpaceChooserBottomSheet.ACTION_ADD_SPACES -> { + addExistingRoomActivityResult.launch(SpaceManageActivity.newIntent(requireContext(), spaceId, ManageType.AddRoomsOnlySpaces)) + } + else -> { + // nop + } + } + } + } + } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -170,7 +192,7 @@ class SpaceDirectoryFragment @Inject constructor( } override fun addExistingRooms(spaceId: String) { - addExistingRoomActivityResult.launch(SpaceManageActivity.newIntent(requireContext(), spaceId, ManageType.AddRooms)) + SpaceAddRoomSpaceChooserBottomSheet.newInstance().show(childFragmentManager, "SpaceAddRoomSpaceChooserBottomSheet") } override fun loadAdditionalItemsIfNeeded() { diff --git a/vector/src/main/java/im/vector/app/features/spaces/invite/SpaceInviteBottomSheet.kt b/vector/src/main/java/im/vector/app/features/spaces/invite/SpaceInviteBottomSheet.kt index 28c06d84d11..4f8d0b6c2fb 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/invite/SpaceInviteBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/invite/SpaceInviteBottomSheet.kt @@ -36,6 +36,7 @@ import im.vector.app.core.platform.ButtonStateView import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.app.core.utils.toast import im.vector.app.databinding.BottomSheetInvitedToSpaceBinding +import im.vector.app.features.displayname.getBestName import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.matrixto.SpaceCardRenderer import kotlinx.parcelize.Parcelize @@ -175,8 +176,7 @@ class SpaceInviteBottomSheet : VectorBaseBottomSheetDialogFragment - val knownMembers = roomSummary.otherMemberIds.filter { session.getExistingDirectRoomWithUser(it) != null }.mapNotNull { session.getUser(it) } @@ -57,6 +61,34 @@ class SpaceInviteBottomSheetViewModel @AssistedInject constructor( peopleYouKnow = Success(peopleYouKnow) ) } + if (roomSummary.membership == Membership.INVITE) { + getLatestRoomSummary(roomSummary) + } + } + } + + /** + * Try to request the room summary api to get more info + */ + private fun getLatestRoomSummary(roomSummary: RoomSummary) { + viewModelScope.launch(Dispatchers.IO) { + val peekResult = tryOrNull { session.peekRoom(roomSummary.roomId) } as? PeekResult.Success ?: return@launch + setState { + copy( + summary = Success( + roomSummary.copy( + joinedMembersCount = peekResult.numJoinedMembers, + // it's also possible that the name/avatar did change since the invite.. + // if it's null keep the old one as summary API might not be available + // and peek result could be null for other reasons (not peekable) + avatarUrl = peekResult.avatarUrl ?: roomSummary.avatarUrl, + displayName = peekResult.name ?: roomSummary.displayName, + topic = peekResult.topic ?: roomSummary.topic + // maybe use someMembers field later? + ) + ) + ) + } } } diff --git a/vector/src/main/java/im/vector/app/features/spaces/leave/SelectChildrenController.kt b/vector/src/main/java/im/vector/app/features/spaces/leave/SelectChildrenController.kt new file mode 100644 index 00000000000..39d1d726755 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/spaces/leave/SelectChildrenController.kt @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.spaces.leave + +import com.airbnb.epoxy.TypedEpoxyController +import com.airbnb.mvrx.Fail +import com.airbnb.mvrx.Loading +import com.airbnb.mvrx.Success +import com.airbnb.mvrx.Uninitialized +import im.vector.app.R +import im.vector.app.core.epoxy.loadingItem +import im.vector.app.core.epoxy.noResultItem +import im.vector.app.core.resources.StringProvider +import im.vector.app.features.home.AvatarRenderer +import im.vector.app.features.spaces.manage.roomSelectionItem +import io.reactivex.functions.Predicate +import org.matrix.android.sdk.api.extensions.orFalse +import org.matrix.android.sdk.api.session.room.model.RoomSummary +import org.matrix.android.sdk.api.util.toMatrixItem +import javax.inject.Inject + +class SelectChildrenController @Inject constructor( + val avatarRenderer: AvatarRenderer, + val stringProvider: StringProvider +) : TypedEpoxyController() { + + interface Listener { + fun onItemSelected(roomSummary: RoomSummary) + } + + var listener: Listener? = null + private val matchFilter = RoomSearchMatchFilter() + override fun buildModels(data: SpaceLeaveAdvanceViewState?) { + val children = data?.allChildren ?: return + val host = this + when (children) { + Uninitialized -> return + is Loading -> { + loadingItem { + id("loading") + } + } + is Success -> { + matchFilter.filter = data.currentFilter + val roomList = children.invoke().filter { matchFilter.test(it) } + + if (roomList.isEmpty()) { + noResultItem { + id("empty") + text(host.stringProvider.getString(R.string.no_result_placeholder)) + } + } else { + roomList.forEach { item -> + roomSelectionItem { + id(item.roomId) + matrixItem(item.toMatrixItem()) + avatarRenderer(host.avatarRenderer) + selected(data.selectedRooms.contains(item.roomId)) + itemClickListener { + host.listener?.onItemSelected(item) + } + } + } + } + } + is Fail -> { +// errorWithRetryItem { +// id("failed_to_load") +// } + } + } + } + + class RoomSearchMatchFilter : Predicate { + var filter: String = "" + + override fun test(roomSummary: RoomSummary): Boolean { + if (filter.isEmpty()) { + // No filter + return true + } + // if filter is "Jo Do", it should match "John Doe" + return filter.split(" ").all { + roomSummary.name.contains(it, ignoreCase = true).orFalse() || + roomSummary.topic.contains(it, ignoreCase = true).orFalse() + } + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvanceViewAction.kt b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvanceViewAction.kt new file mode 100644 index 00000000000..68b313ec7f1 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvanceViewAction.kt @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.spaces.leave + +import im.vector.app.core.platform.VectorViewModelAction + +sealed class SpaceLeaveAdvanceViewAction : VectorViewModelAction { + data class ToggleSelection(val roomId: String) : SpaceLeaveAdvanceViewAction() + data class UpdateFilter(val filter: String) : SpaceLeaveAdvanceViewAction() + object DoLeave : SpaceLeaveAdvanceViewAction() + object ClearError : SpaceLeaveAdvanceViewAction() +} diff --git a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvanceViewState.kt b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvanceViewState.kt new file mode 100644 index 00000000000..f7802d2a311 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvanceViewState.kt @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.spaces.leave + +import com.airbnb.mvrx.Async +import com.airbnb.mvrx.MvRxState +import com.airbnb.mvrx.Uninitialized +import im.vector.app.features.spaces.SpaceBottomSheetSettingsArgs +import org.matrix.android.sdk.api.session.room.model.RoomSummary + +data class SpaceLeaveAdvanceViewState( + val spaceId: String, + val spaceSummary: RoomSummary? = null, + val allChildren: Async> = Uninitialized, + val selectedRooms: List = emptyList(), + val currentFilter: String = "", + val leaveState: Async = Uninitialized +) : MvRxState { + constructor(args: SpaceBottomSheetSettingsArgs) : this( + spaceId = args.spaceId + ) +} diff --git a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedActivity.kt b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedActivity.kt new file mode 100644 index 00000000000..cb667083243 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedActivity.kt @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.spaces.leave + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import androidx.core.view.isGone +import androidx.core.view.isVisible +import com.airbnb.mvrx.Fail +import com.airbnb.mvrx.Loading +import com.airbnb.mvrx.MvRx +import com.airbnb.mvrx.Success +import com.airbnb.mvrx.viewModel +import com.google.android.material.appbar.MaterialToolbar +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import im.vector.app.R +import im.vector.app.core.di.ScreenComponent +import im.vector.app.core.error.ErrorFormatter +import im.vector.app.core.extensions.commitTransaction +import im.vector.app.core.extensions.hideKeyboard +import im.vector.app.core.extensions.setTextOrHide +import im.vector.app.core.platform.ToolbarConfigurable +import im.vector.app.core.platform.VectorBaseActivity +import im.vector.app.databinding.ActivitySimpleLoadingBinding +import im.vector.app.features.spaces.SpaceBottomSheetSettingsArgs +import javax.inject.Inject + +class SpaceLeaveAdvancedActivity : VectorBaseActivity(), + SpaceLeaveAdvancedViewModel.Factory, + ToolbarConfigurable { + + override fun getBinding(): ActivitySimpleLoadingBinding = ActivitySimpleLoadingBinding.inflate(layoutInflater) + + val leaveViewModel: SpaceLeaveAdvancedViewModel by viewModel() + + @Inject lateinit var viewModelFactory: SpaceLeaveAdvancedViewModel.Factory + @Inject lateinit var errorFormatter: ErrorFormatter + + override fun create(initialState: SpaceLeaveAdvanceViewState) = viewModelFactory.create(initialState) + + override fun injectWith(injector: ScreenComponent) { + super.injectWith(injector) + injector.inject(this) + } + + override fun showWaitingView(text: String?) { + hideKeyboard() + views.waitingView.waitingStatusText.isGone = views.waitingView.waitingStatusText.text.isNullOrBlank() + super.showWaitingView(text) + } + + override fun hideWaitingView() { + views.waitingView.waitingStatusText.setTextOrHide(null) + views.waitingView.waitingHorizontalProgress.progress = 0 + views.waitingView.waitingHorizontalProgress.isVisible = false + super.hideWaitingView() + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + val args = intent?.getParcelableExtra(MvRx.KEY_ARG) + + if (isFirstCreation()) { + val simpleName = SpaceLeaveAdvancedFragment::class.java.simpleName + if (supportFragmentManager.findFragmentByTag(simpleName) == null) { + supportFragmentManager.commitTransaction { + replace( + R.id.simpleFragmentContainer, + SpaceLeaveAdvancedFragment::class.java, + Bundle().apply { this.putParcelable(MvRx.KEY_ARG, args) }, + simpleName + ) + } + } + } + } + + override fun initUiAndData() { + super.initUiAndData() + waitingView = views.waitingView.waitingView + leaveViewModel.subscribe(this) { state -> + when (state.leaveState) { + is Loading -> { + showWaitingView() + } + is Success -> { + setResult(RESULT_OK) + finish() + } + is Fail -> { + hideWaitingView() + MaterialAlertDialogBuilder(this) + .setTitle(R.string.dialog_title_error) + .setMessage(errorFormatter.toHumanReadable(state.leaveState.error)) + .setPositiveButton(R.string.ok) { _, _ -> + leaveViewModel.handle(SpaceLeaveAdvanceViewAction.ClearError) + } + .show() + } + else -> { + hideWaitingView() + } + } + } + } + + companion object { + fun newIntent(context: Context, spaceId: String): Intent { + return Intent(context, SpaceLeaveAdvancedActivity::class.java).apply { + putExtra(MvRx.KEY_ARG, SpaceBottomSheetSettingsArgs(spaceId)) + } + } + } + + override fun configure(toolbar: MaterialToolbar) { + configureToolbar(toolbar) + } +} diff --git a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedFragment.kt new file mode 100644 index 00000000000..e78d90c6d94 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedFragment.kt @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.spaces.leave + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.airbnb.mvrx.activityViewModel +import com.airbnb.mvrx.withState +import com.jakewharton.rxbinding3.appcompat.queryTextChanges +import im.vector.app.core.extensions.cleanup +import im.vector.app.core.extensions.configureWith +import im.vector.app.core.platform.VectorBaseFragment +import im.vector.app.databinding.FragmentSpaceLeaveAdvancedBinding +import io.reactivex.rxkotlin.subscribeBy +import org.matrix.android.sdk.api.session.room.model.RoomSummary +import java.util.concurrent.TimeUnit +import javax.inject.Inject + +class SpaceLeaveAdvancedFragment @Inject constructor( + val controller: SelectChildrenController +) : VectorBaseFragment(), + SelectChildrenController.Listener { + + override fun getBinding(inflater: LayoutInflater, container: ViewGroup?) = + FragmentSpaceLeaveAdvancedBinding.inflate(layoutInflater, container, false) + + val viewModel: SpaceLeaveAdvancedViewModel by activityViewModel() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setupToolbar(views.toolbar) + controller.listener = this + views.roomList.configureWith(controller) + views.spaceLeaveCancel.debouncedClicks { requireActivity().finish() } + + views.spaceLeaveButton.debouncedClicks { + viewModel.handle(SpaceLeaveAdvanceViewAction.DoLeave) + } + + views.publicRoomsFilter.queryTextChanges() + .debounce(100, TimeUnit.MILLISECONDS) + .subscribeBy { + viewModel.handle(SpaceLeaveAdvanceViewAction.UpdateFilter(it.toString())) + } + .disposeOnDestroyView() + } + + override fun onDestroyView() { + controller.listener = null + views.roomList.cleanup() + super.onDestroyView() + } + + override fun invalidate() = withState(viewModel) { state -> + super.invalidate() + controller.setData(state) + } + + override fun onItemSelected(roomSummary: RoomSummary) { + viewModel.handle(SpaceLeaveAdvanceViewAction.ToggleSelection(roomSummary.roomId)) + } +} diff --git a/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedViewModel.kt new file mode 100644 index 00000000000..7461d09b8b3 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/spaces/leave/SpaceLeaveAdvancedViewModel.kt @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.spaces.leave + +import androidx.lifecycle.viewModelScope +import com.airbnb.mvrx.ActivityViewModelContext +import com.airbnb.mvrx.Fail +import com.airbnb.mvrx.FragmentViewModelContext +import com.airbnb.mvrx.Loading +import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.Success +import com.airbnb.mvrx.Uninitialized +import com.airbnb.mvrx.ViewModelContext +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import im.vector.app.AppStateHandler +import im.vector.app.core.platform.EmptyViewEvents +import im.vector.app.core.platform.VectorViewModel +import kotlinx.coroutines.launch +import okhttp3.internal.toImmutableList +import org.matrix.android.sdk.api.query.ActiveSpaceFilter +import org.matrix.android.sdk.api.query.RoomCategoryFilter +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.room.model.Membership +import org.matrix.android.sdk.api.session.room.roomSummaryQueryParams +import org.matrix.android.sdk.rx.rx +import timber.log.Timber + +class SpaceLeaveAdvancedViewModel @AssistedInject constructor( + @Assisted val initialState: SpaceLeaveAdvanceViewState, + private val session: Session, + private val appStateHandler: AppStateHandler +) : VectorViewModel(initialState) { + + override fun handle(action: SpaceLeaveAdvanceViewAction) = withState { state -> + when (action) { + is SpaceLeaveAdvanceViewAction.ToggleSelection -> { + val existing = state.selectedRooms.toMutableList() + if (existing.contains(action.roomId)) { + existing.remove(action.roomId) + } else { + existing.add(action.roomId) + } + setState { + copy( + selectedRooms = existing.toImmutableList() + ) + } + } + is SpaceLeaveAdvanceViewAction.UpdateFilter -> { + setState { copy(currentFilter = action.filter) } + } + SpaceLeaveAdvanceViewAction.DoLeave -> { + setState { copy(leaveState = Loading()) } + viewModelScope.launch { + try { + state.selectedRooms.forEach { + try { + session.getRoom(it)?.leave(null) + } catch (failure: Throwable) { + // silently ignore? + Timber.e(failure, "Fail to leave sub rooms/spaces") + } + } + + session.getRoom(initialState.spaceId)?.leave(null) + // We observe the membership and to dismiss when we have remote echo of leaving + } catch (failure: Throwable) { + setState { copy(leaveState = Fail(failure)) } + } + } + } + SpaceLeaveAdvanceViewAction.ClearError -> { + setState { copy(leaveState = Uninitialized) } + } + } + } + + init { + val spaceSummary = session.getRoomSummary(initialState.spaceId) + setState { copy(spaceSummary = spaceSummary) } + session.getRoom(initialState.spaceId)?.let { room -> + room.rx().liveRoomSummary().subscribe { + it.getOrNull()?.let { + if (it.membership == Membership.LEAVE) { + setState { copy(leaveState = Success(Unit)) } + if (appStateHandler.safeActiveSpaceId() == initialState.spaceId) { + // switch to home? + appStateHandler.setCurrentSpace(null, session) + } + } + } + } + } + + viewModelScope.launch { + val children = session.getRoomSummaries( + roomSummaryQueryParams { + includeType = null + memberships = listOf(Membership.JOIN) + activeSpaceFilter = ActiveSpaceFilter.ActiveSpace(initialState.spaceId) + roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS + } + ) + + setState { + copy(allChildren = Success(children)) + } + } + } + + @AssistedFactory + interface Factory { + fun create(initialState: SpaceLeaveAdvanceViewState): SpaceLeaveAdvancedViewModel + } + + companion object : MvRxViewModelFactory { + override fun create(viewModelContext: ViewModelContext, state: SpaceLeaveAdvanceViewState): SpaceLeaveAdvancedViewModel? { + val factory = when (viewModelContext) { + is FragmentViewModelContext -> viewModelContext.fragment as? Factory + is ActivityViewModelContext -> viewModelContext.activity as? Factory + } + return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface") + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/AddRoomListController.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/AddRoomListController.kt index e23cd1ce3fb..15103dd870c 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/AddRoomListController.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/AddRoomListController.kt @@ -43,8 +43,8 @@ class AddRoomListController @Inject constructor( override fun areContentsTheSame(oldItem: RoomSummary, newItem: RoomSummary): Boolean { // for this use case we can test less things - return oldItem.displayName == newItem.displayName - && oldItem.avatarUrl == newItem.avatarUrl + return oldItem.displayName == newItem.displayName && + oldItem.avatarUrl == newItem.avatarUrl } } ) { diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/RoomManageSelectionItem.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/RoomManageSelectionItem.kt index 566750fbae5..21da4f0dfe8 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/RoomManageSelectionItem.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/RoomManageSelectionItem.kt @@ -27,6 +27,7 @@ import im.vector.app.core.epoxy.ClickListener import im.vector.app.core.epoxy.VectorEpoxyHolder import im.vector.app.core.epoxy.VectorEpoxyModel import im.vector.app.core.epoxy.onClick +import im.vector.app.features.displayname.getBestName import im.vector.app.features.home.AvatarRenderer import org.matrix.android.sdk.api.util.MatrixItem diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/RoomSelectionItem.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/RoomSelectionItem.kt index fde47818844..df736bebbbe 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/RoomSelectionItem.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/RoomSelectionItem.kt @@ -26,6 +26,7 @@ import im.vector.app.core.epoxy.ClickListener import im.vector.app.core.epoxy.VectorEpoxyHolder import im.vector.app.core.epoxy.VectorEpoxyModel import im.vector.app.core.epoxy.onClick +import im.vector.app.features.displayname.getBestName import im.vector.app.features.home.AvatarRenderer import org.matrix.android.sdk.api.util.MatrixItem diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomFragment.kt index b2fb061731e..e192ec3c88d 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomFragment.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomFragment.kt @@ -27,6 +27,7 @@ import androidx.recyclerview.widget.LinearLayoutManager import com.airbnb.mvrx.Loading import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.fragmentViewModel +import com.airbnb.mvrx.withState import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.jakewharton.rxbinding3.appcompat.queryTextChanges import im.vector.app.R @@ -109,11 +110,25 @@ class SpaceAddRoomFragment @Inject constructor( }.disposeOnDestroyView() viewModel.selectSubscribe(this, SpaceAddRoomsState::shouldShowDMs) { - dmEpoxyController.disabled = !it + dmEpoxyController.disabled = !it + }.disposeOnDestroyView() + + viewModel.selectSubscribe(this, SpaceAddRoomsState::onlyShowSpaces) { + spaceEpoxyController.disabled = !it + roomEpoxyController.disabled = it + views.createNewRoom.text = if (it) getString(R.string.create_space) else getString(R.string.create_new_room) + val title = if (it) getString(R.string.space_add_existing_spaces) else getString(R.string.space_add_existing_rooms_only) + views.appBarTitle.text = title }.disposeOnDestroyView() views.createNewRoom.debouncedClicks { - sharedViewModel.handle(SpaceManagedSharedAction.CreateRoom) + withState(viewModel) { state -> + if (state.onlyShowSpaces) { + sharedViewModel.handle(SpaceManagedSharedAction.CreateSpace) + } else { + sharedViewModel.handle(SpaceManagedSharedAction.CreateRoom) + } + } } viewModel.observeViewEvents { diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomSpaceChooserBottomSheet.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomSpaceChooserBottomSheet.kt new file mode 100644 index 00000000000..971ff7e0b17 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomSpaceChooserBottomSheet.kt @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.spaces.manage + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.setFragmentResult +import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment +import im.vector.app.databinding.BottomSheetAddRoomsOrSpacesToSpaceBinding + +class SpaceAddRoomSpaceChooserBottomSheet : VectorBaseBottomSheetDialogFragment() { + + override val showExpanded = true + + override fun getBinding(inflater: LayoutInflater, container: ViewGroup?) = + BottomSheetAddRoomsOrSpacesToSpaceBinding.inflate(inflater, container, false) + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + views.addSpaces.views.bottomSheetActionClickableZone.debouncedClicks { + setFragmentResult(REQUEST_KEY, Bundle().apply { + putString(BUNDLE_KEY_ACTION, ACTION_ADD_SPACES) + }) + dismiss() + } + + views.addRooms.views.bottomSheetActionClickableZone.debouncedClicks { + setFragmentResult(REQUEST_KEY, Bundle().apply { + putString(BUNDLE_KEY_ACTION, ACTION_ADD_ROOMS) + }) + dismiss() + } + } + + companion object { + + const val REQUEST_KEY = "SpaceAddRoomSpaceChooserBottomSheet" + const val BUNDLE_KEY_ACTION = "SpaceAddRoomSpaceChooserBottomSheet.Action" + const val ACTION_ADD_ROOMS = "Action.AddRoom" + const val ACTION_ADD_SPACES = "Action.AddSpaces" + + fun newInstance(): SpaceAddRoomSpaceChooserBottomSheet { + return SpaceAddRoomSpaceChooserBottomSheet() + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomsState.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomsState.kt index 2d9113ae680..e941d04b22c 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomsState.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomsState.kt @@ -27,10 +27,12 @@ data class SpaceAddRoomsState( val spaceName: String = "", val ignoreRooms: List = emptyList(), val isSaving: Async> = Uninitialized, - val shouldShowDMs : Boolean = false + val shouldShowDMs: Boolean = false, + val onlyShowSpaces: Boolean = false // val selectionList: Map = emptyMap() ) : MvRxState { constructor(args: SpaceManageArgs) : this( - spaceId = args.spaceId + spaceId = args.spaceId, + onlyShowSpaces = args.manageType == ManageType.AddRoomsOnlySpaces ) } diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomsViewEvents.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomsViewEvents.kt index cb5571b1073..53de1b7ee4d 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomsViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomsViewEvents.kt @@ -21,5 +21,5 @@ import im.vector.app.core.platform.VectorViewEvents sealed class SpaceAddRoomsViewEvents : VectorViewEvents { object WarnUnsavedChanged : SpaceAddRoomsViewEvents() object SavedDone : SpaceAddRoomsViewEvents() - data class SaveFailed(val reason: Throwable): SpaceAddRoomsViewEvents() + data class SaveFailed(val reason: Throwable) : SpaceAddRoomsViewEvents() } diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomsViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomsViewModel.kt index 35c415b0874..9f5cd7d35ef 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceAddRoomsViewModel.kt @@ -127,7 +127,7 @@ class SpaceAddRoomsViewModel @AssistedInject constructor( copy( spaceName = spaceSummary?.displayName ?: "", ignoreRooms = (spaceSummary?.flattenParentIds ?: emptyList()) + listOf(initialState.spaceId), - shouldShowDMs = spaceSummary?.isPublic == false + shouldShowDMs = !onlyShowSpaces && spaceSummary?.isPublic == false ) } } diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceChildInfoMatchFilter.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceChildInfoMatchFilter.kt index 17f5d13e19c..66878a40119 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceChildInfoMatchFilter.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceChildInfoMatchFilter.kt @@ -30,8 +30,8 @@ class SpaceChildInfoMatchFilter : Predicate { } // if filter is "Jo Do", it should match "John Doe" return filter.split(" ").all { - spaceChildInfo.name?.contains(it, ignoreCase = true).orFalse() - || spaceChildInfo.topic?.contains(it, ignoreCase = true).orFalse() + spaceChildInfo.name?.contains(it, ignoreCase = true).orFalse() || + spaceChildInfo.topic?.contains(it, ignoreCase = true).orFalse() } } } diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageActivity.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageActivity.kt index 630c5780693..16a1e18da20 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageActivity.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageActivity.kt @@ -20,12 +20,12 @@ import android.content.Context import android.content.Intent import android.os.Bundle import android.os.Parcelable -import com.google.android.material.appbar.MaterialToolbar import androidx.core.view.isGone import androidx.core.view.isVisible import com.airbnb.mvrx.MvRx import com.airbnb.mvrx.viewModel import com.airbnb.mvrx.withState +import com.google.android.material.appbar.MaterialToolbar import im.vector.app.R import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.addFragmentToBackstack @@ -40,6 +40,7 @@ import im.vector.app.features.roomdirectory.createroom.CreateRoomArgs import im.vector.app.features.roomdirectory.createroom.CreateRoomFragment import im.vector.app.features.roomprofile.RoomProfileArgs import im.vector.app.features.roomprofile.alias.RoomAliasFragment +import im.vector.app.features.roomprofile.permissions.RoomPermissionsFragment import kotlinx.parcelize.Parcelize import javax.inject.Inject @@ -98,7 +99,8 @@ class SpaceManageActivity : VectorBaseActivity(), if (isFirstCreation()) { withState(sharedViewModel) { when (it.manageType) { - ManageType.AddRooms -> { + ManageType.AddRooms, + ManageType.AddRoomsOnlySpaces -> { val simpleName = SpaceAddRoomFragment::class.java.simpleName if (supportFragmentManager.findFragmentByTag(simpleName) == null) { supportFragmentManager.commitTransaction { @@ -110,7 +112,7 @@ class SpaceManageActivity : VectorBaseActivity(), } } } - ManageType.Settings -> { + ManageType.Settings -> { val simpleName = SpaceSettingsFragment::class.java.simpleName if (supportFragmentManager.findFragmentByTag(simpleName) == null && args?.spaceId != null) { supportFragmentManager.commitTransaction { @@ -131,22 +133,29 @@ class SpaceManageActivity : VectorBaseActivity(), sharedViewModel.observeViewEvents { when (it) { - SpaceManagedSharedViewEvents.Finish -> { + SpaceManagedSharedViewEvents.Finish -> { finish() } - SpaceManagedSharedViewEvents.HideLoading -> { + SpaceManagedSharedViewEvents.HideLoading -> { hideWaitingView() } - SpaceManagedSharedViewEvents.ShowLoading -> { + SpaceManagedSharedViewEvents.ShowLoading -> { showWaitingView() } - SpaceManagedSharedViewEvents.NavigateToCreateRoom -> { + SpaceManagedSharedViewEvents.NavigateToCreateRoom -> { addFragmentToBackstack( R.id.simpleFragmentContainer, CreateRoomFragment::class.java, CreateRoomArgs("", parentSpaceId = args?.spaceId) ) } + SpaceManagedSharedViewEvents.NavigateToCreateSpace -> { + addFragmentToBackstack( + R.id.simpleFragmentContainer, + CreateRoomFragment::class.java, + CreateRoomArgs("", parentSpaceId = args?.spaceId, isSpace = true) + ) + } SpaceManagedSharedViewEvents.NavigateToManageRooms -> { args?.spaceId?.let { spaceId -> addFragmentToBackstack( @@ -156,7 +165,7 @@ class SpaceManageActivity : VectorBaseActivity(), ) } } - SpaceManagedSharedViewEvents.NavigateToAliasSettings -> { + SpaceManagedSharedViewEvents.NavigateToAliasSettings -> { args?.spaceId?.let { spaceId -> addFragmentToBackstack( R.id.simpleFragmentContainer, @@ -165,6 +174,14 @@ class SpaceManageActivity : VectorBaseActivity(), ) } } + SpaceManagedSharedViewEvents.NavigateToPermissionSettings -> { + args?.spaceId?.let { spaceId -> + addFragmentToBackstack( + R.id.simpleFragmentContainer, RoomPermissionsFragment::class.java, + RoomProfileArgs(spaceId) + ) + } + } } } } diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsViewModel.kt index 0222dde275a..b1f6d5c3c30 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageRoomsViewModel.kt @@ -33,6 +33,7 @@ import im.vector.app.core.platform.VectorViewModel import im.vector.app.features.session.coroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.session.Session class SpaceManageRoomsViewModel @AssistedInject constructor( @@ -104,6 +105,10 @@ class SpaceManageRoomsViewModel @AssistedInject constructor( } catch (failure: Throwable) { errorList.add(failure) } + tryOrNull { + // also remove space parent if any? and if I can + session.spaceService().removeSpaceParent(it, state.spaceId) + } } if (errorList.isEmpty()) { // success diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageSharedViewModel.kt index f1d041056fd..8f23788d191 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageSharedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageSharedViewModel.kt @@ -23,6 +23,7 @@ import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject +import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import org.matrix.android.sdk.api.session.Session @@ -48,15 +49,17 @@ class SpaceManageSharedViewModel @AssistedInject constructor( override fun handle(action: SpaceManagedSharedAction) { when (action) { - SpaceManagedSharedAction.HandleBack -> { + SpaceManagedSharedAction.HandleBack -> { // for now finish _viewEvents.post(SpaceManagedSharedViewEvents.Finish) } - SpaceManagedSharedAction.HideLoading -> _viewEvents.post(SpaceManagedSharedViewEvents.HideLoading) - SpaceManagedSharedAction.ShowLoading -> _viewEvents.post(SpaceManagedSharedViewEvents.ShowLoading) - SpaceManagedSharedAction.CreateRoom -> _viewEvents.post(SpaceManagedSharedViewEvents.NavigateToCreateRoom) - SpaceManagedSharedAction.ManageRooms -> _viewEvents.post(SpaceManagedSharedViewEvents.NavigateToManageRooms) - SpaceManagedSharedAction.OpenSpaceAliasesSettings -> _viewEvents.post(SpaceManagedSharedViewEvents.NavigateToAliasSettings) - } + SpaceManagedSharedAction.HideLoading -> _viewEvents.post(SpaceManagedSharedViewEvents.HideLoading) + SpaceManagedSharedAction.ShowLoading -> _viewEvents.post(SpaceManagedSharedViewEvents.ShowLoading) + SpaceManagedSharedAction.CreateRoom -> _viewEvents.post(SpaceManagedSharedViewEvents.NavigateToCreateRoom) + SpaceManagedSharedAction.CreateSpace -> _viewEvents.post(SpaceManagedSharedViewEvents.NavigateToCreateSpace) + SpaceManagedSharedAction.ManageRooms -> _viewEvents.post(SpaceManagedSharedViewEvents.NavigateToManageRooms) + SpaceManagedSharedAction.OpenSpaceAliasesSettings -> _viewEvents.post(SpaceManagedSharedViewEvents.NavigateToAliasSettings) + SpaceManagedSharedAction.OpenSpacePermissionSettings -> _viewEvents.post(SpaceManagedSharedViewEvents.NavigateToPermissionSettings) + }.exhaustive } } diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageViewState.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageViewState.kt index 91d49d90d14..35596f08845 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageViewState.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManageViewState.kt @@ -20,6 +20,7 @@ import com.airbnb.mvrx.MvRxState enum class ManageType { AddRooms, + AddRoomsOnlySpaces, Settings, ManageRooms } diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManagedSharedAction.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManagedSharedAction.kt index 77143470bc9..5f2032b25d3 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManagedSharedAction.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManagedSharedAction.kt @@ -23,6 +23,8 @@ sealed class SpaceManagedSharedAction : VectorViewModelAction { object ShowLoading : SpaceManagedSharedAction() object HideLoading : SpaceManagedSharedAction() object CreateRoom : SpaceManagedSharedAction() + object CreateSpace : SpaceManagedSharedAction() object ManageRooms : SpaceManagedSharedAction() object OpenSpaceAliasesSettings : SpaceManagedSharedAction() + object OpenSpacePermissionSettings : SpaceManagedSharedAction() } diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManagedSharedViewEvents.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManagedSharedViewEvents.kt index ab993764c68..789f1073415 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManagedSharedViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceManagedSharedViewEvents.kt @@ -23,6 +23,8 @@ sealed class SpaceManagedSharedViewEvents : VectorViewEvents { object ShowLoading : SpaceManagedSharedViewEvents() object HideLoading : SpaceManagedSharedViewEvents() object NavigateToCreateRoom : SpaceManagedSharedViewEvents() + object NavigateToCreateSpace : SpaceManagedSharedViewEvents() object NavigateToManageRooms : SpaceManagedSharedViewEvents() object NavigateToAliasSettings : SpaceManagedSharedViewEvents() + object NavigateToPermissionSettings : SpaceManagedSharedViewEvents() } diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceSettingsController.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceSettingsController.kt index 27204be8a67..7cd5a70279c 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceSettingsController.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceSettingsController.kt @@ -25,7 +25,6 @@ import im.vector.app.core.resources.StringProvider import im.vector.app.features.form.formEditTextItem import im.vector.app.features.form.formEditableSquareAvatarItem import im.vector.app.features.form.formMultiLineEditTextItem -import im.vector.app.features.form.formSwitchItem import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.roomprofile.settings.RoomSettingsViewState import im.vector.app.features.settings.VectorPreferences @@ -53,6 +52,7 @@ class SpaceSettingsController @Inject constructor( fun onManageRooms() fun setIsPublic(public: Boolean) fun onRoomAliasesClicked() + fun onRoomPermissionsClicked() } var callback: Callback? = null @@ -105,27 +105,35 @@ class SpaceSettingsController @Inject constructor( } val isPublic = (data.newRoomJoinRules.newJoinRules ?: data.currentRoomJoinRules) == RoomJoinRules.PUBLIC - if (vectorPreferences.labsUseExperimentalRestricted()) { - buildProfileAction( - id = "joinRule", - title = stringProvider.getString(R.string.room_settings_room_access_title), - subtitle = data.getJoinRuleWording(stringProvider), - divider = false, - editable = data.actionPermissions.canChangeJoinRule, - action = { if (data.actionPermissions.canChangeJoinRule) callback?.onJoinRuleClicked() } - ) - } else { - formSwitchItem { - id("isPublic") - enabled(data.actionPermissions.canChangeJoinRule) - title(host.stringProvider.getString(R.string.make_this_space_public)) - switchChecked(isPublic) - - listener { value -> - host.callback?.setIsPublic(value) - } - } - } + buildProfileAction( + id = "joinRule", + title = stringProvider.getString(R.string.room_settings_space_access_title), + subtitle = data.getJoinRuleWording(stringProvider), + divider = true, + editable = data.actionPermissions.canChangeJoinRule, + action = { if (data.actionPermissions.canChangeJoinRule) callback?.onJoinRuleClicked() } + ) +// if (vectorPreferences.labsUseExperimentalRestricted()) { +// buildProfileAction( +// id = "joinRule", +// title = stringProvider.getString(R.string.room_settings_room_access_title), +// subtitle = data.getJoinRuleWording(stringProvider), +// divider = false, +// editable = data.actionPermissions.canChangeJoinRule, +// action = { if (data.actionPermissions.canChangeJoinRule) callback?.onJoinRuleClicked() } +// ) +// } else { +// formSwitchItem { +// id("isPublic") +// enabled(data.actionPermissions.canChangeJoinRule) +// title(host.stringProvider.getString(R.string.make_this_space_public)) +// switchChecked(isPublic) +// +// listener { value -> +// host.callback?.setIsPublic(value) +// } +// } +// } dividerItem { id("divider") } @@ -134,7 +142,7 @@ class SpaceSettingsController @Inject constructor( id = "manage_rooms", title = stringProvider.getString(R.string.space_settings_manage_rooms), // subtitle = data.getJoinRuleWording(stringProvider), - divider = vectorPreferences.developerMode() || isPublic, + divider = true, editable = data.actionPermissions.canAddChildren, action = { if (data.actionPermissions.canAddChildren) callback?.onManageRooms() @@ -146,12 +154,21 @@ class SpaceSettingsController @Inject constructor( id = "alias", title = stringProvider.getString(R.string.space_settings_alias_title), subtitle = stringProvider.getString(R.string.space_settings_alias_subtitle), - divider = vectorPreferences.developerMode(), + divider = true, editable = true, action = { callback?.onRoomAliasesClicked() } ) } + buildProfileAction( + id = "permissions", + title = stringProvider.getString(R.string.space_settings_permissions_title), + subtitle = stringProvider.getString(R.string.space_settings_permissions_subtitle), + divider = vectorPreferences.developerMode(), + editable = true, + action = { callback?.onRoomPermissionsClicked() } + ) + if (vectorPreferences.developerMode()) { buildProfileAction( id = "dev_tools", diff --git a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceSettingsFragment.kt b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceSettingsFragment.kt index e831732bccf..5e5eb50b874 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceSettingsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/manage/SpaceSettingsFragment.kt @@ -47,7 +47,7 @@ import im.vector.app.features.roomprofile.settings.RoomSettingsAction import im.vector.app.features.roomprofile.settings.RoomSettingsViewEvents import im.vector.app.features.roomprofile.settings.RoomSettingsViewModel import im.vector.app.features.roomprofile.settings.RoomSettingsViewState -import im.vector.app.features.roomprofile.settings.joinrule.RoomJoinRuleBottomSheet +import im.vector.app.features.roomprofile.settings.joinrule.RoomJoinRuleActivity import im.vector.app.features.roomprofile.settings.joinrule.RoomJoinRuleSharedActionViewModel import org.matrix.android.sdk.api.session.room.model.GuestAccess import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility @@ -133,12 +133,6 @@ class SpaceSettingsFragment @Inject constructor( state.roomSummary()?.let { views.roomSettingsToolbarTitleView.text = it.displayName - views.roomSettingsToolbarTitleView.setCompoundDrawablesWithIntrinsicBounds( - null, - null, - drawableProvider.getDrawable(R.drawable.ic_beta_pill), - null - ) avatarRenderer.render(it.toMatrixItem(), views.roomSettingsToolbarAvatarImageView) views.roomSettingsDecorationToolbarAvatarImageView.render(it.roomEncryptionTrustLevel) } @@ -199,10 +193,8 @@ class SpaceSettingsFragment @Inject constructor( // N/A for space settings screen } - override fun onJoinRuleClicked() = withState(viewModel) { state -> - val currentJoinRule = state.newRoomJoinRules.newJoinRules ?: state.currentRoomJoinRules - RoomJoinRuleBottomSheet.newInstance(currentJoinRule) - .show(childFragmentManager, "RoomJoinRuleBottomSheet") + override fun onJoinRuleClicked() { + startActivity(RoomJoinRuleActivity.newIntent(requireContext(), roomProfileArgs.roomId)) } override fun onToggleGuestAccess() = withState(viewModel) { state -> @@ -237,6 +229,10 @@ class SpaceSettingsFragment @Inject constructor( sharedViewModel.handle(SpaceManagedSharedAction.OpenSpaceAliasesSettings) } + override fun onRoomPermissionsClicked() { + sharedViewModel.handle(SpaceManagedSharedAction.OpenSpacePermissionSettings) + } + override fun onImageReady(uri: Uri?) { uri ?: return viewModel.handle( diff --git a/vector/src/main/java/im/vector/app/features/spaces/preview/SpacePreviewViewEvents.kt b/vector/src/main/java/im/vector/app/features/spaces/preview/SpacePreviewViewEvents.kt index 2f0eddb1895..8f85008c97c 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/preview/SpacePreviewViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/preview/SpacePreviewViewEvents.kt @@ -19,7 +19,7 @@ package im.vector.app.features.spaces.preview import im.vector.app.core.platform.VectorViewEvents sealed class SpacePreviewViewEvents : VectorViewEvents { - object Dismiss: SpacePreviewViewEvents() - object JoinSuccess: SpacePreviewViewEvents() - data class JoinFailure(val message: String?): SpacePreviewViewEvents() + object Dismiss : SpacePreviewViewEvents() + object JoinSuccess : SpacePreviewViewEvents() + data class JoinFailure(val message: String?) : SpacePreviewViewEvents() } diff --git a/vector/src/main/java/im/vector/app/features/spaces/preview/SpaceTabView.kt b/vector/src/main/java/im/vector/app/features/spaces/preview/SpaceTabView.kt index 675e7070e7e..41fc8bf6b90 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/preview/SpaceTabView.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/preview/SpaceTabView.kt @@ -23,8 +23,8 @@ import im.vector.app.R class SpaceTabView constructor(context: Context, attrs: AttributeSet? = null, - defStyleAttr: Int = 0) - : LinearLayout(context, attrs, defStyleAttr) { + defStyleAttr: Int = 0) : + LinearLayout(context, attrs, defStyleAttr) { constructor(context: Context, attrs: AttributeSet) : this(context, attrs, 0) {} constructor(context: Context) : this(context, null, 0) {} diff --git a/vector/src/main/java/im/vector/app/features/spaces/share/ShareSpaceBottomSheet.kt b/vector/src/main/java/im/vector/app/features/spaces/share/ShareSpaceBottomSheet.kt index 4289af7b3b3..bd69de0d95a 100644 --- a/vector/src/main/java/im/vector/app/features/spaces/share/ShareSpaceBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/spaces/share/ShareSpaceBottomSheet.kt @@ -73,7 +73,6 @@ class ShareSpaceBottomSheet : VectorBaseBottomSheetDialogFragment "Idle" + SyncState.InvalidToken -> "InvalidToken" + SyncState.Killed -> "Killed" + SyncState.Killing -> "Killing" + SyncState.NoNetwork -> "NoNetwork" + SyncState.Paused -> "Paused" + is SyncState.Running -> "$this" + } + } + + private fun SyncStatusService.Status.IncrementalSyncStatus.toHumanReadable(): String { + return when (this) { + SyncStatusService.Status.IncrementalSyncIdle -> "Idle" + is SyncStatusService.Status.IncrementalSyncParsing -> "Parsing ${this.rooms} room(s) ${this.toDevice} toDevice(s)" + SyncStatusService.Status.IncrementalSyncError -> "Error" + SyncStatusService.Status.IncrementalSyncDone -> "Done" + else -> "?" + } + } } diff --git a/vector/src/main/java/im/vector/app/features/terms/ReviewTermsFragment.kt b/vector/src/main/java/im/vector/app/features/terms/ReviewTermsFragment.kt index 7a97cbf8cd2..cb76e5b31fa 100644 --- a/vector/src/main/java/im/vector/app/features/terms/ReviewTermsFragment.kt +++ b/vector/src/main/java/im/vector/app/features/terms/ReviewTermsFragment.kt @@ -33,7 +33,6 @@ import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.utils.openUrlInChromeCustomTab import im.vector.app.databinding.FragmentReviewTermsBinding - import org.matrix.android.sdk.api.session.terms.TermsService import javax.inject.Inject diff --git a/vector/src/main/java/im/vector/app/features/terms/ReviewTermsViewModel.kt b/vector/src/main/java/im/vector/app/features/terms/ReviewTermsViewModel.kt index 4ecad80876a..db9e0174734 100644 --- a/vector/src/main/java/im/vector/app/features/terms/ReviewTermsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/terms/ReviewTermsViewModel.kt @@ -23,8 +23,8 @@ import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.core.extensions.exhaustive import im.vector.app.core.platform.VectorViewModel import kotlinx.coroutines.launch diff --git a/vector/src/main/java/im/vector/app/features/themes/ThemeUtils.kt b/vector/src/main/java/im/vector/app/features/themes/ThemeUtils.kt index c4c9ca63aea..6c8ea0a3f91 100644 --- a/vector/src/main/java/im/vector/app/features/themes/ThemeUtils.kt +++ b/vector/src/main/java/im/vector/app/features/themes/ThemeUtils.kt @@ -71,8 +71,8 @@ object ThemeUtils { */ fun isLightTheme(context: Context): Boolean { val theme = getApplicationTheme(context) - return theme == THEME_LIGHT_VALUE - || (theme == SYSTEM_THEME_VALUE && !isSystemDarkTheme(context.resources)) + return theme == THEME_LIGHT_VALUE || + (theme == SYSTEM_THEME_VALUE && !isSystemDarkTheme(context.resources)) } /** diff --git a/vector/src/main/java/im/vector/app/features/usercode/ScanUserCodeFragment.kt b/vector/src/main/java/im/vector/app/features/usercode/ScanUserCodeFragment.kt index da9c6792ff4..e70ffd0d76e 100644 --- a/vector/src/main/java/im/vector/app/features/usercode/ScanUserCodeFragment.kt +++ b/vector/src/main/java/im/vector/app/features/usercode/ScanUserCodeFragment.kt @@ -37,13 +37,12 @@ import im.vector.app.core.utils.registerForPermissionsResult import im.vector.app.databinding.FragmentQrCodeScannerWithButtonBinding import im.vector.lib.multipicker.MultiPicker import im.vector.lib.multipicker.utils.ImageUtils - import me.dm7.barcodescanner.zxing.ZXingScannerView import org.matrix.android.sdk.api.extensions.tryOrNull import javax.inject.Inject -class ScanUserCodeFragment @Inject constructor() - : VectorBaseFragment(), +class ScanUserCodeFragment @Inject constructor() : + VectorBaseFragment(), ZXingScannerView.ResultHandler { override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentQrCodeScannerWithButtonBinding { diff --git a/vector/src/main/java/im/vector/app/features/usercode/ShowUserCodeFragment.kt b/vector/src/main/java/im/vector/app/features/usercode/ShowUserCodeFragment.kt index c4511188133..b794b23d0ef 100644 --- a/vector/src/main/java/im/vector/app/features/usercode/ShowUserCodeFragment.kt +++ b/vector/src/main/java/im/vector/app/features/usercode/ShowUserCodeFragment.kt @@ -30,7 +30,6 @@ import im.vector.app.core.utils.registerForPermissionsResult import im.vector.app.core.utils.startSharePlainTextIntent import im.vector.app.databinding.FragmentUserCodeShowBinding import im.vector.app.features.home.AvatarRenderer - import javax.inject.Inject class ShowUserCodeFragment @Inject constructor( diff --git a/vector/src/main/java/im/vector/app/features/usercode/UserCodeActivity.kt b/vector/src/main/java/im/vector/app/features/usercode/UserCodeActivity.kt index 0771a5d2387..5e8145168bb 100644 --- a/vector/src/main/java/im/vector/app/features/usercode/UserCodeActivity.kt +++ b/vector/src/main/java/im/vector/app/features/usercode/UserCodeActivity.kt @@ -24,6 +24,7 @@ import android.widget.Toast import androidx.core.app.ActivityCompat import androidx.core.view.isVisible import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentManager import com.airbnb.mvrx.MvRx import com.airbnb.mvrx.viewModel import com.airbnb.mvrx.withState @@ -60,8 +61,25 @@ class UserCodeActivity : VectorBaseActivity(), injector.inject(this) } + private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() { + override fun onFragmentResumed(fm: FragmentManager, f: Fragment) { + if (f is MatrixToBottomSheet) { + f.interactionListener = this@UserCodeActivity + } + super.onFragmentResumed(fm, f) + } + + override fun onFragmentPaused(fm: FragmentManager, f: Fragment) { + if (f is MatrixToBottomSheet) { + f.interactionListener = null + } + super.onFragmentPaused(fm, f) + } + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, false) if (isFirstCreation()) { // should be there early for shared element transition @@ -74,7 +92,7 @@ class UserCodeActivity : VectorBaseActivity(), UserCodeState.Mode.SCAN -> showFragment(ScanUserCodeFragment::class, Bundle.EMPTY) is UserCodeState.Mode.RESULT -> { showFragment(ShowUserCodeFragment::class, Bundle.EMPTY) - MatrixToBottomSheet.withLink(mode.rawLink, this).show(supportFragmentManager, "MatrixToBottomSheet") + MatrixToBottomSheet.withLink(mode.rawLink).show(supportFragmentManager, "MatrixToBottomSheet") } } } @@ -97,6 +115,11 @@ class UserCodeActivity : VectorBaseActivity(), } } + override fun onDestroy() { + supportFragmentManager.unregisterFragmentLifecycleCallbacks(fragmentLifecycleCallbacks) + super.onDestroy() + } + private fun showFragment(fragmentClass: KClass, bundle: Bundle) { if (supportFragmentManager.findFragmentByTag(fragmentClass.simpleName) == null) { supportFragmentManager.commitTransaction { @@ -110,10 +133,12 @@ class UserCodeActivity : VectorBaseActivity(), } } - override fun navigateToRoom(roomId: String) { + override fun mxToBottomSheetNavigateToRoom(roomId: String) { navigator.openRoom(this, roomId) } + override fun mxToBottomSheetSwitchToSpace(spaceId: String) {} + override fun onBackPressed() = withState(sharedViewModel) { when (it.mode) { UserCodeState.Mode.SHOW -> super.onBackPressed() diff --git a/vector/src/main/java/im/vector/app/features/usercode/UserCodeSharedViewModel.kt b/vector/src/main/java/im/vector/app/features/usercode/UserCodeSharedViewModel.kt index 071044fc8a6..74da6357872 100644 --- a/vector/src/main/java/im/vector/app/features/usercode/UserCodeSharedViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/usercode/UserCodeSharedViewModel.kt @@ -22,8 +22,8 @@ import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.R import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider diff --git a/vector/src/main/java/im/vector/app/features/userdirectory/InviteByEmailItem.kt b/vector/src/main/java/im/vector/app/features/userdirectory/InviteByEmailItem.kt new file mode 100644 index 00000000000..2258239bde1 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/userdirectory/InviteByEmailItem.kt @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2021 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.userdirectory + +import android.widget.ImageView +import android.widget.TextView +import androidx.core.view.isVisible +import com.airbnb.epoxy.EpoxyAttribute +import com.airbnb.epoxy.EpoxyModelClass +import im.vector.app.R +import im.vector.app.core.epoxy.ClickListener +import im.vector.app.core.epoxy.VectorEpoxyHolder +import im.vector.app.core.epoxy.VectorEpoxyModel +import im.vector.app.features.home.AvatarRenderer + +@EpoxyModelClass(layout = R.layout.item_invite_by_mail) +abstract class InviteByEmailItem : VectorEpoxyModel() { + + @EpoxyAttribute lateinit var avatarRenderer: AvatarRenderer + @EpoxyAttribute lateinit var foundItem: ThreePidUser + @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var clickListener: ClickListener? = null + @EpoxyAttribute var selected: Boolean = false + + override fun bind(holder: Holder) { + super.bind(holder) + holder.itemTitleText.text = foundItem.email + holder.checkedImageView.isVisible = false + holder.avatarImageView.isVisible = true + holder.view.setOnClickListener(clickListener) + if (selected) { + holder.checkedImageView.isVisible = true + holder.avatarImageView.isVisible = false + } else { + holder.checkedImageView.isVisible = false + holder.avatarImageView.isVisible = true + } + } + + class Holder : VectorEpoxyHolder() { + val itemTitleText by bind(R.id.itemTitle) + val avatarImageView by bind(R.id.itemAvatar) + val checkedImageView by bind(R.id.itemAvatarChecked) + } +} diff --git a/vector/src/main/java/im/vector/app/features/userdirectory/PendingSelection.kt b/vector/src/main/java/im/vector/app/features/userdirectory/PendingSelection.kt index 57f950b2c81..e754eb88f80 100644 --- a/vector/src/main/java/im/vector/app/features/userdirectory/PendingSelection.kt +++ b/vector/src/main/java/im/vector/app/features/userdirectory/PendingSelection.kt @@ -16,8 +16,10 @@ package im.vector.app.features.userdirectory +import im.vector.app.features.displayname.getBestName import org.matrix.android.sdk.api.session.identity.ThreePid import org.matrix.android.sdk.api.session.user.model.User +import org.matrix.android.sdk.api.util.toMatrixItem sealed class PendingSelection { data class UserPendingSelection(val user: User) : PendingSelection() @@ -25,7 +27,14 @@ sealed class PendingSelection { fun getBestName(): String { return when (this) { - is UserPendingSelection -> user.getBestName() + is UserPendingSelection -> user.toMatrixItem().getBestName() + is ThreePidPendingSelection -> threePid.value + } + } + + fun getMxId(): String { + return when (this) { + is UserPendingSelection -> user.userId is ThreePidPendingSelection -> threePid.value } } diff --git a/vector/src/main/java/im/vector/app/features/userdirectory/UserListAction.kt b/vector/src/main/java/im/vector/app/features/userdirectory/UserListAction.kt index 7835232b092..83829c11197 100644 --- a/vector/src/main/java/im/vector/app/features/userdirectory/UserListAction.kt +++ b/vector/src/main/java/im/vector/app/features/userdirectory/UserListAction.kt @@ -24,4 +24,5 @@ sealed class UserListAction : VectorViewModelAction { data class AddPendingSelection(val pendingSelection: PendingSelection) : UserListAction() data class RemovePendingSelection(val pendingSelection: PendingSelection) : UserListAction() object ComputeMatrixToLinkForSharing : UserListAction() + data class UpdateUserConsent(val consent: Boolean) : UserListAction() } diff --git a/vector/src/main/java/im/vector/app/features/userdirectory/UserListController.kt b/vector/src/main/java/im/vector/app/features/userdirectory/UserListController.kt index bc2ef1f6946..147367c1da9 100644 --- a/vector/src/main/java/im/vector/app/features/userdirectory/UserListController.kt +++ b/vector/src/main/java/im/vector/app/features/userdirectory/UserListController.kt @@ -26,9 +26,14 @@ import im.vector.app.core.epoxy.errorWithRetryItem import im.vector.app.core.epoxy.loadingItem import im.vector.app.core.epoxy.noResultItem import im.vector.app.core.error.ErrorFormatter +import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.StringProvider +import im.vector.app.core.ui.list.genericPillItem +import im.vector.app.features.displayname.getBestName import im.vector.app.features.home.AvatarRenderer +import me.gujun.android.span.span import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.identity.IdentityServiceError import org.matrix.android.sdk.api.session.identity.ThreePid import org.matrix.android.sdk.api.session.user.model.User import org.matrix.android.sdk.api.util.toMatrixItem @@ -37,6 +42,7 @@ import javax.inject.Inject class UserListController @Inject constructor(private val session: Session, private val avatarRenderer: AvatarRenderer, private val stringProvider: StringProvider, + private val colorProvider: ColorProvider, private val errorFormatter: ErrorFormatter) : EpoxyController() { private var state: UserListViewState? = null @@ -86,6 +92,119 @@ class UserListController @Inject constructor(private val session: Session, } } + when (val matchingEmail = currentState.matchingEmail) { + is Success -> { + matchingEmail()?.let { threePidUser -> + userListHeaderItem { + id("identity_server_result_header") + header(host.stringProvider.getString(R.string.discovery_section, currentState.configuredIdentityServer ?: "")) + } + val isSelected = currentState.pendingSelections.any { pendingSelection -> + when (pendingSelection) { + is PendingSelection.ThreePidPendingSelection -> { + when (pendingSelection.threePid) { + is ThreePid.Email -> pendingSelection.threePid.email == threePidUser.email + is ThreePid.Msisdn -> false + } + } + is PendingSelection.UserPendingSelection -> { + threePidUser.user != null && threePidUser.user.userId == pendingSelection.user.userId + } + } + } + if (threePidUser.user == null) { + inviteByEmailItem { + id("email_${threePidUser.email}") + foundItem(threePidUser) + selected(isSelected) + clickListener { + host.callback?.onThreePidClick(ThreePid.Email(threePidUser.email)) + } + } + } else { + userDirectoryUserItem { + id(threePidUser.user.userId) + selected(isSelected) + matrixItem(threePidUser.user.toMatrixItem().let { + it.copy( + displayName = "${it.getBestName()} [${threePidUser.email}]" + ) + }) + avatarRenderer(host.avatarRenderer) + clickListener { + host.callback?.onItemClick(threePidUser.user) + } + } + } + } + } + is Fail -> { + when (matchingEmail.error) { + is IdentityServiceError.UserConsentNotProvided -> { + genericPillItem { + id("consent_not_given") + text( + span { + span { + text = host.stringProvider.getString(R.string.settings_discovery_consent_notice_off) + } + +"\n" + span { + text = host.stringProvider.getString(R.string.settings_discovery_consent_action_give_consent) + textStyle = "bold" + textColor = host.colorProvider.getColorFromAttribute(R.attr.colorPrimary) + } + } + ) + itemClickAction { + host.callback?.giveIdentityServerConsent() + } + } + } + is IdentityServiceError.NoIdentityServerConfigured -> { + genericPillItem { + id("no_IDS") + imageRes(R.drawable.ic_info) + text( + span { + span { + text = host.stringProvider.getString(R.string.finish_setting_up_discovery) + textColor = host.colorProvider.getColorFromAttribute(R.attr.vctr_content_primary) + } + +"\n" + span { + text = host.stringProvider.getString(R.string.discovery_invite) + textColor = host.colorProvider.getColorFromAttribute(R.attr.vctr_content_secondary) + } + +"\n" + span { + text = host.stringProvider.getString(R.string.finish_setup) + textStyle = "bold" + textColor = host.colorProvider.getColorFromAttribute(R.attr.colorPrimary) + } + } + ) + itemClickAction { + host.callback?.onSetupDiscovery() + } + } + } + } + } + is Loading -> { + userListHeaderItem { + id("identity_server_result_header_loading") + header(host.stringProvider.getString(R.string.discovery_section, currentState.configuredIdentityServer ?: "")) + } + loadingItem { + id("is_loading") + } + } + else -> { + // nop + } + } + when (currentState.knownUsers) { is Uninitialized -> renderEmptyState() is Loading -> renderLoading() @@ -196,5 +315,7 @@ class UserListController @Inject constructor(private val session: Session, fun onItemClick(user: User) fun onMatrixIdClick(matrixId: String) fun onThreePidClick(threePid: ThreePid) + fun onSetupDiscovery() + fun giveIdentityServerConsent() } } diff --git a/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt b/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt index 6e6df7a7aad..5fdc316851d 100644 --- a/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt +++ b/vector/src/main/java/im/vector/app/features/userdirectory/UserListFragment.kt @@ -39,10 +39,11 @@ import im.vector.app.core.extensions.hideKeyboard import im.vector.app.core.extensions.setupAsSearch import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.utils.DimensionConverter +import im.vector.app.core.utils.showIdentityServerConsentDialog import im.vector.app.core.utils.startSharePlainTextIntent import im.vector.app.databinding.FragmentUserListBinding import im.vector.app.features.homeserver.HomeServerCapabilitiesViewModel - +import im.vector.app.features.settings.VectorSettingsActivity import org.matrix.android.sdk.api.session.identity.ThreePid import org.matrix.android.sdk.api.session.user.model.User import javax.inject.Inject @@ -131,7 +132,7 @@ class UserListFragment @Inject constructor( private fun setupSearchView() { withState(viewModel) { - views.userListSearch.hint = getString(R.string.user_directory_search_hint) + views.userListSearch.hint = getString(R.string.user_directory_search_hint_2) } views.userListSearch .textChanges() @@ -217,6 +218,21 @@ class UserListFragment @Inject constructor( viewModel.handle(UserListAction.AddPendingSelection(PendingSelection.ThreePidPendingSelection(threePid))) } + override fun onSetupDiscovery() { + navigator.openSettings( + requireContext(), + VectorSettingsActivity.EXTRA_DIRECT_ACCESS_DISCOVERY_SETTINGS + ) + } + + override fun giveIdentityServerConsent() { + withState(viewModel) { state -> + requireContext().showIdentityServerConsentDialog(state.configuredIdentityServer) { + viewModel.handle(UserListAction.UpdateUserConsent(true)) + } + } + } + override fun onUseQRCode() { view?.hideKeyboard() sharedActionViewModel.post(UserListSharedAction.AddByQrCode) diff --git a/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewModel.kt b/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewModel.kt index 5d5247ec061..fc0dbbfb732 100644 --- a/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewModel.kt @@ -19,18 +19,22 @@ package im.vector.app.features.userdirectory import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MvRxViewModelFactory +import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext import com.jakewharton.rxrelay2.BehaviorRelay import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.core.extensions.exhaustive +import im.vector.app.core.extensions.isEmail import im.vector.app.core.extensions.toggle import im.vector.app.core.platform.VectorViewModel import io.reactivex.Single import io.reactivex.android.schedulers.AndroidSchedulers import org.matrix.android.sdk.api.MatrixPatterns import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.identity.IdentityServiceListener +import org.matrix.android.sdk.api.session.identity.ThreePid import org.matrix.android.sdk.api.session.profile.ProfileService import org.matrix.android.sdk.api.session.user.model.User import org.matrix.android.sdk.api.util.toMatrixItem @@ -41,12 +45,18 @@ import java.util.concurrent.TimeUnit private typealias KnownUsersSearch = String private typealias DirectoryUsersSearch = String +data class ThreePidUser( + val email: String, + val user: User? +) + class UserListViewModel @AssistedInject constructor(@Assisted initialState: UserListViewState, - private val session: Session) - : VectorViewModel(initialState) { + private val session: Session) : + VectorViewModel(initialState) { private val knownUsersSearch = BehaviorRelay.create() private val directoryUsersSearch = BehaviorRelay.create() + private val identityServerUsersSearch = BehaviorRelay.create() @AssistedFactory interface Factory { @@ -64,24 +74,72 @@ class UserListViewModel @AssistedInject constructor(@Assisted initialState: User } } + private val identityServerListener = object : IdentityServiceListener { + override fun onIdentityServerChange() { + withState { + identityServerUsersSearch.accept(it.searchTerm) + setState { + copy( + configuredIdentityServer = cleanISURL(session.identityService().getCurrentIdentityServerUrl()) + ) + } + } + } + } + init { observeUsers() + setState { + copy( + configuredIdentityServer = cleanISURL(session.identityService().getCurrentIdentityServerUrl()) + ) + } + session.identityService().addListener(identityServerListener) + } + + private fun cleanISURL(url: String?): String? { + return url?.removePrefix("https://") + } + + override fun onCleared() { + session.identityService().removeListener(identityServerListener) + super.onCleared() } override fun handle(action: UserListAction) { when (action) { - is UserListAction.SearchUsers -> handleSearchUsers(action.value) - is UserListAction.ClearSearchUsers -> handleClearSearchUsers() - is UserListAction.AddPendingSelection -> handleSelectUser(action) - is UserListAction.RemovePendingSelection -> handleRemoveSelectedUser(action) + is UserListAction.SearchUsers -> handleSearchUsers(action.value) + is UserListAction.ClearSearchUsers -> handleClearSearchUsers() + is UserListAction.AddPendingSelection -> handleSelectUser(action) + is UserListAction.RemovePendingSelection -> handleRemoveSelectedUser(action) UserListAction.ComputeMatrixToLinkForSharing -> handleShareMyMatrixToLink() + is UserListAction.UpdateUserConsent -> handleISUpdateConsent(action) }.exhaustive } + private fun handleISUpdateConsent(action: UserListAction.UpdateUserConsent) { + session.identityService().setUserConsent(action.consent) + withState { + identityServerUsersSearch.accept(it.searchTerm) + } + } + private fun handleSearchUsers(searchTerm: String) { setState { - copy(searchTerm = searchTerm) + copy( + searchTerm = searchTerm + ) + } + if (searchTerm.isEmail().not()) { + // if it's not an email reset to uninitialized + // because the flow won't be triggered and result would stay + setState { + copy( + matchingEmail = Uninitialized + ) + } } + identityServerUsersSearch.accept(searchTerm) knownUsersSearch.accept(searchTerm) directoryUsersSearch.accept(searchTerm) } @@ -95,12 +153,45 @@ class UserListViewModel @AssistedInject constructor(@Assisted initialState: User private fun handleClearSearchUsers() { knownUsersSearch.accept("") directoryUsersSearch.accept("") + identityServerUsersSearch.accept("") setState { copy(searchTerm = "") } } private fun observeUsers() = withState { state -> + + identityServerUsersSearch + .filter { it.isEmail() } + .throttleLast(300, TimeUnit.MILLISECONDS) + .switchMapSingle { search -> + val rx = session.rx() + val stream = + rx.lookupThreePid(ThreePid.Email(search)).flatMap { + it.getOrNull()?.let { foundThreePid -> + rx.getProfileInfo(foundThreePid.matrixId) + .map { json -> + ThreePidUser( + email = search, + user = User( + userId = foundThreePid.matrixId, + displayName = json[ProfileService.DISPLAY_NAME_KEY] as? String, + avatarUrl = json[ProfileService.AVATAR_URL_KEY] as? String + ) + ) + } + .onErrorResumeNext { + Single.just(ThreePidUser(email = search, user = User(foundThreePid.matrixId))) + } + } ?: Single.just(ThreePidUser(email = search, user = null)) + } + stream.toAsync { + copy(matchingEmail = it) + } + } + .subscribe() + .disposeOnClear() + knownUsersSearch .throttleLast(300, TimeUnit.MILLISECONDS) .observeOn(AndroidSchedulers.mainThread()) @@ -136,14 +227,16 @@ class UserListViewModel @AssistedInject constructor(@Assisted initialState: User avatarUrl = json[ProfileService.AVATAR_URL_KEY] as? String ).toOptional() } - .onErrorReturn { + .onErrorResumeNext { // Profile API can be restricted and doesn't have to return result. // In this case allow inviting valid user ids. - User( - userId = search, - displayName = null, - avatarUrl = null - ).toOptional() + Single.just( + User( + userId = search, + displayName = null, + avatarUrl = null + ).toOptional() + ) } Single.zip( diff --git a/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewState.kt b/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewState.kt index f1cbbd3b9d1..b66d36c5f04 100644 --- a/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewState.kt +++ b/vector/src/main/java/im/vector/app/features/userdirectory/UserListViewState.kt @@ -27,10 +27,12 @@ data class UserListViewState( val excludedUserIds: Set? = null, val knownUsers: Async> = Uninitialized, val directoryUsers: Async> = Uninitialized, + val matchingEmail: Async = Uninitialized, val filteredMappedContacts: List = emptyList(), val pendingSelections: Set = emptySet(), val searchTerm: String = "", val singleSelection: Boolean, + val configuredIdentityServer: String? = null, private val showInviteActions: Boolean, val showContactBookAction: Boolean ) : MvRxState { diff --git a/vector/src/main/java/im/vector/app/features/webview/ConsentWebViewEventListener.kt b/vector/src/main/java/im/vector/app/features/webview/ConsentWebViewEventListener.kt index b9ad28c2dfc..a3d4902b417 100644 --- a/vector/src/main/java/im/vector/app/features/webview/ConsentWebViewEventListener.kt +++ b/vector/src/main/java/im/vector/app/features/webview/ConsentWebViewEventListener.kt @@ -31,8 +31,8 @@ private const val RIOT_BOT_ID = "@riot-bot:matrix.org" */ class ConsentWebViewEventListener(activity: VectorBaseActivity<*>, private val session: Session, - private val delegate: WebViewEventListener) - : WebViewEventListener by delegate { + private val delegate: WebViewEventListener) : + WebViewEventListener by delegate { private val safeActivity: VectorBaseActivity<*>? by weak(activity) diff --git a/vector/src/main/java/im/vector/app/features/widgets/WidgetActivity.kt b/vector/src/main/java/im/vector/app/features/widgets/WidgetActivity.kt index 9072957a950..c902b661c03 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/WidgetActivity.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/WidgetActivity.kt @@ -19,10 +19,10 @@ package im.vector.app.features.widgets import android.app.Activity import android.content.Context import android.content.Intent -import com.google.android.material.appbar.MaterialToolbar import androidx.core.view.isVisible import com.airbnb.mvrx.MvRx import com.airbnb.mvrx.viewModel +import com.google.android.material.appbar.MaterialToolbar import im.vector.app.R import im.vector.app.core.di.ScreenComponent import im.vector.app.core.extensions.addFragment @@ -33,7 +33,6 @@ import im.vector.app.features.widgets.permissions.RoomWidgetPermissionBottomShee import im.vector.app.features.widgets.permissions.RoomWidgetPermissionViewEvents import im.vector.app.features.widgets.permissions.RoomWidgetPermissionViewModel import im.vector.app.features.widgets.permissions.RoomWidgetPermissionViewState - import org.matrix.android.sdk.api.session.events.model.Content import java.io.Serializable import javax.inject.Inject diff --git a/vector/src/main/java/im/vector/app/features/widgets/WidgetPostAPIHandler.kt b/vector/src/main/java/im/vector/app/features/widgets/WidgetPostAPIHandler.kt index 7d2d89e1bdd..99b3595d118 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/WidgetPostAPIHandler.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/WidgetPostAPIHandler.kt @@ -18,8 +18,8 @@ package im.vector.app.features.widgets import android.text.TextUtils import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.R import im.vector.app.core.resources.StringProvider import im.vector.app.features.session.coroutineScope diff --git a/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt b/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt index bf27173d83b..27a0be83e8c 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/WidgetViewModel.kt @@ -26,8 +26,8 @@ import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider import im.vector.app.features.widgets.permissions.WidgetPermissionsHelper @@ -50,8 +50,8 @@ import javax.net.ssl.HttpsURLConnection class WidgetViewModel @AssistedInject constructor(@Assisted val initialState: WidgetViewState, widgetPostAPIHandlerFactory: WidgetPostAPIHandler.Factory, private val stringProvider: StringProvider, - private val session: Session) - : VectorViewModel(initialState), + private val session: Session) : + VectorViewModel(initialState), WidgetPostAPIHandler.NavigationCallback, IntegrationManagerService.Listener { diff --git a/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionBottomSheet.kt b/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionBottomSheet.kt index 4036195b65f..68f55773a4f 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionBottomSheet.kt @@ -33,7 +33,6 @@ import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.app.databinding.BottomSheetRoomWidgetPermissionBinding import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.widgets.WidgetArgs - import org.matrix.android.sdk.api.util.toMatrixItem import javax.inject.Inject diff --git a/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionViewModel.kt b/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionViewModel.kt index 844a6619b4b..e156ad40875 100644 --- a/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/widgets/permissions/RoomWidgetPermissionViewModel.kt @@ -21,8 +21,8 @@ import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.R import im.vector.app.core.platform.VectorViewModel import kotlinx.coroutines.launch @@ -36,8 +36,8 @@ import timber.log.Timber import java.net.URL class RoomWidgetPermissionViewModel @AssistedInject constructor(@Assisted val initialState: RoomWidgetPermissionViewState, - private val session: Session) - : VectorViewModel(initialState) { + private val session: Session) : + VectorViewModel(initialState) { private val widgetService = session.widgetService() private val integrationManagerService = session.integrationManagerService() diff --git a/vector/src/main/java/im/vector/app/features/workers/signout/ServerBackupStatusViewModel.kt b/vector/src/main/java/im/vector/app/features/workers/signout/ServerBackupStatusViewModel.kt index 6d95911bdbd..075d424f0a1 100644 --- a/vector/src/main/java/im/vector/app/features/workers/signout/ServerBackupStatusViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/workers/signout/ServerBackupStatusViewModel.kt @@ -25,8 +25,8 @@ import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext import dagger.assisted.Assisted -import dagger.assisted.AssistedInject import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject import im.vector.app.core.platform.EmptyAction import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.VectorViewModel @@ -67,8 +67,8 @@ sealed class BannerState { } class ServerBackupStatusViewModel @AssistedInject constructor(@Assisted initialState: ServerBackupStatusViewState, - private val session: Session) - : VectorViewModel(initialState), KeysBackupStateListener { + private val session: Session) : + VectorViewModel(initialState), KeysBackupStateListener { @AssistedFactory interface Factory { @@ -116,9 +116,9 @@ class ServerBackupStatusViewModel @AssistedInject constructor(@Assisted initialS // So recovery is not setup // Check if cross signing is enabled and local secrets known if ( - crossSigningInfo.getOrNull() == null - || (crossSigningInfo.getOrNull()?.isTrusted() == true - && pInfo.getOrNull()?.allKnown().orFalse()) + crossSigningInfo.getOrNull() == null || + (crossSigningInfo.getOrNull()?.isTrusted() == true && + pInfo.getOrNull()?.allKnown().orFalse()) ) { // So 4S is not setup and we have local secrets, return@Function4 BannerState.Setup(numberOfKeys = getNumberOfKeysToBackup()) diff --git a/vector/src/main/java/im/vector/app/features/workers/signout/SignOutBottomSheetDialogFragment.kt b/vector/src/main/java/im/vector/app/features/workers/signout/SignOutBottomSheetDialogFragment.kt index 48459cdd9e9..7f089082a2c 100644 --- a/vector/src/main/java/im/vector/app/features/workers/signout/SignOutBottomSheetDialogFragment.kt +++ b/vector/src/main/java/im/vector/app/features/workers/signout/SignOutBottomSheetDialogFragment.kt @@ -41,7 +41,6 @@ import im.vector.app.databinding.BottomSheetLogoutAndBackupBinding import im.vector.app.features.crypto.keysbackup.setup.KeysBackupSetupActivity import im.vector.app.features.crypto.recover.BootstrapBottomSheet import im.vector.app.features.crypto.recover.SetupMode - import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupState import javax.inject.Inject diff --git a/vector/src/main/res/drawable/circle_with_halo.xml b/vector/src/main/res/drawable/circle_with_halo.xml new file mode 100644 index 00000000000..8e44bfde7f8 --- /dev/null +++ b/vector/src/main/res/drawable/circle_with_halo.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/vector/src/main/res/drawable/ic_jump_to_unread.xml b/vector/src/main/res/drawable/ic_jump_to_unread.xml index 2c5b8b90c13..3d13aabd50d 100644 --- a/vector/src/main/res/drawable/ic_jump_to_unread.xml +++ b/vector/src/main/res/drawable/ic_jump_to_unread.xml @@ -5,6 +5,6 @@ android:viewportHeight="24"> diff --git a/vector/src/main/res/drawable/ic_radio_on.xml b/vector/src/main/res/drawable/ic_radio_on.xml index 5cda8285e63..4ee05d690cd 100644 --- a/vector/src/main/res/drawable/ic_radio_on.xml +++ b/vector/src/main/res/drawable/ic_radio_on.xml @@ -9,6 +9,6 @@ android:fillType="evenOdd"/> + android:fillColor="#FF0000"/> diff --git a/vector/src/main/res/drawable/ic_selected_community.xml b/vector/src/main/res/drawable/ic_selected_community.xml deleted file mode 100644 index e95b54aab37..00000000000 --- a/vector/src/main/res/drawable/ic_selected_community.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/vector/src/main/res/drawable/ic_voice_message_locked.xml b/vector/src/main/res/drawable/ic_voice_message_locked.xml index 2b92d9d5e09..a396f684b16 100644 --- a/vector/src/main/res/drawable/ic_voice_message_locked.xml +++ b/vector/src/main/res/drawable/ic_voice_message_locked.xml @@ -1,5 +1,5 @@ - + diff --git a/vector/src/main/res/drawable/ic_voice_mic_recording.xml b/vector/src/main/res/drawable/ic_voice_mic_recording.xml index eb6cea39a55..a57852c92f3 100644 --- a/vector/src/main/res/drawable/ic_voice_mic_recording.xml +++ b/vector/src/main/res/drawable/ic_voice_mic_recording.xml @@ -3,14 +3,6 @@ android:height="52dp" android:viewportWidth="52" android:viewportHeight="52"> - - + + + + + + + + + + diff --git a/vector/src/main/res/layout/bottom_sheet_leave_space.xml b/vector/src/main/res/layout/bottom_sheet_leave_space.xml new file mode 100644 index 00000000000..b9626bf7853 --- /dev/null +++ b/vector/src/main/res/layout/bottom_sheet_leave_space.xml @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + +