Merge branch 'develop' into feature/aris/thread_live_thread_list
# Conflicts: # matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/relation/DefaultRelationService.kt
This commit is contained in:
commit
e4282e5f29
5
.github/workflows/build.yml
vendored
5
.github/workflows/build.yml
vendored
@ -20,6 +20,10 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
target: [ Gplay, Fdroid ]
|
target: [ Gplay, Fdroid ]
|
||||||
|
# Allow all jobs on develop. Just one per PR.
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.ref == 'refs/heads/develop' && format('integration-tests-develop-{0}-{1}', matrix.target, github.sha) || format('build-debug-{0}-{1}', matrix.target, github.ref) }}
|
||||||
|
cancel-in-progress: true
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: actions/cache@v2
|
- uses: actions/cache@v2
|
||||||
@ -43,6 +47,7 @@ jobs:
|
|||||||
name: Build unsigned GPlay APKs
|
name: Build unsigned GPlay APKs
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.ref == 'refs/heads/main'
|
if: github.ref == 'refs/heads/main'
|
||||||
|
# Only runs on main, no concurrency.
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: actions/cache@v2
|
- uses: actions/cache@v2
|
||||||
|
@ -5,6 +5,7 @@ jobs:
|
|||||||
validation:
|
validation:
|
||||||
name: "Validation"
|
name: "Validation"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
# No concurrency required, this is a prerequisite to other actions and should run every time.
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: gradle/wrapper-validation-action@v1
|
- uses: gradle/wrapper-validation-action@v1
|
||||||
|
200
.github/workflows/integration_tests.yml
vendored
200
.github/workflows/integration_tests.yml
vendored
@ -1,200 +0,0 @@
|
|||||||
name: Integration Tests
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request: { }
|
|
||||||
push:
|
|
||||||
branches: [ main, develop ]
|
|
||||||
|
|
||||||
# Enrich gradle.properties for CI/CD
|
|
||||||
env:
|
|
||||||
CI_GRADLE_ARG_PROPERTIES: >
|
|
||||||
-Porg.gradle.jvmargs=-Xmx2g
|
|
||||||
-Porg.gradle.parallel=false
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
# Build Android Tests [Matrix SDK]
|
|
||||||
build-android-test-matrix-sdk:
|
|
||||||
name: Matrix SDK - Build Android Tests
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- uses: actions/cache@v2
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.gradle/caches
|
|
||||||
~/.gradle/wrapper
|
|
||||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-gradle-
|
|
||||||
- name: Build Android Tests for matrix-sdk-android
|
|
||||||
run: ./gradlew clean matrix-sdk-android:assembleAndroidTest $CI_GRADLE_ARG_PROPERTIES --stacktrace -PallWarningsAsErrors=false
|
|
||||||
|
|
||||||
# Build Android Tests [Matrix APP]
|
|
||||||
build-android-test-app:
|
|
||||||
name: App - Build Android Tests
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- uses: actions/cache@v2
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.gradle/caches
|
|
||||||
~/.gradle/wrapper
|
|
||||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-gradle-
|
|
||||||
- name: Build Android Tests for vector
|
|
||||||
run: ./gradlew clean vector:assembleAndroidTest $CI_GRADLE_ARG_PROPERTIES --stacktrace -PallWarningsAsErrors=false
|
|
||||||
|
|
||||||
# Run Android Tests
|
|
||||||
integration-tests:
|
|
||||||
name: Matrix SDK - Running Integration Tests
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
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:
|
|
||||||
python-version: 3.8
|
|
||||||
- uses: actions/cache@v2
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.gradle/caches
|
|
||||||
~/.gradle/wrapper
|
|
||||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-gradle-
|
|
||||||
- name: Start synapse server
|
|
||||||
run: |
|
|
||||||
pip install matrix-synapse
|
|
||||||
curl https://raw.githubusercontent.com/matrix-org/synapse/develop/demo/start.sh -o start.sh
|
|
||||||
chmod 777 start.sh
|
|
||||||
./start.sh --no-rate-limit
|
|
||||||
# package: org.matrix.android.sdk.session
|
|
||||||
- name: Run integration tests for Matrix SDK [org.matrix.android.sdk.session] API[${{ matrix.api-level }}]
|
|
||||||
continue-on-error: true
|
|
||||||
uses: reactivecircus/android-emulator-runner@v2
|
|
||||||
with:
|
|
||||||
api-level: ${{ matrix.api-level }}
|
|
||||||
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 -Pandroid.testInstrumentationRunnerArguments.package='org.matrix.android.sdk.session' matrix-sdk-android:connectedDebugAndroidTest
|
|
||||||
- name: Read Results [org.matrix.android.sdk.session]
|
|
||||||
continue-on-error: true
|
|
||||||
id: get-comment-body-session
|
|
||||||
run: |
|
|
||||||
body="$(cat ./matrix-sdk-android/build/outputs/androidTest-results/connected/*.xml | grep "<testsuite" | sed "s@.*tests=\(.*\)time=.*@\1@")"
|
|
||||||
echo "::set-output name=session::passed=$body"
|
|
||||||
# package: org.matrix.android.sdk.account
|
|
||||||
- name: Run integration tests for Matrix SDK [org.matrix.android.sdk.account] API[${{ matrix.api-level }}]
|
|
||||||
continue-on-error: true
|
|
||||||
uses: reactivecircus/android-emulator-runner@v2
|
|
||||||
with:
|
|
||||||
api-level: ${{ matrix.api-level }}
|
|
||||||
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 -Pandroid.testInstrumentationRunnerArguments.package='org.matrix.android.sdk.account' matrix-sdk-android:connectedDebugAndroidTest
|
|
||||||
- name: Read Results [org.matrix.android.sdk.account]
|
|
||||||
continue-on-error: true
|
|
||||||
id: get-comment-body-account
|
|
||||||
run: |
|
|
||||||
body="$(cat ./matrix-sdk-android/build/outputs/androidTest-results/connected/*.xml | grep "<testsuite" | sed "s@.*tests=\(.*\)time=.*@\1@")"
|
|
||||||
echo "::set-output name=account::passed=$body"
|
|
||||||
# package: org.matrix.android.sdk.internal
|
|
||||||
- name: Run integration tests for Matrix SDK [org.matrix.android.sdk.internal] API[${{ matrix.api-level }}]
|
|
||||||
continue-on-error: true
|
|
||||||
uses: reactivecircus/android-emulator-runner@v2
|
|
||||||
with:
|
|
||||||
api-level: ${{ matrix.api-level }}
|
|
||||||
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 -Pandroid.testInstrumentationRunnerArguments.package='org.matrix.android.sdk.internal' matrix-sdk-android:connectedDebugAndroidTest
|
|
||||||
- name: Read Results [org.matrix.android.sdk.internal]
|
|
||||||
continue-on-error: true
|
|
||||||
id: get-comment-body-internal
|
|
||||||
run: |
|
|
||||||
body="$(cat ./matrix-sdk-android/build/outputs/androidTest-results/connected/*.xml | grep "<testsuite" | sed "s@.*tests=\(.*\)time=.*@\1@")"
|
|
||||||
echo "::set-output name=internal::passed=$body"
|
|
||||||
# package: org.matrix.android.sdk.ordering
|
|
||||||
- name: Run integration tests for Matrix SDK [org.matrix.android.sdk.ordering] API[${{ matrix.api-level }}]
|
|
||||||
continue-on-error: true
|
|
||||||
uses: reactivecircus/android-emulator-runner@v2
|
|
||||||
with:
|
|
||||||
api-level: ${{ matrix.api-level }}
|
|
||||||
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 -Pandroid.testInstrumentationRunnerArguments.package='org.matrix.android.sdk.ordering' matrix-sdk-android:connectedDebugAndroidTest
|
|
||||||
- name: Read Results [org.matrix.android.sdk.ordering]
|
|
||||||
continue-on-error: true
|
|
||||||
id: get-comment-body-ordering
|
|
||||||
run: |
|
|
||||||
body="$(cat ./matrix-sdk-android/build/outputs/androidTest-results/connected/*.xml | grep "<testsuite" | sed "s@.*tests=\(.*\)time=.*@\1@")"
|
|
||||||
echo "::set-output name=ordering::passed=$body"
|
|
||||||
# package: class PermalinkParserTest
|
|
||||||
- name: Run integration tests for Matrix SDK class [org.matrix.android.sdk.PermalinkParserTest] API[${{ matrix.api-level }}]
|
|
||||||
continue-on-error: true
|
|
||||||
uses: reactivecircus/android-emulator-runner@v2
|
|
||||||
with:
|
|
||||||
api-level: ${{ matrix.api-level }}
|
|
||||||
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 -Pandroid.testInstrumentationRunnerArguments.class='org.matrix.android.sdk.PermalinkParserTest' matrix-sdk-android:connectedDebugAndroidTest
|
|
||||||
- name: Read Results [org.matrix.android.sd.PermalinkParserTest]
|
|
||||||
continue-on-error: true
|
|
||||||
id: get-comment-body-permalink
|
|
||||||
run: |
|
|
||||||
body="$(cat ./matrix-sdk-android/build/outputs/androidTest-results/connected/*.xml | grep "<testsuite" | sed "s@.*tests=\(.*\)time=.*@\1@")"
|
|
||||||
echo "::set-output name=permalink::passed=$body"
|
|
||||||
- name: Find Comment
|
|
||||||
if: github.event_name == 'pull_request'
|
|
||||||
uses: peter-evans/find-comment@v1
|
|
||||||
id: fc
|
|
||||||
with:
|
|
||||||
issue-number: ${{ github.event.pull_request.number }}
|
|
||||||
comment-author: 'github-actions[bot]'
|
|
||||||
body-includes: Integration Tests Results
|
|
||||||
- name: Publish results to PR
|
|
||||||
if: github.event_name == 'pull_request'
|
|
||||||
uses: peter-evans/create-or-update-comment@v1
|
|
||||||
with:
|
|
||||||
comment-id: ${{ steps.fc.outputs.comment-id }}
|
|
||||||
issue-number: ${{ github.event.pull_request.number }}
|
|
||||||
body: |
|
|
||||||
### Matrix SDK
|
|
||||||
## Integration Tests Results:
|
|
||||||
- `[org.matrix.android.sdk.session]`<br>${{ steps.get-comment-body-session.outputs.session }}
|
|
||||||
- `[org.matrix.android.sdk.account]`<br>${{ steps.get-comment-body-account.outputs.account }}
|
|
||||||
- `[org.matrix.android.sdk.internal]`<br>${{ steps.get-comment-body-internal.outputs.internal }}
|
|
||||||
- `[org.matrix.android.sdk.ordering]`<br>${{ steps.get-comment-body-ordering.outputs.ordering }}
|
|
||||||
- `[org.matrix.android.sdk.PermalinkParserTest]`<br>${{ steps.get-comment-body-permalink.outputs.permalink }}
|
|
||||||
edit-mode: replace
|
|
||||||
## Useful commands
|
|
||||||
# script: ./integration_tests_script.sh
|
|
||||||
# script: ./gradlew $CI_GRADLE_ARG_PROPERTIES -Pandroid.testInstrumentationRunnerArguments.package='org.matrix.android.sdk.session' matrix-sdk-android:connectedDebugAndroidTest --info
|
|
||||||
# script: ./gradlew $CI_GRADLE_ARG_PROPERTIES matrix-sdk-android:connectedAndroidTest --info
|
|
||||||
# script: ./gradlew $CI_GRADLE_ARG_PROPERTIES -PallWarningsAsErrors=false connectedCheck --stacktrace
|
|
||||||
# script: ./gradlew $CI_GRADLE_ARG_PROPERTIES -Pandroid.testInstrumentationRunnerArguments.class=org.matrix.android.sdk.session.room.timeline.ChunkEntityTest matrix-sdk-android:connectedAndroidTest --info
|
|
329
.github/workflows/nightly.yml
vendored
Normal file
329
.github/workflows/nightly.yml
vendored
Normal file
@ -0,0 +1,329 @@
|
|||||||
|
name: Nightly Tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ release/* ]
|
||||||
|
schedule:
|
||||||
|
# At 20:00 every day UTC
|
||||||
|
- cron: '0 20 * * *'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
# Enrich gradle.properties for CI/CD
|
||||||
|
env:
|
||||||
|
CI_GRADLE_ARG_PROPERTIES: >
|
||||||
|
-Porg.gradle.jvmargs=-Xmx4g
|
||||||
|
-Porg.gradle.parallel=false
|
||||||
|
-PallWarningsAsErrors=false
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# Build Android Tests [Matrix SDK]
|
||||||
|
build-android-test-matrix-sdk:
|
||||||
|
name: Matrix SDK - Build Android Tests
|
||||||
|
runs-on: macos-latest
|
||||||
|
# No concurrency required, runs every time on a schedule.
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-java@v2
|
||||||
|
with:
|
||||||
|
distribution: 'adopt'
|
||||||
|
java-version: 11
|
||||||
|
- uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.gradle/caches
|
||||||
|
~/.gradle/wrapper
|
||||||
|
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-gradle-
|
||||||
|
- name: Build Android Tests for matrix-sdk-android
|
||||||
|
run: ./gradlew clean matrix-sdk-android:assembleAndroidTest $CI_GRADLE_ARG_PROPERTIES --stacktrace
|
||||||
|
|
||||||
|
# Build Android Tests [Matrix APP]
|
||||||
|
build-android-test-app:
|
||||||
|
name: App - Build Android Tests
|
||||||
|
runs-on: macos-latest
|
||||||
|
# No concurrency required, runs every time on a schedule.
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-java@v2
|
||||||
|
with:
|
||||||
|
distribution: 'adopt'
|
||||||
|
java-version: 11
|
||||||
|
- uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.gradle/caches
|
||||||
|
~/.gradle/wrapper
|
||||||
|
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-gradle-
|
||||||
|
- name: Build Android Tests for vector
|
||||||
|
run: ./gradlew clean vector:assembleAndroidTest $CI_GRADLE_ARG_PROPERTIES --stacktrace
|
||||||
|
|
||||||
|
# Run Android Tests
|
||||||
|
integration-tests:
|
||||||
|
name: Matrix SDK - Running Integration Tests
|
||||||
|
runs-on: macos-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
api-level: [ 28 ]
|
||||||
|
# No concurrency required, runs every time on a schedule.
|
||||||
|
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@v3
|
||||||
|
with:
|
||||||
|
python-version: 3.8
|
||||||
|
- uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.gradle/caches
|
||||||
|
~/.gradle/wrapper
|
||||||
|
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-gradle-
|
||||||
|
- name: Start synapse server
|
||||||
|
run: |
|
||||||
|
pip install matrix-synapse
|
||||||
|
curl https://raw.githubusercontent.com/matrix-org/synapse/develop/demo/start.sh -o start.sh
|
||||||
|
chmod 777 start.sh
|
||||||
|
./start.sh --no-rate-limit
|
||||||
|
# package: org.matrix.android.sdk.session
|
||||||
|
- name: Run integration tests for Matrix SDK [org.matrix.android.sdk.session] API[${{ matrix.api-level }}]
|
||||||
|
uses: reactivecircus/android-emulator-runner@v2
|
||||||
|
with:
|
||||||
|
api-level: ${{ matrix.api-level }}
|
||||||
|
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: |
|
||||||
|
adb root
|
||||||
|
adb logcat -c
|
||||||
|
touch emulator-session.log
|
||||||
|
chmod 777 emulator-session.log
|
||||||
|
adb logcat >> emulator-session.log &
|
||||||
|
./gradlew $CI_GRADLE_ARG_PROPERTIES -Pandroid.testInstrumentationRunnerArguments.package='org.matrix.android.sdk.session' matrix-sdk-android:connectedDebugAndroidTest
|
||||||
|
- name: Read Results [org.matrix.android.sdk.session]
|
||||||
|
if: always()
|
||||||
|
id: get-comment-body-session
|
||||||
|
run: python3 ./tools/ci/render_test_output.py session ./matrix-sdk-android/build/outputs/androidTest-results/connected/*.xml
|
||||||
|
- name: Remove adb logcat
|
||||||
|
if: always()
|
||||||
|
run: pkill -9 adb
|
||||||
|
- name: Run integration tests for Matrix SDK [org.matrix.android.sdk.account] API[${{ matrix.api-level }}]
|
||||||
|
if: always()
|
||||||
|
uses: reactivecircus/android-emulator-runner@v2
|
||||||
|
with:
|
||||||
|
api-level: ${{ matrix.api-level }}
|
||||||
|
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: |
|
||||||
|
adb root
|
||||||
|
adb logcat -c
|
||||||
|
touch emulator-account.log
|
||||||
|
chmod 777 emulator-account.log
|
||||||
|
adb logcat >> emulator-account.log &
|
||||||
|
./gradlew $CI_GRADLE_ARG_PROPERTIES -Pandroid.testInstrumentationRunnerArguments.package='org.matrix.android.sdk.account' matrix-sdk-android:connectedDebugAndroidTest
|
||||||
|
- name: Read Results [org.matrix.android.sdk.account]
|
||||||
|
if: always()
|
||||||
|
id: get-comment-body-account
|
||||||
|
run: python3 ./tools/ci/render_test_output.py account ./matrix-sdk-android/build/outputs/androidTest-results/connected/*.xml
|
||||||
|
- name: Remove adb logcat
|
||||||
|
if: always()
|
||||||
|
run: pkill -9 adb
|
||||||
|
# package: org.matrix.android.sdk.internal
|
||||||
|
- name: Run integration tests for Matrix SDK [org.matrix.android.sdk.internal] API[${{ matrix.api-level }}]
|
||||||
|
if: always()
|
||||||
|
uses: reactivecircus/android-emulator-runner@v2
|
||||||
|
with:
|
||||||
|
api-level: ${{ matrix.api-level }}
|
||||||
|
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: |
|
||||||
|
adb root
|
||||||
|
adb logcat -c
|
||||||
|
touch emulator-internal.log
|
||||||
|
chmod 777 emulator-internal.log
|
||||||
|
adb logcat >> emulator-internal.log &
|
||||||
|
./gradlew $CI_GRADLE_ARG_PROPERTIES -Pandroid.testInstrumentationRunnerArguments.package='org.matrix.android.sdk.internal' matrix-sdk-android:connectedDebugAndroidTest
|
||||||
|
- name: Read Results [org.matrix.android.sdk.internal]
|
||||||
|
if: always()
|
||||||
|
id: get-comment-body-internal
|
||||||
|
run: python3 ./tools/ci/render_test_output.py internal ./matrix-sdk-android/build/outputs/androidTest-results/connected/*.xml
|
||||||
|
- name: Remove adb logcat
|
||||||
|
if: always()
|
||||||
|
run: pkill -9 adb
|
||||||
|
# package: org.matrix.android.sdk.ordering
|
||||||
|
- name: Run integration tests for Matrix SDK [org.matrix.android.sdk.ordering] API[${{ matrix.api-level }}]
|
||||||
|
if: always()
|
||||||
|
uses: reactivecircus/android-emulator-runner@v2
|
||||||
|
with:
|
||||||
|
api-level: ${{ matrix.api-level }}
|
||||||
|
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: |
|
||||||
|
adb root
|
||||||
|
adb logcat -c
|
||||||
|
touch emulator-ordering.log
|
||||||
|
chmod 777 emulator-ordering.log
|
||||||
|
adb logcat >> emulator-ordering.log &
|
||||||
|
./gradlew $CI_GRADLE_ARG_PROPERTIES -Pandroid.testInstrumentationRunnerArguments.package='org.matrix.android.sdk.ordering' matrix-sdk-android:connectedDebugAndroidTest
|
||||||
|
- name: Read Results [org.matrix.android.sdk.ordering]
|
||||||
|
if: always()
|
||||||
|
id: get-comment-body-ordering
|
||||||
|
run: python3 ./tools/ci/render_test_output.py ordering ./matrix-sdk-android/build/outputs/androidTest-results/connected/*.xml
|
||||||
|
- name: Remove adb logcat
|
||||||
|
if: always()
|
||||||
|
run: pkill -9 adb
|
||||||
|
# package: class PermalinkParserTest
|
||||||
|
- name: Run integration tests for Matrix SDK class [org.matrix.android.sdk.PermalinkParserTest] API[${{ matrix.api-level }}]
|
||||||
|
if: always()
|
||||||
|
uses: reactivecircus/android-emulator-runner@v2
|
||||||
|
with:
|
||||||
|
api-level: ${{ matrix.api-level }}
|
||||||
|
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: |
|
||||||
|
adb root
|
||||||
|
adb logcat -c
|
||||||
|
touch emulator-permalink.log
|
||||||
|
chmod 777 emulator-permalink.log
|
||||||
|
adb logcat >> emulator-permalink.log &
|
||||||
|
./gradlew $CI_GRADLE_ARG_PROPERTIES -Pandroid.testInstrumentationRunnerArguments.class='org.matrix.android.sdk.PermalinkParserTest' matrix-sdk-android:connectedDebugAndroidTest
|
||||||
|
- name: Read Results [org.matrix.android.sdk.PermalinkParserTest]
|
||||||
|
if: always()
|
||||||
|
id: get-comment-body-permalink
|
||||||
|
run: python3 ./tools/ci/render_test_output.py permalink ./matrix-sdk-android/build/outputs/androidTest-results/connected/*.xml
|
||||||
|
- name: Remove adb logcat
|
||||||
|
if: always()
|
||||||
|
run: pkill -9 adb
|
||||||
|
# package: class PermalinkParserTest
|
||||||
|
- name: Find Comment
|
||||||
|
if: always() && github.event_name == 'pull_request'
|
||||||
|
uses: peter-evans/find-comment@v1
|
||||||
|
id: fc
|
||||||
|
with:
|
||||||
|
issue-number: ${{ github.event.pull_request.number }}
|
||||||
|
comment-author: 'github-actions[bot]'
|
||||||
|
body-includes: Integration Tests Results
|
||||||
|
- name: Publish results to PR
|
||||||
|
if: always() && github.event_name == 'pull_request'
|
||||||
|
uses: peter-evans/create-or-update-comment@v1
|
||||||
|
with:
|
||||||
|
comment-id: ${{ steps.fc.outputs.comment-id }}
|
||||||
|
issue-number: ${{ github.event.pull_request.number }}
|
||||||
|
body: |
|
||||||
|
### Matrix SDK
|
||||||
|
## Integration Tests Results:
|
||||||
|
- `[org.matrix.android.sdk.session]`<br>${{ steps.get-comment-body-session.outputs.session }}
|
||||||
|
- `[org.matrix.android.sdk.account]`<br>${{ steps.get-comment-body-account.outputs.account }}
|
||||||
|
- `[org.matrix.android.sdk.internal]`<br>${{ steps.get-comment-body-internal.outputs.internal }}
|
||||||
|
- `[org.matrix.android.sdk.ordering]`<br>${{ steps.get-comment-body-ordering.outputs.ordering }}
|
||||||
|
- `[org.matrix.android.sdk.PermalinkParserTest]`<br>${{ steps.get-comment-body-permalink.outputs.permalink }}
|
||||||
|
edit-mode: replace
|
||||||
|
- name: Upload Test Report Log
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
if: always()
|
||||||
|
with:
|
||||||
|
name: integrationtest-error-results
|
||||||
|
path: |
|
||||||
|
emulator-permalink.log
|
||||||
|
emulator-internal.log
|
||||||
|
emulator-ordering.log
|
||||||
|
emulator-account.log
|
||||||
|
emulator-session.log
|
||||||
|
|
||||||
|
ui-tests:
|
||||||
|
name: UI Tests (Synapse)
|
||||||
|
runs-on: macos-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
api-level: [ 28 ]
|
||||||
|
# No concurrency required, runs every time on a schedule.
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
ref: develop
|
||||||
|
- name: Set up Python 3.8
|
||||||
|
uses: actions/setup-python@v3
|
||||||
|
with:
|
||||||
|
python-version: 3.8
|
||||||
|
- uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.gradle/caches
|
||||||
|
~/.gradle/wrapper
|
||||||
|
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-gradle-
|
||||||
|
- name: Start synapse server
|
||||||
|
run: |
|
||||||
|
pip install matrix-synapse
|
||||||
|
curl -sL https://raw.githubusercontent.com/matrix-org/synapse/develop/demo/start.sh \
|
||||||
|
| sed s/127.0.0.1/0.0.0.0/g | sed 's/http:\/\/localhost/http:\/\/10.0.2.2/g' | bash -s -- --no-rate-limit
|
||||||
|
- uses: actions/setup-java@v2
|
||||||
|
with:
|
||||||
|
distribution: 'adopt'
|
||||||
|
java-version: '11'
|
||||||
|
- name: Run sanity tests on API ${{ matrix.api-level }}
|
||||||
|
uses: reactivecircus/android-emulator-runner@v2
|
||||||
|
with:
|
||||||
|
api-level: ${{ matrix.api-level }}
|
||||||
|
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 # workaround to emulator bug: https://github.com/ReactiveCircus/android-emulator-runner/issues/160
|
||||||
|
script: |
|
||||||
|
adb root
|
||||||
|
adb logcat -c
|
||||||
|
touch emulator.log
|
||||||
|
chmod 777 emulator.log
|
||||||
|
adb logcat >> emulator.log &
|
||||||
|
./gradlew $CI_GRADLE_ARG_PROPERTIES -PallWarningsAsErrors=false connectedGplayDebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=im.vector.app.ui.UiAllScreensSanityTest || (adb pull storage/emulated/0/Pictures/failure_screenshots && exit 1 )
|
||||||
|
- name: Upload Test Report Log
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
if: always()
|
||||||
|
with:
|
||||||
|
name: uitest-error-results
|
||||||
|
path: |
|
||||||
|
emulator.log
|
||||||
|
failure_screenshots/
|
||||||
|
|
||||||
|
# Notify the channel about scheduled runs, do not notify for manually triggered runs
|
||||||
|
notify:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs:
|
||||||
|
- integration-tests
|
||||||
|
- ui-tests
|
||||||
|
if: always() && github.event_name != 'workflow_dispatch'
|
||||||
|
# No concurrency required, runs every time on a schedule.
|
||||||
|
steps:
|
||||||
|
- uses: michaelkaye/matrix-hookshot-action@v0.3.0
|
||||||
|
with:
|
||||||
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
matrix_access_token: ${{ secrets.ELEMENT_ANDROID_NOTIFICATION_ACCESS_TOKEN }}
|
||||||
|
matrix_room_id: ${{ secrets.ELEMENT_ANDROID_INTERNAL_ROOM_ID }}
|
||||||
|
text_template: "Nightly test run: {{#each job_statuses }}{{#with this }}{{#if completed }} {{name}} {{conclusion}} at {{completed_at}}, {{/if}}{{/with}}{{/each}}"
|
||||||
|
html_template: "Nightly test run results: {{#each job_statuses }}{{#with this }}{{#if completed }}<br />{{name}} {{conclusion}} at {{completed_at}} <a href=\"{{html_url}}\">[details]</a>{{/if}}{{/with}}{{/each}}"
|
12
.github/workflows/quality.yml
vendored
12
.github/workflows/quality.yml
vendored
@ -18,6 +18,10 @@ jobs:
|
|||||||
ktlint:
|
ktlint:
|
||||||
name: Kotlin Linter
|
name: Kotlin Linter
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
# Allow all jobs on main and develop. Just one per PR.
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.ref == 'refs/heads/main' && format('ktlint-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('ktlint-develop-{0}', github.sha) || format('ktlint-{0}', github.ref) }}
|
||||||
|
cancel-in-progress: true
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Run ktlint
|
- name: Run ktlint
|
||||||
@ -87,6 +91,10 @@ jobs:
|
|||||||
android-lint:
|
android-lint:
|
||||||
name: Android Linter
|
name: Android Linter
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
# Allow all jobs on main and develop. Just one per PR.
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.ref == 'refs/heads/main' && format('android-lint-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('android-lint-develop-{0}', github.sha) || format('android-lint-{0}', github.ref) }}
|
||||||
|
cancel-in-progress: true
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: actions/cache@v2
|
- uses: actions/cache@v2
|
||||||
@ -116,6 +124,10 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
target: [ Gplay, Fdroid ]
|
target: [ Gplay, Fdroid ]
|
||||||
|
# Allow all jobs on develop. Just one per PR.
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.ref == 'refs/heads/develop' && format('apk-lint-develop-{0}-{1}', matrix.target, github.sha) || format('apk-lint-{0}-{1}', matrix.target, github.ref) }}
|
||||||
|
cancel-in-progress: true
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: actions/cache@v2
|
- uses: actions/cache@v2
|
||||||
|
84
.github/workflows/sanity_test.yml
vendored
84
.github/workflows/sanity_test.yml
vendored
@ -1,84 +0,0 @@
|
|||||||
name: Sanity Test
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
# At 20:00 every day UTC
|
|
||||||
- cron: '0 20 * * *'
|
|
||||||
|
|
||||||
# Enrich gradle.properties for CI/CD
|
|
||||||
env:
|
|
||||||
CI_GRADLE_ARG_PROPERTIES: >
|
|
||||||
-Porg.gradle.jvmargs=-Xmx4g
|
|
||||||
-Porg.gradle.parallel=false
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
integration-tests:
|
|
||||||
name: Sanity Tests (Synapse)
|
|
||||||
runs-on: macos-latest
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
api-level: [ 28 ]
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
with:
|
|
||||||
ref: develop
|
|
||||||
- name: Set up Python 3.8
|
|
||||||
uses: actions/setup-python@v2
|
|
||||||
with:
|
|
||||||
python-version: 3.8
|
|
||||||
- uses: actions/cache@v2
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.gradle/caches
|
|
||||||
~/.gradle/wrapper
|
|
||||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-gradle-
|
|
||||||
- name: Start synapse server
|
|
||||||
run: |
|
|
||||||
pip install matrix-synapse
|
|
||||||
curl -sL https://raw.githubusercontent.com/matrix-org/synapse/develop/demo/start.sh \
|
|
||||||
| sed s/127.0.0.1/0.0.0.0/g | sed 's/http:\/\/localhost/http:\/\/10.0.2.2/g' | bash -s -- --no-rate-limit
|
|
||||||
- uses: actions/setup-java@v2
|
|
||||||
with:
|
|
||||||
distribution: 'adopt'
|
|
||||||
java-version: '11'
|
|
||||||
- name: Run sanity tests on API ${{ matrix.api-level }}
|
|
||||||
uses: reactivecircus/android-emulator-runner@v2
|
|
||||||
with:
|
|
||||||
api-level: ${{ matrix.api-level }}
|
|
||||||
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 # workaround to emulator bug: https://github.com/ReactiveCircus/android-emulator-runner/issues/160
|
|
||||||
script: |
|
|
||||||
adb root
|
|
||||||
adb logcat -c
|
|
||||||
touch emulator.log
|
|
||||||
chmod 777 emulator.log
|
|
||||||
adb logcat >> emulator.log &
|
|
||||||
./gradlew $CI_GRADLE_ARG_PROPERTIES -PallWarningsAsErrors=false connectedGplayDebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=im.vector.app.ui.UiAllScreensSanityTest || (adb pull storage/emulated/0/Pictures/failure_screenshots && exit 1 )
|
|
||||||
- name: Upload Test Report Log
|
|
||||||
uses: actions/upload-artifact@v2
|
|
||||||
if: always()
|
|
||||||
with:
|
|
||||||
name: sanity-error-results
|
|
||||||
path: |
|
|
||||||
emulator.log
|
|
||||||
failure_screenshots/
|
|
||||||
|
|
||||||
|
|
||||||
notify:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: integration-tests
|
|
||||||
if: always()
|
|
||||||
steps:
|
|
||||||
- uses: michaelkaye/matrix-hookshot-action@v0.2.0
|
|
||||||
with:
|
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
matrix_access_token: ${{ secrets.ELEMENT_ANDROID_NOTIFICATION_ACCESS_TOKEN }}
|
|
||||||
matrix_room_id: ${{ secrets.ELEMENT_ANDROID_INTERNAL_ROOM_ID }}
|
|
||||||
text_template: "Sanity test run: {{#each job_statuses }}{{#with this }}{{#if completed }} {{name}} {{conclusion}} at {{completed_at}} {{html_url}}{{/if}}{{/with}}{{/each}}"
|
|
||||||
html_template: "CI Sanity test run results: {{#each job_statuses }}{{#with this }}{{#if completed }} {{name}} {{conclusion}} at {{completed_at}} <a href=\"{{html_url}}\">[details]</a>{{/if}}{{/with}}{{/each}}"
|
|
@ -9,10 +9,11 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
# Skip in forks
|
# Skip in forks
|
||||||
if: github.repository == 'vector-im/element-android'
|
if: github.repository == 'vector-im/element-android'
|
||||||
|
# No concurrency required, runs every time on a schedule.
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Set up Python 3.8
|
- name: Set up Python 3.8
|
||||||
uses: actions/setup-python@v2
|
uses: actions/setup-python@v3
|
||||||
with:
|
with:
|
||||||
python-version: 3.8
|
python-version: 3.8
|
||||||
- name: Install Prerequisite dependencies
|
- name: Install Prerequisite dependencies
|
||||||
@ -35,10 +36,11 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
# Skip in forks
|
# Skip in forks
|
||||||
if: github.repository == 'vector-im/element-android'
|
if: github.repository == 'vector-im/element-android'
|
||||||
|
# No concurrency required, runs every time on a schedule.
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Set up Python 3.8
|
- name: Set up Python 3.8
|
||||||
uses: actions/setup-python@v2
|
uses: actions/setup-python@v3
|
||||||
with:
|
with:
|
||||||
python-version: 3.8
|
python-version: 3.8
|
||||||
- name: Install Prerequisite dependencies
|
- name: Install Prerequisite dependencies
|
||||||
@ -60,6 +62,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
# Skip in forks
|
# Skip in forks
|
||||||
if: github.repository == 'vector-im/element-android'
|
if: github.repository == 'vector-im/element-android'
|
||||||
|
# No concurrency required, runs every time on a schedule.
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Run analytics import script
|
- name: Run analytics import script
|
||||||
|
4
.github/workflows/tests.yml
vendored
4
.github/workflows/tests.yml
vendored
@ -15,6 +15,10 @@ jobs:
|
|||||||
unit-tests:
|
unit-tests:
|
||||||
name: Run Unit Tests
|
name: Run Unit Tests
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
# Allow all jobs on main and develop. Just one per PR.
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.ref == 'refs/heads/main' && format('unit-tests-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('unit-tests-develop-{0}', github.sha) || format('unit-tests-{0}', github.ref) }}
|
||||||
|
cancel-in-progress: true
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: actions/cache@v2
|
- uses: actions/cache@v2
|
||||||
|
@ -14,7 +14,7 @@ It is a total rewrite of [Riot-Android](https://github.com/vector-im/riot-androi
|
|||||||
[<img src="resources/img/google-play-badge.png" alt="Get it on Google Play" height="60">](https://play.google.com/store/apps/details?id=im.vector.app)
|
[<img src="resources/img/google-play-badge.png" alt="Get it on Google Play" height="60">](https://play.google.com/store/apps/details?id=im.vector.app)
|
||||||
[<img src="resources/img/f-droid-badge.png" alt="Get it on F-Droid" height="60">](https://f-droid.org/app/im.vector.app)
|
[<img src="resources/img/f-droid-badge.png" alt="Get it on F-Droid" height="60">](https://f-droid.org/app/im.vector.app)
|
||||||
|
|
||||||
Nightly build: [](https://buildkite.com/matrix-dot-org/element-android/builds?branch=develop) Nighly sanity test status: [](https://github.com/vector-im/element-android/actions/workflows/sanity_test.yml)
|
Nightly build: [](https://buildkite.com/matrix-dot-org/element-android/builds?branch=develop) Nighly test status: [](https://github.com/vector-im/element-android/actions/workflows/nightly.yml)
|
||||||
|
|
||||||
|
|
||||||
# New Android SDK
|
# New Android SDK
|
||||||
|
@ -19,8 +19,8 @@ buildscript {
|
|||||||
classpath libs.gradle.hiltPlugin
|
classpath libs.gradle.hiltPlugin
|
||||||
classpath 'com.google.gms:google-services:4.3.10'
|
classpath 'com.google.gms:google-services:4.3.10'
|
||||||
classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.3'
|
classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.3'
|
||||||
classpath 'com.google.android.gms:oss-licenses-plugin:0.10.4'
|
classpath 'com.google.android.gms:oss-licenses-plugin:0.10.5'
|
||||||
classpath "com.likethesalad.android:string-reference:1.2.2"
|
classpath "com.likethesalad.android:stem-plugin:2.0.0"
|
||||||
|
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
// in the individual module build.gradle files
|
// in the individual module build.gradle files
|
||||||
@ -144,11 +144,6 @@ project(":library:diff-match-patch") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Global configurations across all modules
|
|
||||||
ext {
|
|
||||||
isThreadingEnabled = true
|
|
||||||
}
|
|
||||||
|
|
||||||
//project(":matrix-sdk-android") {
|
//project(":matrix-sdk-android") {
|
||||||
// sonarqube {
|
// sonarqube {
|
||||||
// properties {
|
// properties {
|
||||||
|
1
changelog.d/4319.bugfix
Normal file
1
changelog.d/4319.bugfix
Normal file
@ -0,0 +1 @@
|
|||||||
|
Open direct message screen when clicking on DM button in the space members list
|
1
changelog.d/5005.feature
Normal file
1
changelog.d/5005.feature
Normal file
@ -0,0 +1 @@
|
|||||||
|
Add possibility to save media from Gallery + reorder choices in message context menu
|
1
changelog.d/5325.feature
Normal file
1
changelog.d/5325.feature
Normal file
@ -0,0 +1 @@
|
|||||||
|
Adds forceLoginFallback feature flag and usages to FTUE login and registration
|
1
changelog.d/5326.misc
Normal file
1
changelog.d/5326.misc
Normal file
@ -0,0 +1 @@
|
|||||||
|
[Export e2ee keys] use appName instead of element
|
1
changelog.d/5330.misc
Normal file
1
changelog.d/5330.misc
Normal file
@ -0,0 +1 @@
|
|||||||
|
Continue improving realm usage.
|
1
changelog.d/5330.sdk
Normal file
1
changelog.d/5330.sdk
Normal file
@ -0,0 +1 @@
|
|||||||
|
Change name of getTimeLineEvent and getTimeLineEventLive methods to getTimelineEvent and getTimelineEventLive.
|
1
changelog.d/5348.misc
Normal file
1
changelog.d/5348.misc
Normal file
@ -0,0 +1 @@
|
|||||||
|
Upgrade the plugin which generate strings with template from 1.2.2 to 2.0.0
|
1
changelog.d/5352.misc
Normal file
1
changelog.d/5352.misc
Normal file
@ -0,0 +1 @@
|
|||||||
|
Remove about 700 unused strings and their translations
|
1
changelog.d/5361.misc
Normal file
1
changelog.d/5361.misc
Normal file
@ -0,0 +1 @@
|
|||||||
|
Creates dedicated VectorOverrides for forcing behaviour for local testing/development
|
1
changelog.d/5379.misc
Normal file
1
changelog.d/5379.misc
Normal file
@ -0,0 +1 @@
|
|||||||
|
Cleanup unused threads build configurations
|
1
changelog.d/5392.misc
Normal file
1
changelog.d/5392.misc
Normal file
@ -0,0 +1 @@
|
|||||||
|
Upgrades material dependency version from 1.4.0 to 1.5.0
|
1
changelog.d/5394.bugfix
Normal file
1
changelog.d/5394.bugfix
Normal file
@ -0,0 +1 @@
|
|||||||
|
Fix incorrect media cache size in settings
|
@ -71,8 +71,7 @@ ext.libs = [
|
|||||||
'espressoIntents' : "androidx.test.espresso:espresso-intents:$espresso"
|
'espressoIntents' : "androidx.test.espresso:espresso-intents:$espresso"
|
||||||
],
|
],
|
||||||
google : [
|
google : [
|
||||||
// TODO There is 1.6.0?
|
'material' : "com.google.android.material:material:1.5.0"
|
||||||
'material' : "com.google.android.material:material:1.4.0"
|
|
||||||
],
|
],
|
||||||
dagger : [
|
dagger : [
|
||||||
'dagger' : "com.google.dagger:dagger:$dagger",
|
'dagger' : "com.google.dagger:dagger:$dagger",
|
||||||
|
@ -175,6 +175,7 @@ ext.groups = [
|
|||||||
'org.sonatype.oss',
|
'org.sonatype.oss',
|
||||||
'org.testng',
|
'org.testng',
|
||||||
'org.threeten',
|
'org.threeten',
|
||||||
|
'org.webjars',
|
||||||
'ru.noties',
|
'ru.noties',
|
||||||
'xerces',
|
'xerces',
|
||||||
'xml-apis',
|
'xml-apis',
|
||||||
|
2
fastlane/metadata/android/cs-CZ/changelogs/40104000.txt
Normal file
2
fastlane/metadata/android/cs-CZ/changelogs/40104000.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Hlavní změny v této verzi: Počáteční implementace vláken zpráv. Bubliny zpráv.
|
||||||
|
Úplný seznam změn: https://github.com/vector-im/element-android/releases/tag/v1.4.0
|
2
fastlane/metadata/android/cs-CZ/changelogs/40104020.txt
Normal file
2
fastlane/metadata/android/cs-CZ/changelogs/40104020.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Hlavní změny v této verzi: přidána podpora pro @room a tajné hlasování a mnoho dalších drobných změn
|
||||||
|
Úplný seznam změn: https://github.com/vector-im/element-android/releases/tag/v1.4.2
|
2
fastlane/metadata/android/et/changelogs/40104000.txt
Normal file
2
fastlane/metadata/android/et/changelogs/40104000.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Põhilised muutused selles versioonis: jutulõngade esmane lahendus ja jutumullid.
|
||||||
|
Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases/tag/v1.4.0
|
2
fastlane/metadata/android/et/changelogs/40104020.txt
Normal file
2
fastlane/metadata/android/et/changelogs/40104020.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Põhilised muutused selles versioonis: @jututuba tugi, mitteavalikud küsitlused ning pisiparandused.
|
||||||
|
Kogu ingliskeelne muudatuste logi: https://github.com/vector-im/element-android/releases/tag/v1.4.2
|
2
fastlane/metadata/android/hu-HU/changelogs/40104000.txt
Normal file
2
fastlane/metadata/android/hu-HU/changelogs/40104000.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Fő változás ebben a verzióban: Üzenetszálak kezdeti implementációja. Buborék üzenetek.
|
||||||
|
Teljes változásnapló: https://github.com/vector-im/element-android/releases/tag/v1.4.0
|
2
fastlane/metadata/android/hu-HU/changelogs/40104020.txt
Normal file
2
fastlane/metadata/android/hu-HU/changelogs/40104020.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Fő változás ebben a verzióban: @room támogatás és nem nyilvános szavazások mellett kisebb változtatások.
|
||||||
|
Teljes változásnapló: https://github.com/vector-im/element-android/releases/tag/v1.4.2
|
2
fastlane/metadata/android/id/changelogs/40104000.txt
Normal file
2
fastlane/metadata/android/id/changelogs/40104000.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Perubahan utama dalam versi ini: Implementasi awal perpesanan utasan. Gelembung pesan.
|
||||||
|
Catatan perubahan lanjutan: https://github.com/vector-im/element-android/releases/tag/v1.4.0
|
2
fastlane/metadata/android/id/changelogs/40104020.txt
Normal file
2
fastlane/metadata/android/id/changelogs/40104020.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Perubahan utama dalam versi ini: tambahkan dukungan untuk @room dan pemungutan suara tertutup dan banyak perubahan kecil lainnya.
|
||||||
|
Catatan perubahan lanjutan: https://github.com/vector-im/element-android/releases/tag/v1.4.2
|
2
fastlane/metadata/android/it-IT/changelogs/40104000.txt
Normal file
2
fastlane/metadata/android/it-IT/changelogs/40104000.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Modifiche principali in questa versione: prima implementazione delle discussioni. Messaggi a bolla.
|
||||||
|
Cronologia completa: https://github.com/vector-im/element-android/releases/tag/v1.4.0
|
2
fastlane/metadata/android/it-IT/changelogs/40104020.txt
Normal file
2
fastlane/metadata/android/it-IT/changelogs/40104020.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Modifiche principali in questa versione: aggiunto supporto a @stanza e sondaggi non divulgati, molte altre modifiche.
|
||||||
|
Cronologia completa: https://github.com/vector-im/element-android/releases/tag/v1.4.2
|
2
fastlane/metadata/android/pt-BR/changelogs/40104000.txt
Normal file
2
fastlane/metadata/android/pt-BR/changelogs/40104000.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Principais mudanças nesta versão: Implementação inicial de mensagens de thread. Bolhas de mensagem.
|
||||||
|
Changelog completo: https://github.com/vector-im/element-android/releases/tag/v1.4.0
|
2
fastlane/metadata/android/pt-BR/changelogs/40104020.txt
Normal file
2
fastlane/metadata/android/pt-BR/changelogs/40104020.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Principais mudanças nesta versão: adicionar suporte para @room e sondagens não-divulgadas entre muitas outras pequenas mudanças.
|
||||||
|
Changelog completo: https://github.com/vector-im/element-android/releases/tag/v1.4.2
|
2
fastlane/metadata/android/sk/changelogs/40104000.txt
Normal file
2
fastlane/metadata/android/sk/changelogs/40104000.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Hlavné zmeny v tejto verzii: Počiatočná implementácia správ vo vláknach. Správy v bublinách.
|
||||||
|
Úplný zoznam zmien: https://github.com/vector-im/element-android/releases/tag/v1.4.0
|
2
fastlane/metadata/android/sk/changelogs/40104020.txt
Normal file
2
fastlane/metadata/android/sk/changelogs/40104020.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Hlavné zmeny v tejto verzii: pridanie podpory pre @miestnost a nezverejnené ankety a mnoho ďalších drobných zmien.
|
||||||
|
Úplný zoznam zmien: https://github.com/vector-im/element-android/releases/tag/v1.4.2
|
2
fastlane/metadata/android/uk/changelogs/40104000.txt
Normal file
2
fastlane/metadata/android/uk/changelogs/40104000.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Основні зміни в цій версії: Початкова реалізація тредів повідомлень. Повідомлення бульбашки.
|
||||||
|
Вичерпний перелік змін: https://github.com/vector-im/element-android/releases/tag/v1.4.0
|
2
fastlane/metadata/android/uk/changelogs/40104020.txt
Normal file
2
fastlane/metadata/android/uk/changelogs/40104020.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Основні зміни в цій версії: додано підтримку до @room і нерозкритих опитувань та багато інших незначних змін.
|
||||||
|
Вичерпний перелік змін: https://github.com/vector-im/element-android/releases/tag/v1.4.2
|
2
fastlane/metadata/android/zh-TW/changelogs/40104000.txt
Normal file
2
fastlane/metadata/android/zh-TW/changelogs/40104000.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
此版本中的主要變動:訊息討論串的初始實作。訊息泡泡。
|
||||||
|
完整的變更紀錄:https://github.com/vector-im/element-android/releases/tag/v1.4.0
|
2
fastlane/metadata/android/zh-TW/changelogs/40104020.txt
Normal file
2
fastlane/metadata/android/zh-TW/changelogs/40104020.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
此版本中的主要變動:新增對 @room 的支援與未公開的投票,以及其他許多小變動。
|
||||||
|
完整的變更紀錄:https://github.com/vector-im/element-android/releases/tag/v1.4.2
|
@ -45,6 +45,8 @@ import kotlin.math.abs
|
|||||||
|
|
||||||
abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventListener {
|
abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventListener {
|
||||||
|
|
||||||
|
protected val rootView: View
|
||||||
|
get() = views.rootContainer
|
||||||
protected val pager2: ViewPager2
|
protected val pager2: ViewPager2
|
||||||
get() = views.attachmentPager
|
get() = views.attachmentPager
|
||||||
protected val imageTransitionView: ImageView
|
protected val imageTransitionView: ImageView
|
||||||
@ -298,10 +300,11 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
|
|||||||
|
|
||||||
private fun createSwipeToDismissHandler(): SwipeToDismissHandler =
|
private fun createSwipeToDismissHandler(): SwipeToDismissHandler =
|
||||||
SwipeToDismissHandler(
|
SwipeToDismissHandler(
|
||||||
swipeView = views.dismissContainer,
|
swipeView = views.dismissContainer,
|
||||||
shouldAnimateDismiss = { shouldAnimateDismiss() },
|
shouldAnimateDismiss = { shouldAnimateDismiss() },
|
||||||
onDismiss = { animateClose() },
|
onDismiss = { animateClose() },
|
||||||
onSwipeViewMove = ::handleSwipeViewMove)
|
onSwipeViewMove = ::handleSwipeViewMove
|
||||||
|
)
|
||||||
|
|
||||||
private fun createSwipeDirectionDetector() =
|
private fun createSwipeDirectionDetector() =
|
||||||
SwipeDirectionDetector(this) { swipeDirection = it }
|
SwipeDirectionDetector(this) { swipeDirection = it }
|
||||||
|
@ -59,9 +59,9 @@ class FlowRoom(private val room: Room) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun liveTimelineEvent(eventId: String): Flow<Optional<TimelineEvent>> {
|
fun liveTimelineEvent(eventId: String): Flow<Optional<TimelineEvent>> {
|
||||||
return room.getTimeLineEventLive(eventId).asFlow()
|
return room.getTimelineEventLive(eventId).asFlow()
|
||||||
.startWith(room.coroutineDispatchers.io) {
|
.startWith(room.coroutineDispatchers.io) {
|
||||||
room.getTimeLineEvent(eventId).toOptional()
|
room.getTimelineEvent(eventId).toOptional()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,8 +38,6 @@ android {
|
|||||||
resValue "string", "git_sdk_revision_unix_date", "\"${gitRevisionUnixDate()}\""
|
resValue "string", "git_sdk_revision_unix_date", "\"${gitRevisionUnixDate()}\""
|
||||||
resValue "string", "git_sdk_revision_date", "\"${gitRevisionDate()}\""
|
resValue "string", "git_sdk_revision_date", "\"${gitRevisionDate()}\""
|
||||||
|
|
||||||
// Indicates whether or not threading support is enabled
|
|
||||||
buildConfigField "Boolean", "THREADING_ENABLED", "${isThreadingEnabled}"
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
consumerProguardFiles 'proguard-rules.pro'
|
consumerProguardFiles 'proguard-rules.pro'
|
||||||
}
|
}
|
||||||
@ -169,7 +167,7 @@ dependencies {
|
|||||||
implementation libs.apache.commonsImaging
|
implementation libs.apache.commonsImaging
|
||||||
|
|
||||||
// Phone number https://github.com/google/libphonenumber
|
// Phone number https://github.com/google/libphonenumber
|
||||||
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.43'
|
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.44'
|
||||||
|
|
||||||
testImplementation libs.tests.junit
|
testImplementation libs.tests.junit
|
||||||
testImplementation 'org.robolectric:robolectric:4.7.3'
|
testImplementation 'org.robolectric:robolectric:4.7.3'
|
||||||
|
@ -95,7 +95,7 @@ class PreShareKeysTest : InstrumentedTest {
|
|||||||
assertEquals(megolmSessionId, sentEvent.root.content.toModel<EncryptedEventContent>()?.sessionId, "Unexpected megolm session")
|
assertEquals(megolmSessionId, sentEvent.root.content.toModel<EncryptedEventContent>()?.sessionId, "Unexpected megolm session")
|
||||||
testHelper.waitWithLatch { latch ->
|
testHelper.waitWithLatch { latch ->
|
||||||
testHelper.retryPeriodicallyWithLatch(latch) {
|
testHelper.retryPeriodicallyWithLatch(latch) {
|
||||||
bobSession.getRoom(e2eRoomID)?.getTimeLineEvent(sentEvent.eventId)?.root?.getClearType() == EventType.MESSAGE
|
bobSession.getRoom(e2eRoomID)?.getTimelineEvent(sentEvent.eventId)?.root?.getClearType() == EventType.MESSAGE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,7 +92,7 @@ class KeyShareTests : InstrumentedTest {
|
|||||||
|
|
||||||
val roomSecondSessionPOV = aliceSession2.getRoom(roomId)
|
val roomSecondSessionPOV = aliceSession2.getRoom(roomId)
|
||||||
|
|
||||||
val receivedEvent = roomSecondSessionPOV?.getTimeLineEvent(sentEventId)
|
val receivedEvent = roomSecondSessionPOV?.getTimelineEvent(sentEventId)
|
||||||
assertNotNull(receivedEvent)
|
assertNotNull(receivedEvent)
|
||||||
assert(receivedEvent!!.isEncrypted())
|
assert(receivedEvent!!.isEncrypted())
|
||||||
|
|
||||||
@ -382,7 +382,7 @@ class KeyShareTests : InstrumentedTest {
|
|||||||
commonTestHelper.sendTextMessage(roomAlicePov, "After", 1)
|
commonTestHelper.sendTextMessage(roomAlicePov, "After", 1)
|
||||||
|
|
||||||
val roomRoomBobPov = aliceSession.getRoom(roomId)
|
val roomRoomBobPov = aliceSession.getRoom(roomId)
|
||||||
val beforeJoin = roomRoomBobPov!!.getTimeLineEvent(secondEventId)
|
val beforeJoin = roomRoomBobPov!!.getTimelineEvent(secondEventId)
|
||||||
|
|
||||||
var dRes = tryOrNull { bobSession.cryptoService().decryptEvent(beforeJoin!!.root, "") }
|
var dRes = tryOrNull { bobSession.cryptoService().decryptEvent(beforeJoin!!.root, "") }
|
||||||
|
|
||||||
|
@ -80,11 +80,11 @@ class WithHeldTests : InstrumentedTest {
|
|||||||
// await for bob unverified session to get the message
|
// await for bob unverified session to get the message
|
||||||
testHelper.waitWithLatch { latch ->
|
testHelper.waitWithLatch { latch ->
|
||||||
testHelper.retryPeriodicallyWithLatch(latch) {
|
testHelper.retryPeriodicallyWithLatch(latch) {
|
||||||
bobUnverifiedSession.getRoom(roomId)?.getTimeLineEvent(timelineEvent.eventId) != null
|
bobUnverifiedSession.getRoom(roomId)?.getTimelineEvent(timelineEvent.eventId) != null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val eventBobPOV = bobUnverifiedSession.getRoom(roomId)?.getTimeLineEvent(timelineEvent.eventId)!!
|
val eventBobPOV = bobUnverifiedSession.getRoom(roomId)?.getTimelineEvent(timelineEvent.eventId)!!
|
||||||
|
|
||||||
// =============================
|
// =============================
|
||||||
// ASSERT
|
// ASSERT
|
||||||
@ -109,7 +109,7 @@ class WithHeldTests : InstrumentedTest {
|
|||||||
|
|
||||||
testHelper.waitWithLatch { latch ->
|
testHelper.waitWithLatch { latch ->
|
||||||
testHelper.retryPeriodicallyWithLatch(latch) {
|
testHelper.retryPeriodicallyWithLatch(latch) {
|
||||||
val ev = bobUnverifiedSession.getRoom(roomId)?.getTimeLineEvent(secondEvent.eventId)
|
val ev = bobUnverifiedSession.getRoom(roomId)?.getTimelineEvent(secondEvent.eventId)
|
||||||
// wait until it's decrypted
|
// wait until it's decrypted
|
||||||
ev?.root?.getClearType() == EventType.MESSAGE
|
ev?.root?.getClearType() == EventType.MESSAGE
|
||||||
}
|
}
|
||||||
@ -157,12 +157,12 @@ class WithHeldTests : InstrumentedTest {
|
|||||||
// await for bob session to get the message
|
// await for bob session to get the message
|
||||||
testHelper.waitWithLatch { latch ->
|
testHelper.waitWithLatch { latch ->
|
||||||
testHelper.retryPeriodicallyWithLatch(latch) {
|
testHelper.retryPeriodicallyWithLatch(latch) {
|
||||||
bobSession.getRoom(testData.roomId)?.getTimeLineEvent(eventId) != null
|
bobSession.getRoom(testData.roomId)?.getTimelineEvent(eventId) != null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Previous message should still be undecryptable (partially withheld session)
|
// Previous message should still be undecryptable (partially withheld session)
|
||||||
val eventBobPOV = bobSession.getRoom(testData.roomId)?.getTimeLineEvent(eventId)
|
val eventBobPOV = bobSession.getRoom(testData.roomId)?.getTimelineEvent(eventId)
|
||||||
try {
|
try {
|
||||||
// .. might need to wait a bit for stability?
|
// .. might need to wait a bit for stability?
|
||||||
bobSession.cryptoService().decryptEvent(eventBobPOV!!.root, "")
|
bobSession.cryptoService().decryptEvent(eventBobPOV!!.root, "")
|
||||||
@ -190,7 +190,7 @@ class WithHeldTests : InstrumentedTest {
|
|||||||
// await for bob SecondSession session to get the message
|
// await for bob SecondSession session to get the message
|
||||||
testHelper.waitWithLatch { latch ->
|
testHelper.waitWithLatch { latch ->
|
||||||
testHelper.retryPeriodicallyWithLatch(latch) {
|
testHelper.retryPeriodicallyWithLatch(latch) {
|
||||||
bobSecondSession.getRoom(testData.roomId)?.getTimeLineEvent(secondMessageId) != null
|
bobSecondSession.getRoom(testData.roomId)?.getTimelineEvent(secondMessageId) != null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,7 +231,7 @@ class WithHeldTests : InstrumentedTest {
|
|||||||
// await for bob SecondSession session to get the message
|
// await for bob SecondSession session to get the message
|
||||||
testHelper.waitWithLatch { latch ->
|
testHelper.waitWithLatch { latch ->
|
||||||
testHelper.retryPeriodicallyWithLatch(latch) {
|
testHelper.retryPeriodicallyWithLatch(latch) {
|
||||||
val timeLineEvent = bobSecondSession.getRoom(testData.roomId)?.getTimeLineEvent(eventId)?.also {
|
val timeLineEvent = bobSecondSession.getRoom(testData.roomId)?.getTimelineEvent(eventId)?.also {
|
||||||
// try to decrypt and force key request
|
// try to decrypt and force key request
|
||||||
tryOrNull { bobSecondSession.cryptoService().decryptEvent(it.root, "") }
|
tryOrNull { bobSecondSession.cryptoService().decryptEvent(it.root, "") }
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ import org.junit.Assert.assertEquals
|
|||||||
import org.junit.Assert.assertTrue
|
import org.junit.Assert.assertTrue
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.FixMethodOrder
|
import org.junit.FixMethodOrder
|
||||||
|
import org.junit.Ignore
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
import org.junit.runners.MethodSorters
|
import org.junit.runners.MethodSorters
|
||||||
@ -31,6 +32,7 @@ import org.matrix.android.sdk.common.assertByteArrayNotEqual
|
|||||||
import org.matrix.olm.OlmManager
|
import org.matrix.olm.OlmManager
|
||||||
import org.matrix.olm.OlmPkDecryption
|
import org.matrix.olm.OlmPkDecryption
|
||||||
|
|
||||||
|
@Ignore("Ignored in order to speed up test run time")
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
@FixMethodOrder(MethodSorters.JVM)
|
@FixMethodOrder(MethodSorters.JVM)
|
||||||
class KeysBackupPasswordTest : InstrumentedTest {
|
class KeysBackupPasswordTest : InstrumentedTest {
|
||||||
|
@ -117,5 +117,5 @@ interface FileService {
|
|||||||
/**
|
/**
|
||||||
* Get size of cached files
|
* Get size of cached files
|
||||||
*/
|
*/
|
||||||
fun getCacheSize(): Int
|
fun getCacheSize(): Long
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ interface TimelineService {
|
|||||||
* At the opposite of getTimeLineEventLive which will be updated when local echo event is synced, it will return null in this case.
|
* At the opposite of getTimeLineEventLive which will be updated when local echo event is synced, it will return null in this case.
|
||||||
* @param eventId the eventId to get the TimelineEvent
|
* @param eventId the eventId to get the TimelineEvent
|
||||||
*/
|
*/
|
||||||
fun getTimeLineEvent(eventId: String): TimelineEvent?
|
fun getTimelineEvent(eventId: String): TimelineEvent?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a LiveData of Optional TimelineEvent event with eventId.
|
* Creates a LiveData of Optional TimelineEvent event with eventId.
|
||||||
@ -49,7 +49,7 @@ interface TimelineService {
|
|||||||
* In this case, makes sure to use the new synced eventId from the TimelineEvent class if you want to interact, as the local echo is removed from the SDK.
|
* In this case, makes sure to use the new synced eventId from the TimelineEvent class if you want to interact, as the local echo is removed from the SDK.
|
||||||
* @param eventId the eventId to listen for TimelineEvent
|
* @param eventId the eventId to listen for TimelineEvent
|
||||||
*/
|
*/
|
||||||
fun getTimeLineEventLive(eventId: String): LiveData<Optional<TimelineEvent>>
|
fun getTimelineEventLive(eventId: String): LiveData<Optional<TimelineEvent>>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a snapshot list of TimelineEvent with EventType.MESSAGE and MessageType.MSGTYPE_IMAGE or MessageType.MSGTYPE_VIDEO.
|
* Returns a snapshot list of TimelineEvent with EventType.MESSAGE and MessageType.MSGTYPE_IMAGE or MessageType.MSGTYPE_VIDEO.
|
||||||
|
@ -16,8 +16,11 @@
|
|||||||
|
|
||||||
package org.matrix.android.sdk.internal.database.mapper
|
package org.matrix.android.sdk.internal.database.mapper
|
||||||
|
|
||||||
|
import io.realm.Realm
|
||||||
|
import io.realm.RealmList
|
||||||
import org.matrix.android.sdk.api.session.room.model.ReadReceipt
|
import org.matrix.android.sdk.api.session.room.model.ReadReceipt
|
||||||
import org.matrix.android.sdk.internal.database.RealmSessionProvider
|
import org.matrix.android.sdk.internal.database.RealmSessionProvider
|
||||||
|
import org.matrix.android.sdk.internal.database.model.ReadReceiptEntity
|
||||||
import org.matrix.android.sdk.internal.database.model.ReadReceiptsSummaryEntity
|
import org.matrix.android.sdk.internal.database.model.ReadReceiptsSummaryEntity
|
||||||
import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntity
|
import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntity
|
||||||
import org.matrix.android.sdk.internal.database.query.where
|
import org.matrix.android.sdk.internal.database.query.where
|
||||||
@ -32,14 +35,22 @@ internal class ReadReceiptsSummaryMapper @Inject constructor(
|
|||||||
return emptyList()
|
return emptyList()
|
||||||
}
|
}
|
||||||
val readReceipts = readReceiptsSummaryEntity.readReceipts
|
val readReceipts = readReceiptsSummaryEntity.readReceipts
|
||||||
|
// Avoid opening a new realm if we already have one opened
|
||||||
return realmSessionProvider.withRealm { realm ->
|
return if (readReceiptsSummaryEntity.isManaged) {
|
||||||
readReceipts
|
map(readReceipts, readReceiptsSummaryEntity.realm)
|
||||||
.mapNotNull {
|
} else {
|
||||||
val roomMember = RoomMemberSummaryEntity.where(realm, roomId = it.roomId, userId = it.userId).findFirst()
|
realmSessionProvider.withRealm { realm ->
|
||||||
?: return@mapNotNull null
|
map(readReceipts, realm)
|
||||||
ReadReceipt(roomMember.asDomain(), it.originServerTs.toLong())
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun map(readReceipts: RealmList<ReadReceiptEntity>, realm: Realm): List<ReadReceipt> {
|
||||||
|
return readReceipts
|
||||||
|
.mapNotNull {
|
||||||
|
val roomMember = RoomMemberSummaryEntity.where(realm, roomId = it.roomId, userId = it.userId).findFirst()
|
||||||
|
?: return@mapNotNull null
|
||||||
|
ReadReceipt(roomMember.asDomain(), it.originServerTs.toLong())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -323,13 +323,13 @@ internal class DefaultFileService @Inject constructor(
|
|||||||
return FileProvider.getUriForFile(context, authority, targetFile)
|
return FileProvider.getUriForFile(context, authority, targetFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getCacheSize(): Int {
|
override fun getCacheSize(): Long {
|
||||||
return downloadFolder.walkTopDown()
|
return downloadFolder.walkTopDown()
|
||||||
.onEnter {
|
.onEnter {
|
||||||
Timber.v("Get size of ${it.absolutePath}")
|
Timber.v("Get size of ${it.absolutePath}")
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
.sumOf { it.length().toInt() }
|
.sumOf { it.length() }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun clearCache() {
|
override fun clearCache() {
|
||||||
|
@ -529,7 +529,7 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
|
|||||||
|
|
||||||
private fun getPollEvent(roomId: String, eventId: String): TimelineEvent? {
|
private fun getPollEvent(roomId: String, eventId: String): TimelineEvent? {
|
||||||
val session = sessionManager.getSessionComponent(sessionId)?.session()
|
val session = sessionManager.getSessionComponent(sessionId)?.session()
|
||||||
return session?.getRoom(roomId)?.getTimeLineEvent(eventId) ?: return null.also {
|
return session?.getRoom(roomId)?.getTimelineEvent(eventId) ?: return null.also {
|
||||||
Timber.v("## POLL target poll event $eventId not found in room $roomId")
|
Timber.v("## POLL target poll event $eventId not found in room $roomId")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,14 +30,13 @@ import org.matrix.android.sdk.api.util.Cancelable
|
|||||||
import org.matrix.android.sdk.api.util.NoOpCancellable
|
import org.matrix.android.sdk.api.util.NoOpCancellable
|
||||||
import org.matrix.android.sdk.api.util.Optional
|
import org.matrix.android.sdk.api.util.Optional
|
||||||
import org.matrix.android.sdk.api.util.toOptional
|
import org.matrix.android.sdk.api.util.toOptional
|
||||||
import org.matrix.android.sdk.internal.database.mapper.TimelineEventMapper
|
|
||||||
import org.matrix.android.sdk.internal.database.mapper.asDomain
|
import org.matrix.android.sdk.internal.database.mapper.asDomain
|
||||||
import org.matrix.android.sdk.internal.database.model.EventAnnotationsSummaryEntity
|
import org.matrix.android.sdk.internal.database.model.EventAnnotationsSummaryEntity
|
||||||
import org.matrix.android.sdk.internal.database.model.TimelineEventEntity
|
|
||||||
import org.matrix.android.sdk.internal.database.query.where
|
import org.matrix.android.sdk.internal.database.query.where
|
||||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||||
import org.matrix.android.sdk.internal.session.room.send.LocalEchoEventFactory
|
import org.matrix.android.sdk.internal.session.room.send.LocalEchoEventFactory
|
||||||
import org.matrix.android.sdk.internal.session.room.send.queue.EventSenderProcessor
|
import org.matrix.android.sdk.internal.session.room.send.queue.EventSenderProcessor
|
||||||
|
import org.matrix.android.sdk.internal.session.room.timeline.TimelineEventDataSource
|
||||||
import org.matrix.android.sdk.internal.util.fetchCopyMap
|
import org.matrix.android.sdk.internal.util.fetchCopyMap
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
@ -48,7 +47,7 @@ internal class DefaultRelationService @AssistedInject constructor(
|
|||||||
private val eventFactory: LocalEchoEventFactory,
|
private val eventFactory: LocalEchoEventFactory,
|
||||||
private val findReactionEventForUndoTask: FindReactionEventForUndoTask,
|
private val findReactionEventForUndoTask: FindReactionEventForUndoTask,
|
||||||
private val fetchEditHistoryTask: FetchEditHistoryTask,
|
private val fetchEditHistoryTask: FetchEditHistoryTask,
|
||||||
private val timelineEventMapper: TimelineEventMapper,
|
private val timelineEventDataSource: TimelineEventDataSource,
|
||||||
@SessionDatabase private val monarchy: Monarchy
|
@SessionDatabase private val monarchy: Monarchy
|
||||||
) : RelationService {
|
) : RelationService {
|
||||||
|
|
||||||
@ -58,14 +57,8 @@ internal class DefaultRelationService @AssistedInject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun sendReaction(targetEventId: String, reaction: String): Cancelable {
|
override fun sendReaction(targetEventId: String, reaction: String): Cancelable {
|
||||||
return if (monarchy
|
val targetTimelineEvent = timelineEventDataSource.getTimelineEvent(roomId, targetEventId)
|
||||||
.fetchCopyMap(
|
return if (targetTimelineEvent
|
||||||
{ realm ->
|
|
||||||
TimelineEventEntity.where(realm, roomId, targetEventId).findFirst()
|
|
||||||
},
|
|
||||||
{ entity, _ ->
|
|
||||||
timelineEventMapper.map(entity)
|
|
||||||
})
|
|
||||||
?.annotations
|
?.annotations
|
||||||
?.reactionsSummary
|
?.reactionsSummary
|
||||||
.orEmpty()
|
.orEmpty()
|
||||||
|
@ -21,37 +21,24 @@ import com.zhuinden.monarchy.Monarchy
|
|||||||
import dagger.assisted.Assisted
|
import dagger.assisted.Assisted
|
||||||
import dagger.assisted.AssistedFactory
|
import dagger.assisted.AssistedFactory
|
||||||
import dagger.assisted.AssistedInject
|
import dagger.assisted.AssistedInject
|
||||||
import io.realm.Sort
|
|
||||||
import io.realm.kotlin.where
|
|
||||||
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
|
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
|
||||||
import org.matrix.android.sdk.api.session.events.model.isImageMessage
|
|
||||||
import org.matrix.android.sdk.api.session.events.model.isVideoMessage
|
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.Timeline
|
import org.matrix.android.sdk.api.session.room.timeline.Timeline
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineService
|
import org.matrix.android.sdk.api.session.room.timeline.TimelineService
|
||||||
import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
|
import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
|
||||||
import org.matrix.android.sdk.api.util.Optional
|
import org.matrix.android.sdk.api.util.Optional
|
||||||
import org.matrix.android.sdk.internal.database.RealmSessionProvider
|
|
||||||
import org.matrix.android.sdk.internal.database.lightweight.LightweightSettingsStorage
|
import org.matrix.android.sdk.internal.database.lightweight.LightweightSettingsStorage
|
||||||
import org.matrix.android.sdk.internal.database.mapper.TimelineEventMapper
|
import org.matrix.android.sdk.internal.database.mapper.TimelineEventMapper
|
||||||
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.query.where
|
|
||||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||||
import org.matrix.android.sdk.internal.di.UserId
|
|
||||||
import org.matrix.android.sdk.internal.session.room.membership.LoadRoomMembersTask
|
import org.matrix.android.sdk.internal.session.room.membership.LoadRoomMembersTask
|
||||||
import org.matrix.android.sdk.internal.session.room.relation.threads.FetchThreadTimelineTask
|
import org.matrix.android.sdk.internal.session.room.relation.threads.FetchThreadTimelineTask
|
||||||
import org.matrix.android.sdk.internal.session.sync.handler.room.ReadReceiptHandler
|
import org.matrix.android.sdk.internal.session.sync.handler.room.ReadReceiptHandler
|
||||||
import org.matrix.android.sdk.internal.session.sync.handler.room.ThreadsAwarenessHandler
|
import org.matrix.android.sdk.internal.session.sync.handler.room.ThreadsAwarenessHandler
|
||||||
import org.matrix.android.sdk.internal.task.TaskExecutor
|
|
||||||
|
|
||||||
internal class DefaultTimelineService @AssistedInject constructor(
|
internal class DefaultTimelineService @AssistedInject constructor(
|
||||||
@Assisted private val roomId: String,
|
@Assisted private val roomId: String,
|
||||||
@UserId private val userId: String,
|
|
||||||
@SessionDatabase private val monarchy: Monarchy,
|
@SessionDatabase private val monarchy: Monarchy,
|
||||||
private val realmSessionProvider: RealmSessionProvider,
|
|
||||||
private val timelineInput: TimelineInput,
|
private val timelineInput: TimelineInput,
|
||||||
private val taskExecutor: TaskExecutor,
|
|
||||||
private val contextOfEventTask: GetContextOfEventTask,
|
private val contextOfEventTask: GetContextOfEventTask,
|
||||||
private val eventDecryptor: TimelineEventDecryptor,
|
private val eventDecryptor: TimelineEventDecryptor,
|
||||||
private val paginationTask: PaginationTask,
|
private val paginationTask: PaginationTask,
|
||||||
@ -62,7 +49,8 @@ internal class DefaultTimelineService @AssistedInject constructor(
|
|||||||
private val threadsAwarenessHandler: ThreadsAwarenessHandler,
|
private val threadsAwarenessHandler: ThreadsAwarenessHandler,
|
||||||
private val lightweightSettingsStorage: LightweightSettingsStorage,
|
private val lightweightSettingsStorage: LightweightSettingsStorage,
|
||||||
private val readReceiptHandler: ReadReceiptHandler,
|
private val readReceiptHandler: ReadReceiptHandler,
|
||||||
private val coroutineDispatchers: MatrixCoroutineDispatchers
|
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||||
|
private val timelineEventDataSource: TimelineEventDataSource
|
||||||
) : TimelineService {
|
) : TimelineService {
|
||||||
|
|
||||||
@AssistedFactory
|
@AssistedFactory
|
||||||
@ -91,27 +79,15 @@ internal class DefaultTimelineService @AssistedInject constructor(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getTimeLineEvent(eventId: String): TimelineEvent? {
|
override fun getTimelineEvent(eventId: String): TimelineEvent? {
|
||||||
return realmSessionProvider.withRealm { realm ->
|
return timelineEventDataSource.getTimelineEvent(roomId, eventId)
|
||||||
TimelineEventEntity.where(realm, roomId = roomId, eventId = eventId).findFirst()?.let {
|
|
||||||
timelineEventMapper.map(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getTimeLineEventLive(eventId: String): LiveData<Optional<TimelineEvent>> {
|
override fun getTimelineEventLive(eventId: String): LiveData<Optional<TimelineEvent>> {
|
||||||
return LiveTimelineEvent(monarchy, taskExecutor.executorScope, timelineEventMapper, roomId, eventId)
|
return timelineEventDataSource.getTimelineEventLive(roomId, eventId)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getAttachmentMessages(): List<TimelineEvent> {
|
override fun getAttachmentMessages(): List<TimelineEvent> {
|
||||||
// TODO pretty bad query.. maybe we should denormalize clear type in base?
|
return timelineEventDataSource.getAttachmentMessages(roomId)
|
||||||
return realmSessionProvider.withRealm { realm ->
|
|
||||||
realm.where<TimelineEventEntity>()
|
|
||||||
.equalTo(TimelineEventEntityFields.ROOM_ID, roomId)
|
|
||||||
.sort(TimelineEventEntityFields.DISPLAY_INDEX, Sort.ASCENDING)
|
|
||||||
.findAll()
|
|
||||||
?.mapNotNull { timelineEventMapper.map(it).takeIf { it.root.isImageMessage() || it.root.isVideoMessage() } }
|
|
||||||
.orEmpty()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,7 @@ internal class RealmSendingEventsDataSource(
|
|||||||
|
|
||||||
private val sendingTimelineEventsListener = RealmChangeListener<RealmList<TimelineEventEntity>> { events ->
|
private val sendingTimelineEventsListener = RealmChangeListener<RealmList<TimelineEventEntity>> { events ->
|
||||||
uiEchoManager.onSentEventsInDatabase(events.map { it.eventId })
|
uiEchoManager.onSentEventsInDatabase(events.map { it.eventId })
|
||||||
frozenSendingTimelineEvents = sendingTimelineEvents?.freeze()
|
updateFrozenResults(events)
|
||||||
onEventsUpdated(false)
|
onEventsUpdated(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,10 +59,17 @@ internal class RealmSendingEventsDataSource(
|
|||||||
|
|
||||||
override fun stop() {
|
override fun stop() {
|
||||||
sendingTimelineEvents?.removeChangeListener(sendingTimelineEventsListener)
|
sendingTimelineEvents?.removeChangeListener(sendingTimelineEventsListener)
|
||||||
|
updateFrozenResults(null)
|
||||||
sendingTimelineEvents = null
|
sendingTimelineEvents = null
|
||||||
roomEntity = null
|
roomEntity = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun updateFrozenResults(sendingEvents: RealmList<TimelineEventEntity>?) {
|
||||||
|
// Makes sure to close the previous frozen realm
|
||||||
|
frozenSendingTimelineEvents?.realm?.close()
|
||||||
|
frozenSendingTimelineEvents = sendingEvents?.freeze()
|
||||||
|
}
|
||||||
|
|
||||||
override fun buildSendingEvents(): List<TimelineEvent> {
|
override fun buildSendingEvents(): List<TimelineEvent> {
|
||||||
val builtSendingEvents = mutableListOf<TimelineEvent>()
|
val builtSendingEvents = mutableListOf<TimelineEvent>()
|
||||||
uiEchoManager.getInMemorySendingEvents()
|
uiEchoManager.getInMemorySendingEvents()
|
||||||
|
@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.matrix.android.sdk.internal.session.room.timeline
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import com.zhuinden.monarchy.Monarchy
|
||||||
|
import io.realm.Sort
|
||||||
|
import io.realm.kotlin.where
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.isImageMessage
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.isVideoMessage
|
||||||
|
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
|
||||||
|
import org.matrix.android.sdk.api.util.Optional
|
||||||
|
import org.matrix.android.sdk.internal.database.RealmSessionProvider
|
||||||
|
import org.matrix.android.sdk.internal.database.mapper.TimelineEventMapper
|
||||||
|
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.query.where
|
||||||
|
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||||
|
import org.matrix.android.sdk.internal.task.TaskExecutor
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
internal class TimelineEventDataSource @Inject constructor(private val realmSessionProvider: RealmSessionProvider,
|
||||||
|
private val timelineEventMapper: TimelineEventMapper,
|
||||||
|
private val taskExecutor: TaskExecutor,
|
||||||
|
@SessionDatabase private val monarchy: Monarchy) {
|
||||||
|
|
||||||
|
fun getTimelineEvent(roomId: String, eventId: String): TimelineEvent? {
|
||||||
|
return realmSessionProvider.withRealm { realm ->
|
||||||
|
TimelineEventEntity.where(realm, roomId = roomId, eventId = eventId).findFirst()?.let {
|
||||||
|
timelineEventMapper.map(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getTimelineEventLive(roomId: String, eventId: String): LiveData<Optional<TimelineEvent>> {
|
||||||
|
return LiveTimelineEvent(monarchy, taskExecutor.executorScope, timelineEventMapper, roomId, eventId)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getAttachmentMessages(roomId: String): List<TimelineEvent> {
|
||||||
|
// TODO pretty bad query.. maybe we should denormalize clear type in base?
|
||||||
|
return realmSessionProvider.withRealm { realm ->
|
||||||
|
realm.where<TimelineEventEntity>()
|
||||||
|
.equalTo(TimelineEventEntityFields.ROOM_ID, roomId)
|
||||||
|
.sort(TimelineEventEntityFields.DISPLAY_INDEX, Sort.ASCENDING)
|
||||||
|
.findAll()
|
||||||
|
?.mapNotNull { timelineEventMapper.map(it).takeIf { it.root.isImageMessage() || it.root.isVideoMessage() } }
|
||||||
|
.orEmpty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -27,7 +27,6 @@ import org.matrix.android.sdk.internal.di.WorkManagerProvider
|
|||||||
import org.matrix.android.sdk.internal.session.SessionComponent
|
import org.matrix.android.sdk.internal.session.SessionComponent
|
||||||
import org.matrix.android.sdk.internal.session.sync.SyncPresence
|
import org.matrix.android.sdk.internal.session.sync.SyncPresence
|
||||||
import org.matrix.android.sdk.internal.session.sync.SyncTask
|
import org.matrix.android.sdk.internal.session.sync.SyncTask
|
||||||
import org.matrix.android.sdk.internal.task.TaskExecutor
|
|
||||||
import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker
|
import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker
|
||||||
import org.matrix.android.sdk.internal.worker.SessionWorkerParams
|
import org.matrix.android.sdk.internal.worker.SessionWorkerParams
|
||||||
import org.matrix.android.sdk.internal.worker.WorkerParamsFactory
|
import org.matrix.android.sdk.internal.worker.WorkerParamsFactory
|
||||||
@ -58,7 +57,6 @@ internal class SyncWorker(context: Context, workerParameters: WorkerParameters,
|
|||||||
) : SessionWorkerParams
|
) : SessionWorkerParams
|
||||||
|
|
||||||
@Inject lateinit var syncTask: SyncTask
|
@Inject lateinit var syncTask: SyncTask
|
||||||
@Inject lateinit var taskExecutor: TaskExecutor
|
|
||||||
@Inject lateinit var workManagerProvider: WorkManagerProvider
|
@Inject lateinit var workManagerProvider: WorkManagerProvider
|
||||||
|
|
||||||
override fun injectWith(injector: SessionComponent) {
|
override fun injectWith(injector: SessionComponent) {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
include ':vector'
|
include ':vector'
|
||||||
|
include ':vector-config'
|
||||||
include ':matrix-sdk-android'
|
include ':matrix-sdk-android'
|
||||||
include ':library:core-utils'
|
include ':library:core-utils'
|
||||||
include ':library:ui-styles'
|
include ':library:ui-styles'
|
||||||
|
45
tools/ci/render_test_output.py
Executable file
45
tools/ci/render_test_output.py
Executable file
@ -0,0 +1,45 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# Renders a list of xml files taken as arguments into GHA-styled messages in groups.
|
||||||
|
# Explicitly aims not to have any dependencies, to reduce installation load.
|
||||||
|
# Should just be able to run in the context of your standard github runner.
|
||||||
|
|
||||||
|
# Potentially rewrite as an independent action, use handlebars to template result
|
||||||
|
import sys
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
suitename = sys.argv[1]
|
||||||
|
xmlfiles = sys.argv[2:]
|
||||||
|
|
||||||
|
print(f"Arguments: {sys.argv}")
|
||||||
|
|
||||||
|
for xmlfile in xmlfiles:
|
||||||
|
print(f"Handling: {xmlfile}")
|
||||||
|
tree = ET.parse(xmlfile)
|
||||||
|
|
||||||
|
root = tree.getroot()
|
||||||
|
name = root.attrib['name']
|
||||||
|
time = root.attrib['time']
|
||||||
|
tests = int(root.attrib['tests'])
|
||||||
|
skipped = int(root.attrib['skipped'])
|
||||||
|
errors = int(root.attrib['errors'])
|
||||||
|
failures = int(root.attrib['failures'])
|
||||||
|
success = tests - failures - errors - skipped
|
||||||
|
total = tests - skipped
|
||||||
|
print(f"::group::{name} {success}/{total} ({skipped} skipped) in {time}")
|
||||||
|
|
||||||
|
for testcase in root:
|
||||||
|
if testcase.tag != "testcase":
|
||||||
|
continue
|
||||||
|
testname = testcase.attrib['classname']
|
||||||
|
message = testcase.attrib['name']
|
||||||
|
time = testcase.attrib['time']
|
||||||
|
child = testcase.find("failure")
|
||||||
|
if child is None:
|
||||||
|
print(f"{message} in {time}s")
|
||||||
|
else:
|
||||||
|
print(f"::error file={testname}::{message} in {time}s")
|
||||||
|
print(child.text)
|
||||||
|
body = f"passed={success} failures={failures} errors={errors} skipped={skipped}"
|
||||||
|
print(f"::set-output name={suitename}::={body}")
|
||||||
|
print("::endgroup::")
|
||||||
|
|
1
vector-config/.gitignore
vendored
Normal file
1
vector-config/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/build
|
20
vector-config/build.gradle
Normal file
20
vector-config/build.gradle
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
plugins {
|
||||||
|
id 'com.android.library'
|
||||||
|
id 'org.jetbrains.kotlin.android'
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdk versions.compileSdk
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdk versions.minSdk
|
||||||
|
targetSdk versions.targetSdk
|
||||||
|
}
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility versions.sourceCompat
|
||||||
|
targetCompatibility versions.targetCompat
|
||||||
|
}
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = "11"
|
||||||
|
}
|
||||||
|
}
|
2
vector-config/src/main/AndroidManifest.xml
Normal file
2
vector-config/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest package="im.vector.app.config" />
|
47
vector-config/src/main/res/values/config-settings.xml
Executable file
47
vector-config/src/main/res/values/config-settings.xml
Executable file
@ -0,0 +1,47 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
<!-- This file contains values to show or hide some settings, and default values for some settings
|
||||||
|
- boolean keys ending with "_visible" set the visibility of the setting
|
||||||
|
- boolean keys ending with "_default" set the default value of the setting
|
||||||
|
When a setting is hidden, the default value still applies
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!-- Level 0: Root -->
|
||||||
|
|
||||||
|
<bool name="settings_root_general_visible">true</bool>
|
||||||
|
<bool name="settings_root_notification_visible">true</bool>
|
||||||
|
<bool name="settings_root_preferences_visible">true</bool>
|
||||||
|
<bool name="settings_root_voice_video_visible">true</bool>
|
||||||
|
<bool name="settings_root_ignored_users_visible">true</bool>
|
||||||
|
<bool name="settings_root_security_privacy_visible">true</bool>
|
||||||
|
<bool name="settings_root_labs_visible">true</bool>
|
||||||
|
<bool name="settings_root_advanced_visible">true</bool>
|
||||||
|
<bool name="settings_root_help_about_visible">true</bool>
|
||||||
|
<bool name="settings_root_legals_visible">true</bool>
|
||||||
|
|
||||||
|
<!-- Level 1: General -->
|
||||||
|
|
||||||
|
<!-- Level 1: Notifications -->
|
||||||
|
|
||||||
|
<!-- Level 1: Preferences -->
|
||||||
|
|
||||||
|
<bool name="settings_interface_bubble_visible">true</bool>
|
||||||
|
<bool name="settings_interface_bubble_default">false</bool>
|
||||||
|
|
||||||
|
<!-- Level 1: Voice and video -->
|
||||||
|
|
||||||
|
<!-- Level 1: Ignored Users -->
|
||||||
|
|
||||||
|
<!-- Level 1: Security and Privacy -->
|
||||||
|
|
||||||
|
<!-- Level 1: Labs -->
|
||||||
|
|
||||||
|
<!-- Level 1: Advcanced settings -->
|
||||||
|
|
||||||
|
<!-- Level 1: Help and about -->
|
||||||
|
|
||||||
|
<!-- Level 1: Legals -->
|
||||||
|
|
||||||
|
|
||||||
|
</resources>
|
@ -5,7 +5,7 @@ apply plugin: 'com.google.android.gms.oss-licenses-plugin'
|
|||||||
apply plugin: 'kotlin-android'
|
apply plugin: 'kotlin-android'
|
||||||
apply plugin: 'kotlin-parcelize'
|
apply plugin: 'kotlin-parcelize'
|
||||||
apply plugin: 'kotlin-kapt'
|
apply plugin: 'kotlin-kapt'
|
||||||
apply plugin: 'placeholder-resolver'
|
apply plugin: 'com.likethesalad.stem'
|
||||||
apply plugin: 'dagger.hilt.android.plugin'
|
apply plugin: 'dagger.hilt.android.plugin'
|
||||||
|
|
||||||
kapt {
|
kapt {
|
||||||
@ -132,16 +132,9 @@ android {
|
|||||||
versionName "${versionMajor}.${versionMinor}.${versionPatch}-sonar"
|
versionName "${versionMajor}.${versionMinor}.${versionPatch}-sonar"
|
||||||
|
|
||||||
buildConfigField "String", "GIT_REVISION", "\"${gitRevision()}\""
|
buildConfigField "String", "GIT_REVISION", "\"${gitRevision()}\""
|
||||||
resValue "string", "git_revision", "\"${gitRevision()}\""
|
|
||||||
|
|
||||||
buildConfigField "String", "GIT_REVISION_DATE", "\"${gitRevisionDate()}\""
|
buildConfigField "String", "GIT_REVISION_DATE", "\"${gitRevisionDate()}\""
|
||||||
resValue "string", "git_revision_date", "\"${gitRevisionDate()}\""
|
|
||||||
|
|
||||||
buildConfigField "String", "GIT_BRANCH_NAME", "\"${gitBranchName()}\""
|
buildConfigField "String", "GIT_BRANCH_NAME", "\"${gitBranchName()}\""
|
||||||
resValue "string", "git_branch_name", "\"${gitBranchName()}\""
|
|
||||||
|
|
||||||
buildConfigField "String", "BUILD_NUMBER", "\"${buildNumber}\""
|
buildConfigField "String", "BUILD_NUMBER", "\"${buildNumber}\""
|
||||||
resValue "string", "build_number", "\"${buildNumber}\""
|
|
||||||
|
|
||||||
buildConfigField "im.vector.app.features.VectorFeatures.OnboardingVariant", "ONBOARDING_VARIANT", "im.vector.app.features.VectorFeatures.OnboardingVariant.FTUE_AUTH"
|
buildConfigField "im.vector.app.features.VectorFeatures.OnboardingVariant", "ONBOARDING_VARIANT", "im.vector.app.features.VectorFeatures.OnboardingVariant.FTUE_AUTH"
|
||||||
|
|
||||||
@ -153,9 +146,6 @@ android {
|
|||||||
// This *must* only be set in trusted environments.
|
// This *must* only be set in trusted environments.
|
||||||
buildConfigField "Boolean", "handleCallAssertedIdentityEvents", "false"
|
buildConfigField "Boolean", "handleCallAssertedIdentityEvents", "false"
|
||||||
|
|
||||||
// Indicates whether or not threading support is enabled
|
|
||||||
buildConfigField "Boolean", "THREADING_ENABLED", "${isThreadingEnabled}"
|
|
||||||
|
|
||||||
buildConfigField "Boolean", "enableLocationSharing", "true"
|
buildConfigField "Boolean", "enableLocationSharing", "true"
|
||||||
buildConfigField "String", "mapTilerKey", "\"fU3vlMsMn4Jb6dnEIFsx\""
|
buildConfigField "String", "mapTilerKey", "\"fU3vlMsMn4Jb6dnEIFsx\""
|
||||||
|
|
||||||
@ -233,7 +223,6 @@ android {
|
|||||||
applicationIdSuffix ".debug"
|
applicationIdSuffix ".debug"
|
||||||
resValue "string", "app_name", "Element dbg"
|
resValue "string", "app_name", "Element dbg"
|
||||||
|
|
||||||
resValue "bool", "debug_mode", "true"
|
|
||||||
buildConfigField "boolean", "LOW_PRIVACY_LOG_ENABLE", "false"
|
buildConfigField "boolean", "LOW_PRIVACY_LOG_ENABLE", "false"
|
||||||
// Set to true if you want to enable strict mode in debug
|
// Set to true if you want to enable strict mode in debug
|
||||||
buildConfigField "boolean", "ENABLE_STRICT_MODE_LOGS", "false"
|
buildConfigField "boolean", "ENABLE_STRICT_MODE_LOGS", "false"
|
||||||
@ -244,7 +233,6 @@ android {
|
|||||||
release {
|
release {
|
||||||
resValue "string", "app_name", "Element"
|
resValue "string", "app_name", "Element"
|
||||||
|
|
||||||
resValue "bool", "debug_mode", "false"
|
|
||||||
buildConfigField "boolean", "LOW_PRIVACY_LOG_ENABLE", "false"
|
buildConfigField "boolean", "LOW_PRIVACY_LOG_ENABLE", "false"
|
||||||
buildConfigField "boolean", "ENABLE_STRICT_MODE_LOGS", "false"
|
buildConfigField "boolean", "ENABLE_STRICT_MODE_LOGS", "false"
|
||||||
|
|
||||||
@ -332,7 +320,7 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
implementation project(":vector-config")
|
||||||
implementation project(":matrix-sdk-android")
|
implementation project(":matrix-sdk-android")
|
||||||
implementation project(":matrix-sdk-android-flow")
|
implementation project(":matrix-sdk-android-flow")
|
||||||
implementation project(":library:jsonviewer")
|
implementation project(":library:jsonviewer")
|
||||||
@ -376,7 +364,7 @@ dependencies {
|
|||||||
implementation 'com.facebook.stetho:stetho:1.6.0'
|
implementation 'com.facebook.stetho:stetho:1.6.0'
|
||||||
|
|
||||||
// Phone number https://github.com/google/libphonenumber
|
// Phone number https://github.com/google/libphonenumber
|
||||||
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.43'
|
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.44'
|
||||||
|
|
||||||
// FlowBinding
|
// FlowBinding
|
||||||
implementation libs.github.flowBinding
|
implementation libs.github.flowBinding
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
<issue id="MissingTranslation" severity="ignore" />
|
<issue id="MissingTranslation" severity="ignore" />
|
||||||
<issue id="TypographyEllipsis" severity="error" />
|
<issue id="TypographyEllipsis" severity="error" />
|
||||||
<issue id="ImpliedQuantity" severity="warning" />
|
<issue id="ImpliedQuantity" severity="warning" />
|
||||||
|
<issue id="MissingQuantity" severity="warning" />
|
||||||
<issue id="UnusedQuantity" severity="error" />
|
<issue id="UnusedQuantity" severity="error" />
|
||||||
<issue id="IconXmlAndPng" severity="error" />
|
<issue id="IconXmlAndPng" severity="error" />
|
||||||
<issue id="IconDipSize" severity="error" />
|
<issue id="IconDipSize" severity="error" />
|
||||||
@ -14,6 +15,39 @@
|
|||||||
<issue id="IconExpectedSize" severity="error" />
|
<issue id="IconExpectedSize" severity="error" />
|
||||||
<issue id="LocaleFolder" severity="error" />
|
<issue id="LocaleFolder" severity="error" />
|
||||||
|
|
||||||
|
<!-- String resource has to be used, for the rest we can ignore for now
|
||||||
|
TODO: stop ignoring warning at all those locations -->
|
||||||
|
<issue id="UnusedResources" severity="error">
|
||||||
|
<ignore path="**/build.gradle" />
|
||||||
|
<ignore path="**/anim/**" />
|
||||||
|
<ignore path="**/color/**" />
|
||||||
|
<ignore path="**/drawable*/**" />
|
||||||
|
<ignore path="**/layout/**" />
|
||||||
|
<ignore path="**/menu/**" />
|
||||||
|
<ignore path="**/raw/**" />
|
||||||
|
<ignore path="**/values/dimens.xml" />
|
||||||
|
<ignore path="**/values/colors.xml" />
|
||||||
|
<ignore path="**/values/palette.xml" />
|
||||||
|
<ignore path="**/values/palette_mobile.xml" />
|
||||||
|
<ignore path="**/values/donottranslate.xml" />
|
||||||
|
<ignore path="**/values/strings_login_v2.xml" />
|
||||||
|
<ignore path="**/library/ui-styles/src/main/res/values/**" />
|
||||||
|
<ignore path="**/xml/**" />
|
||||||
|
|
||||||
|
<!-- Following resources are for F-Droid variant only, so ignore for GPlay -->
|
||||||
|
<ignore regexp="settings_troubleshoot_test_service_boot_*" />
|
||||||
|
<ignore regexp="settings_troubleshoot_test_bg_restricted_*" />
|
||||||
|
<ignore regexp="settings_troubleshoot_test_battery_*" />
|
||||||
|
|
||||||
|
<!-- Following resources are for GPlay variant only, so ignore for F-Droid -->
|
||||||
|
<ignore regexp="settings_troubleshoot_test_play_services_" />
|
||||||
|
<ignore regexp="settings_troubleshoot_test_push_loop_" />
|
||||||
|
<ignore regexp="settings_troubleshoot_test_token_registration_" />
|
||||||
|
<ignore regexp="settings_troubleshoot_test_fcm_" />
|
||||||
|
<ignore regexp="no_valid_google_play_services_apk" />
|
||||||
|
<ignore regexp="sas_error_unknown" />
|
||||||
|
</issue>
|
||||||
|
|
||||||
<!-- UX -->
|
<!-- UX -->
|
||||||
<issue id="ButtonOrder" severity="error" />
|
<issue id="ButtonOrder" severity="error" />
|
||||||
<issue id="TextFields" severity="error" />
|
<issue id="TextFields" severity="error" />
|
||||||
@ -75,14 +109,6 @@
|
|||||||
<issue id="Typos" severity="error" />
|
<issue id="Typos" severity="error" />
|
||||||
<issue id="TypographyDashes" severity="error" />
|
<issue id="TypographyDashes" severity="error" />
|
||||||
|
|
||||||
<!-- Ignore lint issue in generated resource file from templates.
|
|
||||||
https://github.com/LikeTheSalad/android-string-reference generates string from the default language
|
|
||||||
if the translation is missing, and it can lead to typos. Lint should only check the string from the
|
|
||||||
original file, so with id starting by `template_`. -->
|
|
||||||
<issue id="all">
|
|
||||||
<ignore path="**/generated/resolved/**/resolved.xml" />
|
|
||||||
</issue>
|
|
||||||
|
|
||||||
<!-- DI -->
|
<!-- DI -->
|
||||||
<issue id="JvmStaticProvidesInObjectDetector" severity="error" />
|
<issue id="JvmStaticProvidesInObjectDetector" severity="error" />
|
||||||
</lint>
|
</lint>
|
||||||
|
@ -23,8 +23,11 @@ import dagger.Provides
|
|||||||
import dagger.hilt.InstallIn
|
import dagger.hilt.InstallIn
|
||||||
import dagger.hilt.components.SingletonComponent
|
import dagger.hilt.components.SingletonComponent
|
||||||
import im.vector.app.features.DefaultVectorFeatures
|
import im.vector.app.features.DefaultVectorFeatures
|
||||||
|
import im.vector.app.features.DefaultVectorOverrides
|
||||||
import im.vector.app.features.VectorFeatures
|
import im.vector.app.features.VectorFeatures
|
||||||
|
import im.vector.app.features.VectorOverrides
|
||||||
import im.vector.app.features.debug.features.DebugVectorFeatures
|
import im.vector.app.features.debug.features.DebugVectorFeatures
|
||||||
|
import im.vector.app.features.debug.features.DebugVectorOverrides
|
||||||
|
|
||||||
@InstallIn(SingletonComponent::class)
|
@InstallIn(SingletonComponent::class)
|
||||||
@Module
|
@Module
|
||||||
@ -33,6 +36,9 @@ interface FeaturesModule {
|
|||||||
@Binds
|
@Binds
|
||||||
fun bindFeatures(debugFeatures: DebugVectorFeatures): VectorFeatures
|
fun bindFeatures(debugFeatures: DebugVectorFeatures): VectorFeatures
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
fun bindOverrides(debugOverrides: DebugVectorOverrides): VectorOverrides
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@ -44,5 +50,15 @@ interface FeaturesModule {
|
|||||||
fun providesDebugVectorFeatures(context: Context, defaultVectorFeatures: DefaultVectorFeatures): DebugVectorFeatures {
|
fun providesDebugVectorFeatures(context: Context, defaultVectorFeatures: DefaultVectorFeatures): DebugVectorFeatures {
|
||||||
return DebugVectorFeatures(context, defaultVectorFeatures)
|
return DebugVectorFeatures(context, defaultVectorFeatures)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
fun providesDefaultVectorOverrides(): DefaultVectorOverrides {
|
||||||
|
return DefaultVectorOverrides()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
fun providesDebugVectorOverrides(context: Context): DebugVectorOverrides {
|
||||||
|
return DebugVectorOverrides(context)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@ class DebugFeaturesStateFactory @Inject constructor(
|
|||||||
label = "FTUE Personalize profile",
|
label = "FTUE Personalize profile",
|
||||||
key = DebugFeatureKeys.onboardingPersonalize,
|
key = DebugFeatureKeys.onboardingPersonalize,
|
||||||
factory = VectorFeatures::isOnboardingPersonalizeEnabled
|
factory = VectorFeatures::isOnboardingPersonalizeEnabled
|
||||||
)
|
),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.features.debug.features
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.datastore.core.DataStore
|
||||||
|
import androidx.datastore.preferences.core.Preferences
|
||||||
|
import androidx.datastore.preferences.core.booleanPreferencesKey
|
||||||
|
import androidx.datastore.preferences.core.edit
|
||||||
|
import androidx.datastore.preferences.preferencesDataStore
|
||||||
|
import im.vector.app.features.VectorOverrides
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import org.matrix.android.sdk.api.extensions.orFalse
|
||||||
|
|
||||||
|
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "vector_overrides")
|
||||||
|
private val keyForceDialPadDisplay = booleanPreferencesKey("force_dial_pad_display")
|
||||||
|
private val keyForceLoginFallback = booleanPreferencesKey("force_login_fallback")
|
||||||
|
|
||||||
|
class DebugVectorOverrides(private val context: Context) : VectorOverrides {
|
||||||
|
|
||||||
|
override val forceDialPad = context.dataStore.data.map { preferences ->
|
||||||
|
preferences[keyForceDialPadDisplay].orFalse()
|
||||||
|
}
|
||||||
|
|
||||||
|
override val forceLoginFallback = context.dataStore.data.map { preferences ->
|
||||||
|
preferences[keyForceLoginFallback].orFalse()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun setForceDialPadDisplay(force: Boolean) {
|
||||||
|
context.dataStore.edit { settings ->
|
||||||
|
settings[keyForceDialPadDisplay] = force
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun setForceLoginFallback(force: Boolean) {
|
||||||
|
context.dataStore.edit { settings ->
|
||||||
|
settings[keyForceLoginFallback] = force
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -43,9 +43,13 @@ class DebugPrivateSettingsFragment : VectorBaseFragment<FragmentDebugPrivateSett
|
|||||||
views.forceDialPadTabDisplay.setOnCheckedChangeListener { _, isChecked ->
|
views.forceDialPadTabDisplay.setOnCheckedChangeListener { _, isChecked ->
|
||||||
viewModel.handle(DebugPrivateSettingsViewActions.SetDialPadVisibility(isChecked))
|
viewModel.handle(DebugPrivateSettingsViewActions.SetDialPadVisibility(isChecked))
|
||||||
}
|
}
|
||||||
|
views.forceLoginFallback.setOnCheckedChangeListener { _, isChecked ->
|
||||||
|
viewModel.handle(DebugPrivateSettingsViewActions.SetForceLoginFallbackEnabled(isChecked))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun invalidate() = withState(viewModel) {
|
override fun invalidate() = withState(viewModel) {
|
||||||
views.forceDialPadTabDisplay.isChecked = it.dialPadVisible
|
views.forceDialPadTabDisplay.isChecked = it.dialPadVisible
|
||||||
|
views.forceLoginFallback.isChecked = it.forceLoginFallback
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,4 +20,5 @@ import im.vector.app.core.platform.VectorViewModelAction
|
|||||||
|
|
||||||
sealed class DebugPrivateSettingsViewActions : VectorViewModelAction {
|
sealed class DebugPrivateSettingsViewActions : VectorViewModelAction {
|
||||||
data class SetDialPadVisibility(val force: Boolean) : DebugPrivateSettingsViewActions()
|
data class SetDialPadVisibility(val force: Boolean) : DebugPrivateSettingsViewActions()
|
||||||
|
data class SetForceLoginFallbackEnabled(val force: Boolean) : DebugPrivateSettingsViewActions()
|
||||||
}
|
}
|
||||||
|
@ -24,12 +24,12 @@ import im.vector.app.core.di.MavericksAssistedViewModelFactory
|
|||||||
import im.vector.app.core.di.hiltMavericksViewModelFactory
|
import im.vector.app.core.di.hiltMavericksViewModelFactory
|
||||||
import im.vector.app.core.platform.EmptyViewEvents
|
import im.vector.app.core.platform.EmptyViewEvents
|
||||||
import im.vector.app.core.platform.VectorViewModel
|
import im.vector.app.core.platform.VectorViewModel
|
||||||
import im.vector.app.features.settings.VectorDataStore
|
import im.vector.app.features.debug.features.DebugVectorOverrides
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class DebugPrivateSettingsViewModel @AssistedInject constructor(
|
class DebugPrivateSettingsViewModel @AssistedInject constructor(
|
||||||
@Assisted initialState: DebugPrivateSettingsViewState,
|
@Assisted initialState: DebugPrivateSettingsViewState,
|
||||||
private val vectorDataStore: VectorDataStore
|
private val debugVectorOverrides: DebugVectorOverrides
|
||||||
) : VectorViewModel<DebugPrivateSettingsViewState, DebugPrivateSettingsViewActions, EmptyViewEvents>(initialState) {
|
) : VectorViewModel<DebugPrivateSettingsViewState, DebugPrivateSettingsViewActions, EmptyViewEvents>(initialState) {
|
||||||
|
|
||||||
@AssistedFactory
|
@AssistedFactory
|
||||||
@ -44,22 +44,32 @@ class DebugPrivateSettingsViewModel @AssistedInject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun observeVectorDataStore() {
|
private fun observeVectorDataStore() {
|
||||||
vectorDataStore.forceDialPadDisplayFlow.setOnEach {
|
debugVectorOverrides.forceDialPad.setOnEach {
|
||||||
copy(
|
copy(
|
||||||
dialPadVisible = it
|
dialPadVisible = it
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
debugVectorOverrides.forceLoginFallback.setOnEach {
|
||||||
|
copy(forceLoginFallback = it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun handle(action: DebugPrivateSettingsViewActions) {
|
override fun handle(action: DebugPrivateSettingsViewActions) {
|
||||||
when (action) {
|
when (action) {
|
||||||
is DebugPrivateSettingsViewActions.SetDialPadVisibility -> handleSetDialPadVisibility(action)
|
is DebugPrivateSettingsViewActions.SetDialPadVisibility -> handleSetDialPadVisibility(action)
|
||||||
|
is DebugPrivateSettingsViewActions.SetForceLoginFallbackEnabled -> handleSetForceLoginFallbackEnabled(action)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleSetDialPadVisibility(action: DebugPrivateSettingsViewActions.SetDialPadVisibility) {
|
private fun handleSetDialPadVisibility(action: DebugPrivateSettingsViewActions.SetDialPadVisibility) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
vectorDataStore.setForceDialPadDisplay(action.force)
|
debugVectorOverrides.setForceDialPadDisplay(action.force)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleSetForceLoginFallbackEnabled(action: DebugPrivateSettingsViewActions.SetForceLoginFallbackEnabled) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
debugVectorOverrides.setForceLoginFallback(action.force)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,5 +19,6 @@ package im.vector.app.features.debug.settings
|
|||||||
import com.airbnb.mvrx.MavericksState
|
import com.airbnb.mvrx.MavericksState
|
||||||
|
|
||||||
data class DebugPrivateSettingsViewState(
|
data class DebugPrivateSettingsViewState(
|
||||||
val dialPadVisible: Boolean = false
|
val dialPadVisible: Boolean = false,
|
||||||
|
val forceLoginFallback: Boolean = false,
|
||||||
) : MavericksState
|
) : MavericksState
|
||||||
|
@ -25,6 +25,12 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="Force DialPad tab display" />
|
android:text="Force DialPad tab display" />
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
android:id="@+id/forceLoginFallback"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Force login and registration fallback" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
@ -213,7 +213,7 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
|
|||||||
try {
|
try {
|
||||||
val session = activeSessionHolder.getSafeActiveSession() ?: return false
|
val session = activeSessionHolder.getSafeActiveSession() ?: return false
|
||||||
val room = session.getRoom(roomId) ?: return false
|
val room = session.getRoom(roomId) ?: return false
|
||||||
return room.getTimeLineEvent(eventId) != null
|
return room.getTimelineEvent(eventId) != null
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.tag(loggerTag.value).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")
|
||||||
}
|
}
|
||||||
|
@ -99,6 +99,7 @@ import im.vector.app.features.matrixto.MatrixToRoomSpaceFragment
|
|||||||
import im.vector.app.features.matrixto.MatrixToUserFragment
|
import im.vector.app.features.matrixto.MatrixToUserFragment
|
||||||
import im.vector.app.features.onboarding.ftueauth.FtueAuthAccountCreatedFragment
|
import im.vector.app.features.onboarding.ftueauth.FtueAuthAccountCreatedFragment
|
||||||
import im.vector.app.features.onboarding.ftueauth.FtueAuthCaptchaFragment
|
import im.vector.app.features.onboarding.ftueauth.FtueAuthCaptchaFragment
|
||||||
|
import im.vector.app.features.onboarding.ftueauth.FtueAuthChooseDisplayNameFragment
|
||||||
import im.vector.app.features.onboarding.ftueauth.FtueAuthGenericTextInputFormFragment
|
import im.vector.app.features.onboarding.ftueauth.FtueAuthGenericTextInputFormFragment
|
||||||
import im.vector.app.features.onboarding.ftueauth.FtueAuthLoginFragment
|
import im.vector.app.features.onboarding.ftueauth.FtueAuthLoginFragment
|
||||||
import im.vector.app.features.onboarding.ftueauth.FtueAuthResetPasswordFragment
|
import im.vector.app.features.onboarding.ftueauth.FtueAuthResetPasswordFragment
|
||||||
@ -479,6 +480,11 @@ interface FragmentModule {
|
|||||||
@FragmentKey(FtueAuthAccountCreatedFragment::class)
|
@FragmentKey(FtueAuthAccountCreatedFragment::class)
|
||||||
fun bindFtueAuthAccountCreatedFragment(fragment: FtueAuthAccountCreatedFragment): Fragment
|
fun bindFtueAuthAccountCreatedFragment(fragment: FtueAuthAccountCreatedFragment): Fragment
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoMap
|
||||||
|
@FragmentKey(FtueAuthChooseDisplayNameFragment::class)
|
||||||
|
fun bindFtueAuthChooseDisplayNameFragment(fragment: FtueAuthChooseDisplayNameFragment): Fragment
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@IntoMap
|
@IntoMap
|
||||||
@FragmentKey(UserListFragment::class)
|
@FragmentKey(UserListFragment::class)
|
||||||
|
@ -58,6 +58,7 @@ import im.vector.app.features.login.LoginViewModel
|
|||||||
import im.vector.app.features.login2.LoginViewModel2
|
import im.vector.app.features.login2.LoginViewModel2
|
||||||
import im.vector.app.features.login2.created.AccountCreatedViewModel
|
import im.vector.app.features.login2.created.AccountCreatedViewModel
|
||||||
import im.vector.app.features.matrixto.MatrixToBottomSheetViewModel
|
import im.vector.app.features.matrixto.MatrixToBottomSheetViewModel
|
||||||
|
import im.vector.app.features.media.VectorAttachmentViewerViewModel
|
||||||
import im.vector.app.features.onboarding.OnboardingViewModel
|
import im.vector.app.features.onboarding.OnboardingViewModel
|
||||||
import im.vector.app.features.poll.create.CreatePollViewModel
|
import im.vector.app.features.poll.create.CreatePollViewModel
|
||||||
import im.vector.app.features.qrcode.QrCodeScannerViewModel
|
import im.vector.app.features.qrcode.QrCodeScannerViewModel
|
||||||
@ -594,4 +595,9 @@ interface MavericksViewModelModule {
|
|||||||
@IntoMap
|
@IntoMap
|
||||||
@MavericksViewModelKey(LocationSharingViewModel::class)
|
@MavericksViewModelKey(LocationSharingViewModel::class)
|
||||||
fun createLocationSharingViewModelFactory(factory: LocationSharingViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
|
fun createLocationSharingViewModelFactory(factory: LocationSharingViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoMap
|
||||||
|
@MavericksViewModelKey(VectorAttachmentViewerViewModel::class)
|
||||||
|
fun vectorAttachmentViewerViewModelFactory(factory: VectorAttachmentViewerViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
|
||||||
}
|
}
|
||||||
|
@ -169,22 +169,24 @@ const val POP_BACK_STACK_EXCLUSIVE = 0
|
|||||||
|
|
||||||
fun Fragment.queryExportKeys(userId: String, activityResultLauncher: ActivityResultLauncher<Intent>) {
|
fun Fragment.queryExportKeys(userId: String, activityResultLauncher: ActivityResultLauncher<Intent>) {
|
||||||
val timestamp = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(Date())
|
val timestamp = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(Date())
|
||||||
|
val appName = getString(R.string.app_name).replace(" ", "-")
|
||||||
|
|
||||||
selectTxtFileToWrite(
|
selectTxtFileToWrite(
|
||||||
activity = requireActivity(),
|
activity = requireActivity(),
|
||||||
activityResultLauncher = activityResultLauncher,
|
activityResultLauncher = activityResultLauncher,
|
||||||
defaultFileName = "element-megolm-export-$userId-$timestamp.txt",
|
defaultFileName = "$appName-megolm-export-$userId-$timestamp.txt",
|
||||||
chooserHint = getString(R.string.keys_backup_setup_step1_manual_export)
|
chooserHint = getString(R.string.keys_backup_setup_step1_manual_export)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Activity.queryExportKeys(userId: String, activityResultLauncher: ActivityResultLauncher<Intent>) {
|
fun Activity.queryExportKeys(userId: String, activityResultLauncher: ActivityResultLauncher<Intent>) {
|
||||||
val timestamp = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(Date())
|
val timestamp = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(Date())
|
||||||
|
val appName = getString(R.string.app_name).replace(" ", "-")
|
||||||
|
|
||||||
selectTxtFileToWrite(
|
selectTxtFileToWrite(
|
||||||
activity = this,
|
activity = this,
|
||||||
activityResultLauncher = activityResultLauncher,
|
activityResultLauncher = activityResultLauncher,
|
||||||
defaultFileName = "element-megolm-export-$userId-$timestamp.txt",
|
defaultFileName = "$appName-megolm-export-$userId-$timestamp.txt",
|
||||||
chooserHint = getString(R.string.keys_backup_setup_step1_manual_export)
|
chooserHint = getString(R.string.keys_backup_setup_step1_manual_export)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -125,11 +125,11 @@ fun getFileExtension(fileUri: String): String? {
|
|||||||
* Size
|
* Size
|
||||||
* ========================================================================================== */
|
* ========================================================================================== */
|
||||||
|
|
||||||
fun getSizeOfFiles(root: File): Int {
|
fun getSizeOfFiles(root: File): Long {
|
||||||
return root.walkTopDown()
|
return root.walkTopDown()
|
||||||
.onEnter {
|
.onEnter {
|
||||||
Timber.v("Get size of ${it.absolutePath}")
|
Timber.v("Get size of ${it.absolutePath}")
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
.sumOf { it.length().toInt() }
|
.sumOf { it.length() }
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022 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
|
||||||
|
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.flowOf
|
||||||
|
|
||||||
|
interface VectorOverrides {
|
||||||
|
val forceDialPad: Flow<Boolean>
|
||||||
|
val forceLoginFallback: Flow<Boolean>
|
||||||
|
}
|
||||||
|
|
||||||
|
class DefaultVectorOverrides : VectorOverrides {
|
||||||
|
override val forceDialPad = flowOf(false)
|
||||||
|
override val forceLoginFallback = flowOf(false)
|
||||||
|
}
|
@ -40,6 +40,16 @@ data class Interaction(
|
|||||||
) : VectorAnalyticsEvent {
|
) : VectorAnalyticsEvent {
|
||||||
|
|
||||||
enum class Name {
|
enum class Name {
|
||||||
|
/**
|
||||||
|
* User tapped on Add to Home button on Room Details screen.
|
||||||
|
*/
|
||||||
|
MobileRoomAddHome,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User tapped on Leave Room button on Room Details screen.
|
||||||
|
*/
|
||||||
|
MobileRoomLeave,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User tapped the already selected space from the space list.
|
* User tapped the already selected space from the space list.
|
||||||
*/
|
*/
|
||||||
@ -52,8 +62,8 @@ data class Interaction(
|
|||||||
SpacePanelSwitchSpace,
|
SpacePanelSwitchSpace,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User clicked the create room button in the + context menu of the room
|
* User clicked the create room button in the add existing room to space
|
||||||
* list header in Element Web/Desktop.
|
* dialog in Element Web/Desktop.
|
||||||
*/
|
*/
|
||||||
WebAddExistingToSpaceDialogCreateRoomButton,
|
WebAddExistingToSpaceDialogCreateRoomButton,
|
||||||
|
|
||||||
@ -153,6 +163,12 @@ data class Interaction(
|
|||||||
*/
|
*/
|
||||||
WebRoomListHeaderPlusMenuCreateRoomItem,
|
WebRoomListHeaderPlusMenuCreateRoomItem,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User clicked the explore rooms button in the + context menu of the
|
||||||
|
* room list header in Element Web/Desktop.
|
||||||
|
*/
|
||||||
|
WebRoomListHeaderPlusMenuExploreRoomsItem,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User adjusted their favourites using the context menu on a room tile
|
* User adjusted their favourites using the context menu on a room tile
|
||||||
* in the room list in Element Web/Desktop.
|
* in the room list in Element Web/Desktop.
|
||||||
@ -189,6 +205,12 @@ data class Interaction(
|
|||||||
*/
|
*/
|
||||||
WebRoomListRoomsSublistPlusMenuCreateRoomItem,
|
WebRoomListRoomsSublistPlusMenuCreateRoomItem,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User clicked the explore rooms button in the + context menu of the
|
||||||
|
* rooms sublist in Element Web/Desktop.
|
||||||
|
*/
|
||||||
|
WebRoomListRoomsSublistPlusMenuExploreRoomsItem,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User interacted with leave action in the general tab of the room
|
* User interacted with leave action in the general tab of the room
|
||||||
* settings dialog in Element Web/Desktop.
|
* settings dialog in Element Web/Desktop.
|
||||||
@ -214,14 +236,26 @@ data class Interaction(
|
|||||||
WebSettingsSidebarTabSpacesCheckbox,
|
WebSettingsSidebarTabSpacesCheckbox,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User clicked the create room button in the + context menu of the room
|
* User clicked the explore rooms button in the context menu of a space
|
||||||
* list header in Element Web/Desktop.
|
* in Element Web/Desktop.
|
||||||
|
*/
|
||||||
|
WebSpaceContextMenuExploreRoomsItem,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User clicked the home button in the context menu of a space in
|
||||||
|
* Element Web/Desktop.
|
||||||
|
*/
|
||||||
|
WebSpaceContextMenuHomeItem,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User clicked the new room button in the context menu of a space in
|
||||||
|
* Element Web/Desktop.
|
||||||
*/
|
*/
|
||||||
WebSpaceContextMenuNewRoomItem,
|
WebSpaceContextMenuNewRoomItem,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User clicked the create room button in the + context menu of the room
|
* User clicked the new room button in the context menu on the space
|
||||||
* list header in Element Web/Desktop.
|
* home in Element Web/Desktop.
|
||||||
*/
|
*/
|
||||||
WebSpaceHomeCreateRoomButton,
|
WebSpaceHomeCreateRoomButton,
|
||||||
|
|
||||||
|
@ -44,6 +44,11 @@ data class JoinedRoom(
|
|||||||
) : VectorAnalyticsEvent {
|
) : VectorAnalyticsEvent {
|
||||||
|
|
||||||
enum class Trigger {
|
enum class Trigger {
|
||||||
|
/**
|
||||||
|
* Room joined via an invite.
|
||||||
|
*/
|
||||||
|
Invite,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Room joined via a push/desktop notification.
|
* Room joined via a push/desktop notification.
|
||||||
*/
|
*/
|
||||||
@ -54,6 +59,16 @@ data class JoinedRoom(
|
|||||||
*/
|
*/
|
||||||
RoomDirectory,
|
RoomDirectory,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Room joined via its preview.
|
||||||
|
*/
|
||||||
|
RoomPreview,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Room joined via the /join slash command.
|
||||||
|
*/
|
||||||
|
SlashCommand,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Room joined via the space hierarchy view.
|
* Room joined via the space hierarchy view.
|
||||||
*/
|
*/
|
||||||
|
@ -105,6 +105,11 @@ data class MobileScreen(
|
|||||||
*/
|
*/
|
||||||
Room,
|
Room,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The room addresses screen shown from the Room Details screen.
|
||||||
|
*/
|
||||||
|
RoomAddresses,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The screen shown when tapping the name of a room from the Room
|
* The screen shown when tapping the name of a room from the Room
|
||||||
* screen.
|
* screen.
|
||||||
@ -132,6 +137,16 @@ data class MobileScreen(
|
|||||||
*/
|
*/
|
||||||
RoomNotifications,
|
RoomNotifications,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The roles permissions screen shown from the Room Details screen.
|
||||||
|
*/
|
||||||
|
RoomPermissions,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Screen that displays room preview if user hasn't joined yet
|
||||||
|
*/
|
||||||
|
RoomPreview,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The screen that allows you to search for messages/files in a specific
|
* The screen that allows you to search for messages/files in a specific
|
||||||
* room.
|
* room.
|
||||||
@ -180,21 +195,67 @@ data class MobileScreen(
|
|||||||
*/
|
*/
|
||||||
Settings,
|
Settings,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The advanced settings screen (developer mode, rageshake, push
|
||||||
|
* notification rules)
|
||||||
|
*/
|
||||||
|
SettingsAdvanced,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The settings screen to change the default notification options.
|
* The settings screen to change the default notification options.
|
||||||
*/
|
*/
|
||||||
SettingsDefaultNotifications,
|
SettingsDefaultNotifications,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The settings screen with general profile settings.
|
||||||
|
*/
|
||||||
|
SettingsGeneral,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Help and About screen
|
||||||
|
*/
|
||||||
|
SettingsHelp,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The settings screen with list of the ignored users.
|
||||||
|
*/
|
||||||
|
SettingsIgnoredUsers,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The experimental features settings screen,
|
||||||
|
*/
|
||||||
|
SettingsLabs,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The settings screen with legals information
|
||||||
|
*/
|
||||||
|
SettingsLegals,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The settings screen to manage notification mentions and keywords.
|
* The settings screen to manage notification mentions and keywords.
|
||||||
*/
|
*/
|
||||||
SettingsMentionsAndKeywords,
|
SettingsMentionsAndKeywords,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The notifications settings screen.
|
||||||
|
*/
|
||||||
|
SettingsNotifications,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The preferences screen (theme, language, editor preferences, etc.
|
||||||
|
*/
|
||||||
|
SettingsPreferences,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The global security settings screen.
|
* The global security settings screen.
|
||||||
*/
|
*/
|
||||||
SettingsSecurity,
|
SettingsSecurity,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The calls settings screen.
|
||||||
|
*/
|
||||||
|
SettingsVoiceVideo,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The sidebar shown on mobile with spaces, settings etc.
|
* The sidebar shown on mobile with spaces, settings etc.
|
||||||
*/
|
*/
|
||||||
|
@ -25,6 +25,18 @@ import im.vector.app.features.analytics.itf.VectorAnalyticsEvent
|
|||||||
* Triggered when the user changes rooms.
|
* Triggered when the user changes rooms.
|
||||||
*/
|
*/
|
||||||
data class ViewRoom(
|
data class ViewRoom(
|
||||||
|
/**
|
||||||
|
* active space when user navigated to the room.
|
||||||
|
*/
|
||||||
|
val activeSpace: ActiveSpace? = null,
|
||||||
|
/**
|
||||||
|
* Whether the room is a DM.
|
||||||
|
*/
|
||||||
|
val isDM: Boolean? = null,
|
||||||
|
/**
|
||||||
|
* Whether the room is a Space.
|
||||||
|
*/
|
||||||
|
val isSpace: Boolean? = null,
|
||||||
/**
|
/**
|
||||||
* The reason for the room change if known.
|
* The reason for the room change if known.
|
||||||
*/
|
*/
|
||||||
@ -179,10 +191,36 @@ data class ViewRoom(
|
|||||||
Widget,
|
Widget,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class ActiveSpace {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Active space is Home.
|
||||||
|
*/
|
||||||
|
Home,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Active space is a meta space.
|
||||||
|
*/
|
||||||
|
Meta,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Active space is a private space.
|
||||||
|
*/
|
||||||
|
Private,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Active space is a public space.
|
||||||
|
*/
|
||||||
|
Public,
|
||||||
|
}
|
||||||
|
|
||||||
override fun getName() = "ViewRoom"
|
override fun getName() = "ViewRoom"
|
||||||
|
|
||||||
override fun getProperties(): Map<String, Any>? {
|
override fun getProperties(): Map<String, Any>? {
|
||||||
return mutableMapOf<String, Any>().apply {
|
return mutableMapOf<String, Any>().apply {
|
||||||
|
activeSpace?.let { put("activeSpace", it.name) }
|
||||||
|
isDM?.let { put("isDM", it) }
|
||||||
|
isSpace?.let { put("isSpace", it) }
|
||||||
trigger?.let { put("trigger", it.name) }
|
trigger?.let { put("trigger", it.name) }
|
||||||
viaKeyboard?.let { put("viaKeyboard", it) }
|
viaKeyboard?.let { put("viaKeyboard", it) }
|
||||||
}.takeIf { it.isNotEmpty() }
|
}.takeIf { it.isNotEmpty() }
|
||||||
|
@ -28,6 +28,7 @@ import im.vector.app.core.di.MavericksAssistedViewModelFactory
|
|||||||
import im.vector.app.core.di.hiltMavericksViewModelFactory
|
import im.vector.app.core.di.hiltMavericksViewModelFactory
|
||||||
import im.vector.app.core.extensions.singletonEntryPoint
|
import im.vector.app.core.extensions.singletonEntryPoint
|
||||||
import im.vector.app.core.platform.VectorViewModel
|
import im.vector.app.core.platform.VectorViewModel
|
||||||
|
import im.vector.app.features.VectorOverrides
|
||||||
import im.vector.app.features.call.dialpad.DialPadLookup
|
import im.vector.app.features.call.dialpad.DialPadLookup
|
||||||
import im.vector.app.features.call.lookup.CallProtocolsChecker
|
import im.vector.app.features.call.lookup.CallProtocolsChecker
|
||||||
import im.vector.app.features.call.webrtc.WebRtcCallManager
|
import im.vector.app.features.call.webrtc.WebRtcCallManager
|
||||||
@ -67,7 +68,8 @@ class HomeDetailViewModel @AssistedInject constructor(
|
|||||||
private val callManager: WebRtcCallManager,
|
private val callManager: WebRtcCallManager,
|
||||||
private val directRoomHelper: DirectRoomHelper,
|
private val directRoomHelper: DirectRoomHelper,
|
||||||
private val appStateHandler: AppStateHandler,
|
private val appStateHandler: AppStateHandler,
|
||||||
private val autoAcceptInvites: AutoAcceptInvites
|
private val autoAcceptInvites: AutoAcceptInvites,
|
||||||
|
private val vectorOverrides: VectorOverrides
|
||||||
) : VectorViewModel<HomeDetailViewState, HomeDetailAction, HomeDetailViewEvents>(initialState),
|
) : VectorViewModel<HomeDetailViewState, HomeDetailAction, HomeDetailViewEvents>(initialState),
|
||||||
CallProtocolsChecker.Listener {
|
CallProtocolsChecker.Listener {
|
||||||
|
|
||||||
@ -106,8 +108,7 @@ class HomeDetailViewModel @AssistedInject constructor(
|
|||||||
pushCounter = nbOfPush
|
pushCounter = nbOfPush
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
vectorOverrides.forceDialPad.setOnEach { force ->
|
||||||
vectorDataStore.forceDialPadDisplayFlow.setOnEach { force ->
|
|
||||||
copy(
|
copy(
|
||||||
forceDialPadTab = force
|
forceDialPadTab = force
|
||||||
)
|
)
|
||||||
|
@ -92,7 +92,6 @@ sealed class RoomDetailAction : VectorViewModelAction {
|
|||||||
|
|
||||||
data class UpdateJoinJitsiCallStatus(val conferenceEvent: ConferenceEvent) : RoomDetailAction()
|
data class UpdateJoinJitsiCallStatus(val conferenceEvent: ConferenceEvent) : RoomDetailAction()
|
||||||
|
|
||||||
data class OpenOrCreateDm(val userId: String) : RoomDetailAction()
|
|
||||||
data class JumpToReadReceipt(val userId: String) : RoomDetailAction()
|
data class JumpToReadReceipt(val userId: String) : RoomDetailAction()
|
||||||
object QuickActionInvitePeople : RoomDetailAction()
|
object QuickActionInvitePeople : RoomDetailAction()
|
||||||
object QuickActionSetAvatar : RoomDetailAction()
|
object QuickActionSetAvatar : RoomDetailAction()
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
package im.vector.app.features.home.room.detail
|
package im.vector.app.features.home.room.detail
|
||||||
|
|
||||||
sealed class RoomDetailPendingAction {
|
sealed class RoomDetailPendingAction {
|
||||||
data class OpenOrCreateDm(val userId: String) : RoomDetailPendingAction()
|
|
||||||
data class JumpToReadReceipt(val userId: String) : RoomDetailPendingAction()
|
data class JumpToReadReceipt(val userId: String) : RoomDetailPendingAction()
|
||||||
data class MentionUser(val userId: String) : RoomDetailPendingAction()
|
data class MentionUser(val userId: String) : RoomDetailPendingAction()
|
||||||
data class OpenRoom(val roomId: String, val closeCurrentRoom: Boolean = false) : RoomDetailPendingAction()
|
data class OpenRoom(val roomId: String, val closeCurrentRoom: Boolean = false) : RoomDetailPendingAction()
|
||||||
|
@ -1247,8 +1247,6 @@ class TimelineFragment @Inject constructor(
|
|||||||
timelineViewModel.handle(RoomDetailAction.JumpToReadReceipt(roomDetailPendingAction.userId))
|
timelineViewModel.handle(RoomDetailAction.JumpToReadReceipt(roomDetailPendingAction.userId))
|
||||||
is RoomDetailPendingAction.MentionUser ->
|
is RoomDetailPendingAction.MentionUser ->
|
||||||
insertUserDisplayNameInTextEditor(roomDetailPendingAction.userId)
|
insertUserDisplayNameInTextEditor(roomDetailPendingAction.userId)
|
||||||
is RoomDetailPendingAction.OpenOrCreateDm ->
|
|
||||||
timelineViewModel.handle(RoomDetailAction.OpenOrCreateDm(roomDetailPendingAction.userId))
|
|
||||||
is RoomDetailPendingAction.OpenRoom ->
|
is RoomDetailPendingAction.OpenRoom ->
|
||||||
handleOpenRoom(RoomDetailViewEvents.OpenRoom(roomDetailPendingAction.roomId, roomDetailPendingAction.closeCurrentRoom))
|
handleOpenRoom(RoomDetailViewEvents.OpenRoom(roomDetailPendingAction.roomId, roomDetailPendingAction.closeCurrentRoom))
|
||||||
}.exhaustive
|
}.exhaustive
|
||||||
|
@ -420,7 +420,6 @@ class TimelineViewModel @AssistedInject constructor(
|
|||||||
is RoomDetailAction.RemoveWidget -> handleDeleteWidget(action.widgetId)
|
is RoomDetailAction.RemoveWidget -> handleDeleteWidget(action.widgetId)
|
||||||
is RoomDetailAction.EnsureNativeWidgetAllowed -> handleCheckWidgetAllowed(action)
|
is RoomDetailAction.EnsureNativeWidgetAllowed -> handleCheckWidgetAllowed(action)
|
||||||
is RoomDetailAction.CancelSend -> handleCancel(action)
|
is RoomDetailAction.CancelSend -> handleCancel(action)
|
||||||
is RoomDetailAction.OpenOrCreateDm -> handleOpenOrCreateDm(action)
|
|
||||||
is RoomDetailAction.JumpToReadReceipt -> handleJumpToReadReceipt(action)
|
is RoomDetailAction.JumpToReadReceipt -> handleJumpToReadReceipt(action)
|
||||||
RoomDetailAction.QuickActionInvitePeople -> handleInvitePeople()
|
RoomDetailAction.QuickActionInvitePeople -> handleInvitePeople()
|
||||||
RoomDetailAction.QuickActionSetAvatar -> handleQuickSetAvatar()
|
RoomDetailAction.QuickActionSetAvatar -> handleQuickSetAvatar()
|
||||||
@ -500,20 +499,6 @@ class TimelineViewModel @AssistedInject constructor(
|
|||||||
_viewEvents.post(RoomDetailViewEvents.OpenSetRoomAvatarDialog)
|
_viewEvents.post(RoomDetailViewEvents.OpenSetRoomAvatarDialog)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleOpenOrCreateDm(action: RoomDetailAction.OpenOrCreateDm) {
|
|
||||||
viewModelScope.launch {
|
|
||||||
val roomId = try {
|
|
||||||
directRoomHelper.ensureDMExists(action.userId)
|
|
||||||
} catch (failure: Throwable) {
|
|
||||||
_viewEvents.post(RoomDetailViewEvents.ActionFailure(action, failure))
|
|
||||||
return@launch
|
|
||||||
}
|
|
||||||
if (roomId != initialState.roomId) {
|
|
||||||
_viewEvents.post(RoomDetailViewEvents.OpenRoom(roomId = roomId))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleJumpToReadReceipt(action: RoomDetailAction.JumpToReadReceipt) {
|
private fun handleJumpToReadReceipt(action: RoomDetailAction.JumpToReadReceipt) {
|
||||||
room.getUserReadReceipt(action.userId)
|
room.getUserReadReceipt(action.userId)
|
||||||
?.let { handleNavigateToEvent(RoomDetailAction.NavigateToEvent(it, true)) }
|
?.let { handleNavigateToEvent(RoomDetailAction.NavigateToEvent(it, true)) }
|
||||||
@ -745,7 +730,7 @@ class TimelineViewModel @AssistedInject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun handleRedactEvent(action: RoomDetailAction.RedactAction) {
|
private fun handleRedactEvent(action: RoomDetailAction.RedactAction) {
|
||||||
val event = room.getTimeLineEvent(action.targetEventId) ?: return
|
val event = room.getTimelineEvent(action.targetEventId) ?: return
|
||||||
room.redactEvent(event.root, action.reason)
|
room.redactEvent(event.root, action.reason)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -785,7 +770,7 @@ class TimelineViewModel @AssistedInject constructor(
|
|||||||
}
|
}
|
||||||
// We need to update this with the related m.replace also (to move read receipt)
|
// We need to update this with the related m.replace also (to move read receipt)
|
||||||
action.event.annotations?.editSummary?.sourceEvents?.forEach {
|
action.event.annotations?.editSummary?.sourceEvents?.forEach {
|
||||||
room.getTimeLineEvent(it)?.let { event ->
|
room.getTimelineEvent(it)?.let { event ->
|
||||||
visibleEventsSource.post(RoomDetailAction.TimelineEventTurnsVisible(event))
|
visibleEventsSource.post(RoomDetailAction.TimelineEventTurnsVisible(event))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -813,7 +798,7 @@ class TimelineViewModel @AssistedInject constructor(
|
|||||||
notificationDrawerManager.updateEvents { it.clearMemberShipNotificationForRoom(initialState.roomId) }
|
notificationDrawerManager.updateEvents { it.clearMemberShipNotificationForRoom(initialState.roomId) }
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
try {
|
try {
|
||||||
session.leaveRoom(room.roomId)
|
session.leaveRoom(room.roomId)
|
||||||
} catch (throwable: Throwable) {
|
} catch (throwable: Throwable) {
|
||||||
_viewEvents.post(RoomDetailViewEvents.Failure(throwable, showInDialog = true))
|
_viewEvents.post(RoomDetailViewEvents.Failure(throwable, showInDialog = true))
|
||||||
}
|
}
|
||||||
@ -889,7 +874,7 @@ class TimelineViewModel @AssistedInject constructor(
|
|||||||
|
|
||||||
private fun handleResendEvent(action: RoomDetailAction.ResendMessage) {
|
private fun handleResendEvent(action: RoomDetailAction.ResendMessage) {
|
||||||
val targetEventId = action.eventId
|
val targetEventId = action.eventId
|
||||||
room.getTimeLineEvent(targetEventId)?.let {
|
room.getTimelineEvent(targetEventId)?.let {
|
||||||
// State must be UNDELIVERED or Failed
|
// State must be UNDELIVERED or Failed
|
||||||
if (!it.root.sendState.hasFailed()) {
|
if (!it.root.sendState.hasFailed()) {
|
||||||
Timber.e("Cannot resend message, it is not failed, Cancel first")
|
Timber.e("Cannot resend message, it is not failed, Cancel first")
|
||||||
@ -907,7 +892,7 @@ class TimelineViewModel @AssistedInject constructor(
|
|||||||
|
|
||||||
private fun handleRemove(action: RoomDetailAction.RemoveFailedEcho) {
|
private fun handleRemove(action: RoomDetailAction.RemoveFailedEcho) {
|
||||||
val targetEventId = action.eventId
|
val targetEventId = action.eventId
|
||||||
room.getTimeLineEvent(targetEventId)?.let {
|
room.getTimelineEvent(targetEventId)?.let {
|
||||||
// State must be UNDELIVERED or Failed
|
// State must be UNDELIVERED or Failed
|
||||||
if (!it.root.sendState.hasFailed()) {
|
if (!it.root.sendState.hasFailed()) {
|
||||||
Timber.e("Cannot resend message, it is not failed, Cancel first")
|
Timber.e("Cannot resend message, it is not failed, Cancel first")
|
||||||
@ -923,7 +908,7 @@ class TimelineViewModel @AssistedInject constructor(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
val targetEventId = action.eventId
|
val targetEventId = action.eventId
|
||||||
room.getTimeLineEvent(targetEventId)?.let {
|
room.getTimelineEvent(targetEventId)?.let {
|
||||||
// State must be in one of the sending states
|
// State must be in one of the sending states
|
||||||
if (!it.root.sendState.isSending()) {
|
if (!it.root.sendState.isSending()) {
|
||||||
Timber.e("Cannot cancel message, it is not sending")
|
Timber.e("Cannot cancel message, it is not sending")
|
||||||
@ -1049,14 +1034,14 @@ class TimelineViewModel @AssistedInject constructor(
|
|||||||
|
|
||||||
private fun handleReRequestKeys(action: RoomDetailAction.ReRequestKeys) {
|
private fun handleReRequestKeys(action: RoomDetailAction.ReRequestKeys) {
|
||||||
// Check if this request is still active and handled by me
|
// Check if this request is still active and handled by me
|
||||||
room.getTimeLineEvent(action.eventId)?.let {
|
room.getTimelineEvent(action.eventId)?.let {
|
||||||
session.cryptoService().reRequestRoomKeyForEvent(it.root)
|
session.cryptoService().reRequestRoomKeyForEvent(it.root)
|
||||||
_viewEvents.post(RoomDetailViewEvents.ShowMessage(stringProvider.getString(R.string.e2e_re_request_encryption_key_dialog_content)))
|
_viewEvents.post(RoomDetailViewEvents.ShowMessage(stringProvider.getString(R.string.e2e_re_request_encryption_key_dialog_content)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleTapOnFailedToDecrypt(action: RoomDetailAction.TapOnFailedToDecrypt) {
|
private fun handleTapOnFailedToDecrypt(action: RoomDetailAction.TapOnFailedToDecrypt) {
|
||||||
room.getTimeLineEvent(action.eventId)?.let {
|
room.getTimelineEvent(action.eventId)?.let {
|
||||||
val code = when (it.root.mCryptoError) {
|
val code = when (it.root.mCryptoError) {
|
||||||
MXCryptoError.ErrorType.KEYS_WITHHELD -> {
|
MXCryptoError.ErrorType.KEYS_WITHHELD -> {
|
||||||
WithHeldCode.fromCode(it.root.mCryptoErrorReason)
|
WithHeldCode.fromCode(it.root.mCryptoErrorReason)
|
||||||
@ -1072,7 +1057,7 @@ class TimelineViewModel @AssistedInject constructor(
|
|||||||
// Do not allow to vote unsent local echo of the poll event
|
// Do not allow to vote unsent local echo of the poll event
|
||||||
if (LocalEcho.isLocalEchoId(action.eventId)) return
|
if (LocalEcho.isLocalEchoId(action.eventId)) return
|
||||||
// Do not allow to vote the same option twice
|
// Do not allow to vote the same option twice
|
||||||
room.getTimeLineEvent(action.eventId)?.let { pollTimelineEvent ->
|
room.getTimelineEvent(action.eventId)?.let { pollTimelineEvent ->
|
||||||
val currentVote = pollTimelineEvent.annotations?.pollResponseSummary?.aggregatedContent?.myVote
|
val currentVote = pollTimelineEvent.annotations?.pollResponseSummary?.aggregatedContent?.myVote
|
||||||
if (currentVote != action.optionKey) {
|
if (currentVote != action.optionKey) {
|
||||||
room.voteToPoll(action.eventId, action.optionKey)
|
room.voteToPoll(action.eventId, action.optionKey)
|
||||||
|
@ -143,7 +143,7 @@ class MessageComposerViewModel @AssistedInject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun handleEnterEditMode(action: MessageComposerAction.EnterEditMode) {
|
private fun handleEnterEditMode(action: MessageComposerAction.EnterEditMode) {
|
||||||
room.getTimeLineEvent(action.eventId)?.let { timelineEvent ->
|
room.getTimelineEvent(action.eventId)?.let { timelineEvent ->
|
||||||
setState { copy(sendMode = SendMode.Edit(timelineEvent, timelineEvent.getTextEditableContent())) }
|
setState { copy(sendMode = SendMode.Edit(timelineEvent, timelineEvent.getTextEditableContent())) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -175,13 +175,13 @@ class MessageComposerViewModel @AssistedInject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun handleEnterQuoteMode(action: MessageComposerAction.EnterQuoteMode) {
|
private fun handleEnterQuoteMode(action: MessageComposerAction.EnterQuoteMode) {
|
||||||
room.getTimeLineEvent(action.eventId)?.let { timelineEvent ->
|
room.getTimelineEvent(action.eventId)?.let { timelineEvent ->
|
||||||
setState { copy(sendMode = SendMode.Quote(timelineEvent, action.text)) }
|
setState { copy(sendMode = SendMode.Quote(timelineEvent, action.text)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleEnterReplyMode(action: MessageComposerAction.EnterReplyMode) {
|
private fun handleEnterReplyMode(action: MessageComposerAction.EnterReplyMode) {
|
||||||
room.getTimeLineEvent(action.eventId)?.let { timelineEvent ->
|
room.getTimelineEvent(action.eventId)?.let { timelineEvent ->
|
||||||
setState { copy(sendMode = SendMode.Reply(timelineEvent, action.text)) }
|
setState { copy(sendMode = SendMode.Reply(timelineEvent, action.text)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -479,7 +479,7 @@ class MessageComposerViewModel @AssistedInject constructor(
|
|||||||
|
|
||||||
if (inReplyTo != null) {
|
if (inReplyTo != null) {
|
||||||
// TODO check if same content?
|
// TODO check if same content?
|
||||||
room.getTimeLineEvent(inReplyTo)?.let {
|
room.getTimelineEvent(inReplyTo)?.let {
|
||||||
room.editReply(state.sendMode.timelineEvent, it, action.text.toString())
|
room.editReply(state.sendMode.timelineEvent, it, action.text.toString())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -555,17 +555,17 @@ class MessageComposerViewModel @AssistedInject constructor(
|
|||||||
sendMode = when (currentDraft) {
|
sendMode = when (currentDraft) {
|
||||||
is UserDraft.Regular -> SendMode.Regular(currentDraft.content, false)
|
is UserDraft.Regular -> SendMode.Regular(currentDraft.content, false)
|
||||||
is UserDraft.Quote -> {
|
is UserDraft.Quote -> {
|
||||||
room.getTimeLineEvent(currentDraft.linkedEventId)?.let { timelineEvent ->
|
room.getTimelineEvent(currentDraft.linkedEventId)?.let { timelineEvent ->
|
||||||
SendMode.Quote(timelineEvent, currentDraft.content)
|
SendMode.Quote(timelineEvent, currentDraft.content)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is UserDraft.Reply -> {
|
is UserDraft.Reply -> {
|
||||||
room.getTimeLineEvent(currentDraft.linkedEventId)?.let { timelineEvent ->
|
room.getTimelineEvent(currentDraft.linkedEventId)?.let { timelineEvent ->
|
||||||
SendMode.Reply(timelineEvent, currentDraft.content)
|
SendMode.Reply(timelineEvent, currentDraft.content)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is UserDraft.Edit -> {
|
is UserDraft.Edit -> {
|
||||||
room.getTimeLineEvent(currentDraft.linkedEventId)?.let { timelineEvent ->
|
room.getTimelineEvent(currentDraft.linkedEventId)?.let { timelineEvent ->
|
||||||
SendMode.Edit(timelineEvent, currentDraft.content)
|
SendMode.Edit(timelineEvent, currentDraft.content)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -343,24 +343,6 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
|
|||||||
add(EventSharedAction.Edit(eventId, timelineEvent.root.getClearType()))
|
add(EventSharedAction.Edit(eventId, timelineEvent.root.getClearType()))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (canRedact(timelineEvent, actionPermissions)) {
|
|
||||||
if (timelineEvent.root.getClearType() == EventType.POLL_START) {
|
|
||||||
add(EventSharedAction.Redact(
|
|
||||||
eventId,
|
|
||||||
askForReason = informationData.senderId != session.myUserId,
|
|
||||||
dialogTitleRes = R.string.delete_poll_dialog_title,
|
|
||||||
dialogDescriptionRes = R.string.delete_poll_dialog_content
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
add(EventSharedAction.Redact(
|
|
||||||
eventId,
|
|
||||||
askForReason = informationData.senderId != session.myUserId,
|
|
||||||
dialogTitleRes = R.string.delete_event_dialog_title,
|
|
||||||
dialogDescriptionRes = R.string.delete_event_dialog_content
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (canCopy(msgType)) {
|
if (canCopy(msgType)) {
|
||||||
// TODO copy images? html? see ClipBoard
|
// TODO copy images? html? see ClipBoard
|
||||||
add(EventSharedAction.Copy(messageContent!!.body))
|
add(EventSharedAction.Copy(messageContent!!.body))
|
||||||
@ -382,12 +364,30 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
|
|||||||
add(EventSharedAction.ViewEditHistory(informationData))
|
add(EventSharedAction.ViewEditHistory(informationData))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (canSave(msgType) && messageContent is MessageWithAttachmentContent) {
|
||||||
|
add(EventSharedAction.Save(timelineEvent.eventId, messageContent))
|
||||||
|
}
|
||||||
|
|
||||||
if (canShare(msgType)) {
|
if (canShare(msgType)) {
|
||||||
add(EventSharedAction.Share(timelineEvent.eventId, messageContent!!))
|
add(EventSharedAction.Share(timelineEvent.eventId, messageContent!!))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (canSave(msgType) && messageContent is MessageWithAttachmentContent) {
|
if (canRedact(timelineEvent, actionPermissions)) {
|
||||||
add(EventSharedAction.Save(timelineEvent.eventId, messageContent))
|
if (timelineEvent.root.getClearType() == EventType.POLL_START) {
|
||||||
|
add(EventSharedAction.Redact(
|
||||||
|
eventId,
|
||||||
|
askForReason = informationData.senderId != session.myUserId,
|
||||||
|
dialogTitleRes = R.string.delete_poll_dialog_title,
|
||||||
|
dialogDescriptionRes = R.string.delete_poll_dialog_content
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
add(EventSharedAction.Redact(
|
||||||
|
eventId,
|
||||||
|
askForReason = informationData.senderId != session.myUserId,
|
||||||
|
dialogTitleRes = R.string.delete_event_dialog_title,
|
||||||
|
dialogDescriptionRes = R.string.delete_event_dialog_content
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ class VerificationItemFactory @Inject constructor(
|
|||||||
?: return ignoredConclusion(params)
|
?: return ignoredConclusion(params)
|
||||||
|
|
||||||
// If we cannot find the referenced request we do not display the done event
|
// If we cannot find the referenced request we do not display the done event
|
||||||
val refEvent = session.getRoom(event.root.roomId ?: "")?.getTimeLineEvent(refEventId)
|
val refEvent = session.getRoom(event.root.roomId ?: "")?.getTimelineEvent(refEventId)
|
||||||
?: return ignoredConclusion(params)
|
?: return ignoredConclusion(params)
|
||||||
|
|
||||||
// If it's not a request ignore this event
|
// If it's not a request ignore this event
|
||||||
|
@ -86,7 +86,7 @@ class ViewReactionsViewModel @AssistedInject constructor(@Assisted
|
|||||||
annotationsSummary.reactionsSummary
|
annotationsSummary.reactionsSummary
|
||||||
.flatMap { reactionsSummary ->
|
.flatMap { reactionsSummary ->
|
||||||
reactionsSummary.sourceEvents.map {
|
reactionsSummary.sourceEvents.map {
|
||||||
val event = room.getTimeLineEvent(it)
|
val event = room.getTimelineEvent(it)
|
||||||
?: throw RuntimeException("Your eventId is not valid")
|
?: throw RuntimeException("Your eventId is not valid")
|
||||||
ReactionInfo(
|
ReactionInfo(
|
||||||
event.root.eventId!!,
|
event.root.eventId!!,
|
||||||
|
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022 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.media
|
||||||
|
|
||||||
|
interface AttachmentInteractionListener {
|
||||||
|
fun onDismiss()
|
||||||
|
fun onShare()
|
||||||
|
fun onDownload()
|
||||||
|
fun onPlayPause(play: Boolean)
|
||||||
|
fun videoSeekTo(percent: Int)
|
||||||
|
}
|
@ -30,35 +30,33 @@ class AttachmentOverlayView @JvmOverloads constructor(
|
|||||||
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
|
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
|
||||||
) : ConstraintLayout(context, attrs, defStyleAttr), AttachmentEventListener {
|
) : ConstraintLayout(context, attrs, defStyleAttr), AttachmentEventListener {
|
||||||
|
|
||||||
var onShareCallback: (() -> Unit)? = null
|
var interactionListener: AttachmentInteractionListener? = null
|
||||||
var onBack: (() -> Unit)? = null
|
|
||||||
var onPlayPause: ((play: Boolean) -> Unit)? = null
|
|
||||||
var videoSeekTo: ((progress: Int) -> Unit)? = null
|
|
||||||
|
|
||||||
val views: MergeImageAttachmentOverlayBinding
|
val views: MergeImageAttachmentOverlayBinding
|
||||||
|
|
||||||
var isPlaying = false
|
private var isPlaying = false
|
||||||
|
private var suspendSeekBarUpdate = false
|
||||||
var suspendSeekBarUpdate = false
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
inflate(context, R.layout.merge_image_attachment_overlay, this)
|
inflate(context, R.layout.merge_image_attachment_overlay, this)
|
||||||
views = MergeImageAttachmentOverlayBinding.bind(this)
|
views = MergeImageAttachmentOverlayBinding.bind(this)
|
||||||
setBackgroundColor(Color.TRANSPARENT)
|
setBackgroundColor(Color.TRANSPARENT)
|
||||||
views.overlayBackButton.setOnClickListener {
|
views.overlayBackButton.setOnClickListener {
|
||||||
onBack?.invoke()
|
interactionListener?.onDismiss()
|
||||||
}
|
}
|
||||||
views.overlayShareButton.setOnClickListener {
|
views.overlayShareButton.setOnClickListener {
|
||||||
onShareCallback?.invoke()
|
interactionListener?.onShare()
|
||||||
|
}
|
||||||
|
views.overlayDownloadButton.setOnClickListener {
|
||||||
|
interactionListener?.onDownload()
|
||||||
}
|
}
|
||||||
views.overlayPlayPauseButton.setOnClickListener {
|
views.overlayPlayPauseButton.setOnClickListener {
|
||||||
onPlayPause?.invoke(!isPlaying)
|
interactionListener?.onPlayPause(!isPlaying)
|
||||||
}
|
}
|
||||||
|
|
||||||
views.overlaySeekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
|
views.overlaySeekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
|
||||||
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
|
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
|
||||||
if (fromUser) {
|
if (fromUser) {
|
||||||
videoSeekTo?.invoke(progress)
|
interactionListener?.videoSeekTo(progress)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,14 +49,7 @@ abstract class BaseAttachmentProvider<Type>(
|
|||||||
private val stringProvider: StringProvider
|
private val stringProvider: StringProvider
|
||||||
) : AttachmentSourceProvider {
|
) : AttachmentSourceProvider {
|
||||||
|
|
||||||
interface InteractionListener {
|
var interactionListener: AttachmentInteractionListener? = null
|
||||||
fun onDismissTapped()
|
|
||||||
fun onShareTapped()
|
|
||||||
fun onPlayPause(play: Boolean)
|
|
||||||
fun videoSeekTo(percent: Int)
|
|
||||||
}
|
|
||||||
|
|
||||||
var interactionListener: InteractionListener? = null
|
|
||||||
|
|
||||||
private var overlayView: AttachmentOverlayView? = null
|
private var overlayView: AttachmentOverlayView? = null
|
||||||
|
|
||||||
@ -68,18 +61,7 @@ abstract class BaseAttachmentProvider<Type>(
|
|||||||
if (position == -1) return null
|
if (position == -1) return null
|
||||||
if (overlayView == null) {
|
if (overlayView == null) {
|
||||||
overlayView = AttachmentOverlayView(context)
|
overlayView = AttachmentOverlayView(context)
|
||||||
overlayView?.onBack = {
|
overlayView?.interactionListener = interactionListener
|
||||||
interactionListener?.onDismissTapped()
|
|
||||||
}
|
|
||||||
overlayView?.onShareCallback = {
|
|
||||||
interactionListener?.onShareTapped()
|
|
||||||
}
|
|
||||||
overlayView?.onPlayPause = { play ->
|
|
||||||
interactionListener?.onPlayPause(play)
|
|
||||||
}
|
|
||||||
overlayView?.videoSeekTo = { percent ->
|
|
||||||
interactionListener?.videoSeekTo(percent)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val timelineEvent = getTimelineEventAtPosition(position)
|
val timelineEvent = getTimelineEventAtPosition(position)
|
||||||
|
@ -81,7 +81,7 @@ class DataAttachmentRoomProvider(
|
|||||||
|
|
||||||
override fun getTimelineEventAtPosition(position: Int): TimelineEvent? {
|
override fun getTimelineEventAtPosition(position: Int): TimelineEvent? {
|
||||||
val item = getItem(position)
|
val item = getItem(position)
|
||||||
return room?.getTimeLineEvent(item.eventId)
|
return room?.getTimelineEvent(item.eventId)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getFileForSharing(position: Int): File? {
|
override suspend fun getFileForSharing(position: Int): File? {
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022 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.media
|
||||||
|
|
||||||
|
import im.vector.app.core.platform.VectorViewModelAction
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
sealed class VectorAttachmentViewerAction : VectorViewModelAction {
|
||||||
|
data class DownloadMedia(val file: File) : VectorAttachmentViewerAction()
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user