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:
ariskotsomitopoulos 2022-03-03 13:56:59 +02:00
commit e4282e5f29
212 changed files with 5434 additions and 32625 deletions

View file

@ -20,6 +20,10 @@ jobs:
fail-fast: false
matrix:
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:
- uses: actions/checkout@v2
- uses: actions/cache@v2
@ -43,6 +47,7 @@ jobs:
name: Build unsigned GPlay APKs
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
# Only runs on main, no concurrency.
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v2

View file

@ -5,6 +5,7 @@ jobs:
validation:
name: "Validation"
runs-on: ubuntu-latest
# No concurrency required, this is a prerequisite to other actions and should run every time.
steps:
- uses: actions/checkout@v2
- uses: gradle/wrapper-validation-action@v1

View file

@ -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
View 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}}"

View file

@ -18,6 +18,10 @@ jobs:
ktlint:
name: Kotlin Linter
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:
- uses: actions/checkout@v2
- name: Run ktlint
@ -87,6 +91,10 @@ jobs:
android-lint:
name: Android Linter
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:
- uses: actions/checkout@v2
- uses: actions/cache@v2
@ -116,6 +124,10 @@ jobs:
fail-fast: false
matrix:
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:
- uses: actions/checkout@v2
- uses: actions/cache@v2

View file

@ -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}}"

View file

@ -9,10 +9,11 @@ jobs:
runs-on: ubuntu-latest
# Skip in forks
if: github.repository == 'vector-im/element-android'
# No concurrency required, runs every time on a schedule.
steps:
- uses: actions/checkout@v2
- name: Set up Python 3.8
uses: actions/setup-python@v2
uses: actions/setup-python@v3
with:
python-version: 3.8
- name: Install Prerequisite dependencies
@ -35,10 +36,11 @@ jobs:
runs-on: ubuntu-latest
# Skip in forks
if: github.repository == 'vector-im/element-android'
# No concurrency required, runs every time on a schedule.
steps:
- uses: actions/checkout@v2
- name: Set up Python 3.8
uses: actions/setup-python@v2
uses: actions/setup-python@v3
with:
python-version: 3.8
- name: Install Prerequisite dependencies
@ -60,6 +62,7 @@ jobs:
runs-on: ubuntu-latest
# Skip in forks
if: github.repository == 'vector-im/element-android'
# No concurrency required, runs every time on a schedule.
steps:
- uses: actions/checkout@v2
- name: Run analytics import script

View file

@ -15,6 +15,10 @@ jobs:
unit-tests:
name: Run Unit Tests
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:
- uses: actions/checkout@v2
- uses: actions/cache@v2

View file

@ -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/f-droid-badge.png" alt="Get it on F-Droid" height="60">](https://f-droid.org/app/im.vector.app)
Nightly build: [![Buildkite](https://badge.buildkite.com/ad0065c1b70f557cd3b1d3d68f9c2154010f83c4d6f71706a9.svg?branch=develop)](https://buildkite.com/matrix-dot-org/element-android/builds?branch=develop) Nighly sanity test status: [![allScreensTest](https://github.com/vector-im/element-android/actions/workflows/sanity_test.yml/badge.svg)](https://github.com/vector-im/element-android/actions/workflows/sanity_test.yml)
Nightly build: [![Buildkite](https://badge.buildkite.com/ad0065c1b70f557cd3b1d3d68f9c2154010f83c4d6f71706a9.svg?branch=develop)](https://buildkite.com/matrix-dot-org/element-android/builds?branch=develop) Nighly test status: [![allScreensTest](https://github.com/vector-im/element-android/actions/workflows/nightly.yml/badge.svg)](https://github.com/vector-im/element-android/actions/workflows/nightly.yml)
# New Android SDK

View file

@ -19,8 +19,8 @@ buildscript {
classpath libs.gradle.hiltPlugin
classpath 'com.google.gms:google-services:4.3.10'
classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.3'
classpath 'com.google.android.gms:oss-licenses-plugin:0.10.4'
classpath "com.likethesalad.android:string-reference:1.2.2"
classpath 'com.google.android.gms:oss-licenses-plugin:0.10.5'
classpath "com.likethesalad.android:stem-plugin:2.0.0"
// NOTE: Do not place your application dependencies here; they belong
// 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") {
// sonarqube {
// properties {

1
changelog.d/4319.bugfix Normal file
View 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
View file

@ -0,0 +1 @@
Add possibility to save media from Gallery + reorder choices in message context menu

1
changelog.d/5325.feature Normal file
View file

@ -0,0 +1 @@
Adds forceLoginFallback feature flag and usages to FTUE login and registration

1
changelog.d/5326.misc Normal file
View file

@ -0,0 +1 @@
[Export e2ee keys] use appName instead of element

1
changelog.d/5330.misc Normal file
View file

@ -0,0 +1 @@
Continue improving realm usage.

1
changelog.d/5330.sdk Normal file
View file

@ -0,0 +1 @@
Change name of getTimeLineEvent and getTimeLineEventLive methods to getTimelineEvent and getTimelineEventLive.

1
changelog.d/5348.misc Normal file
View 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
View file

@ -0,0 +1 @@
Remove about 700 unused strings and their translations

1
changelog.d/5361.misc Normal file
View file

@ -0,0 +1 @@
Creates dedicated VectorOverrides for forcing behaviour for local testing/development

1
changelog.d/5379.misc Normal file
View file

@ -0,0 +1 @@
Cleanup unused threads build configurations

1
changelog.d/5392.misc Normal file
View file

@ -0,0 +1 @@
Upgrades material dependency version from 1.4.0 to 1.5.0

1
changelog.d/5394.bugfix Normal file
View file

@ -0,0 +1 @@
Fix incorrect media cache size in settings

View file

@ -71,8 +71,7 @@ ext.libs = [
'espressoIntents' : "androidx.test.espresso:espresso-intents:$espresso"
],
google : [
// TODO There is 1.6.0?
'material' : "com.google.android.material:material:1.4.0"
'material' : "com.google.android.material:material:1.5.0"
],
dagger : [
'dagger' : "com.google.dagger:dagger:$dagger",

View file

@ -175,6 +175,7 @@ ext.groups = [
'org.sonatype.oss',
'org.testng',
'org.threeten',
'org.webjars',
'ru.noties',
'xerces',
'xml-apis',

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View file

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

View file

@ -0,0 +1,2 @@
Основні зміни в цій версії: додано підтримку до @room і нерозкритих опитувань та багато інших незначних змін.
Вичерпний перелік змін: https://github.com/vector-im/element-android/releases/tag/v1.4.2

View file

@ -0,0 +1,2 @@
此版本中的主要變動:訊息討論串的初始實作。訊息泡泡。
完整的變更紀錄https://github.com/vector-im/element-android/releases/tag/v1.4.0

View file

@ -0,0 +1,2 @@
此版本中的主要變動:新增對 @room 的支援與未公開的投票,以及其他許多小變動。
完整的變更紀錄https://github.com/vector-im/element-android/releases/tag/v1.4.2

View file

@ -45,6 +45,8 @@ import kotlin.math.abs
abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventListener {
protected val rootView: View
get() = views.rootContainer
protected val pager2: ViewPager2
get() = views.attachmentPager
protected val imageTransitionView: ImageView
@ -298,10 +300,11 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
private fun createSwipeToDismissHandler(): SwipeToDismissHandler =
SwipeToDismissHandler(
swipeView = views.dismissContainer,
shouldAnimateDismiss = { shouldAnimateDismiss() },
onDismiss = { animateClose() },
onSwipeViewMove = ::handleSwipeViewMove)
swipeView = views.dismissContainer,
shouldAnimateDismiss = { shouldAnimateDismiss() },
onDismiss = { animateClose() },
onSwipeViewMove = ::handleSwipeViewMove
)
private fun createSwipeDirectionDetector() =
SwipeDirectionDetector(this) { swipeDirection = it }

View file

@ -59,9 +59,9 @@ class FlowRoom(private val room: Room) {
}
fun liveTimelineEvent(eventId: String): Flow<Optional<TimelineEvent>> {
return room.getTimeLineEventLive(eventId).asFlow()
return room.getTimelineEventLive(eventId).asFlow()
.startWith(room.coroutineDispatchers.io) {
room.getTimeLineEvent(eventId).toOptional()
room.getTimelineEvent(eventId).toOptional()
}
}

View file

@ -38,8 +38,6 @@ android {
resValue "string", "git_sdk_revision_unix_date", "\"${gitRevisionUnixDate()}\""
resValue "string", "git_sdk_revision_date", "\"${gitRevisionDate()}\""
// Indicates whether or not threading support is enabled
buildConfigField "Boolean", "THREADING_ENABLED", "${isThreadingEnabled}"
defaultConfig {
consumerProguardFiles 'proguard-rules.pro'
}
@ -169,7 +167,7 @@ dependencies {
implementation libs.apache.commonsImaging
// 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 'org.robolectric:robolectric:4.7.3'

View file

@ -95,7 +95,7 @@ class PreShareKeysTest : InstrumentedTest {
assertEquals(megolmSessionId, sentEvent.root.content.toModel<EncryptedEventContent>()?.sessionId, "Unexpected megolm session")
testHelper.waitWithLatch { latch ->
testHelper.retryPeriodicallyWithLatch(latch) {
bobSession.getRoom(e2eRoomID)?.getTimeLineEvent(sentEvent.eventId)?.root?.getClearType() == EventType.MESSAGE
bobSession.getRoom(e2eRoomID)?.getTimelineEvent(sentEvent.eventId)?.root?.getClearType() == EventType.MESSAGE
}
}

View file

@ -92,7 +92,7 @@ class KeyShareTests : InstrumentedTest {
val roomSecondSessionPOV = aliceSession2.getRoom(roomId)
val receivedEvent = roomSecondSessionPOV?.getTimeLineEvent(sentEventId)
val receivedEvent = roomSecondSessionPOV?.getTimelineEvent(sentEventId)
assertNotNull(receivedEvent)
assert(receivedEvent!!.isEncrypted())
@ -382,7 +382,7 @@ class KeyShareTests : InstrumentedTest {
commonTestHelper.sendTextMessage(roomAlicePov, "After", 1)
val roomRoomBobPov = aliceSession.getRoom(roomId)
val beforeJoin = roomRoomBobPov!!.getTimeLineEvent(secondEventId)
val beforeJoin = roomRoomBobPov!!.getTimelineEvent(secondEventId)
var dRes = tryOrNull { bobSession.cryptoService().decryptEvent(beforeJoin!!.root, "") }

View file

@ -80,11 +80,11 @@ class WithHeldTests : InstrumentedTest {
// await for bob unverified session to get the message
testHelper.waitWithLatch { 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
@ -109,7 +109,7 @@ class WithHeldTests : InstrumentedTest {
testHelper.waitWithLatch { 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
ev?.root?.getClearType() == EventType.MESSAGE
}
@ -157,12 +157,12 @@ class WithHeldTests : InstrumentedTest {
// await for bob session to get the message
testHelper.waitWithLatch { 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)
val eventBobPOV = bobSession.getRoom(testData.roomId)?.getTimeLineEvent(eventId)
val eventBobPOV = bobSession.getRoom(testData.roomId)?.getTimelineEvent(eventId)
try {
// .. might need to wait a bit for stability?
bobSession.cryptoService().decryptEvent(eventBobPOV!!.root, "")
@ -190,7 +190,7 @@ class WithHeldTests : InstrumentedTest {
// await for bob SecondSession session to get the message
testHelper.waitWithLatch { 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
testHelper.waitWithLatch { 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
tryOrNull { bobSecondSession.cryptoService().decryptEvent(it.root, "") }
}

View file

@ -22,6 +22,7 @@ import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.FixMethodOrder
import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
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.OlmPkDecryption
@Ignore("Ignored in order to speed up test run time")
@RunWith(AndroidJUnit4::class)
@FixMethodOrder(MethodSorters.JVM)
class KeysBackupPasswordTest : InstrumentedTest {

View file

@ -117,5 +117,5 @@ interface FileService {
/**
* Get size of cached files
*/
fun getCacheSize(): Int
fun getCacheSize(): Long
}

View file

@ -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.
* @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.
@ -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.
* @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.

View file

@ -16,8 +16,11 @@
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.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.RoomMemberSummaryEntity
import org.matrix.android.sdk.internal.database.query.where
@ -32,14 +35,22 @@ internal class ReadReceiptsSummaryMapper @Inject constructor(
return emptyList()
}
val readReceipts = readReceiptsSummaryEntity.readReceipts
return realmSessionProvider.withRealm { realm ->
readReceipts
.mapNotNull {
val roomMember = RoomMemberSummaryEntity.where(realm, roomId = it.roomId, userId = it.userId).findFirst()
?: return@mapNotNull null
ReadReceipt(roomMember.asDomain(), it.originServerTs.toLong())
}
// Avoid opening a new realm if we already have one opened
return if (readReceiptsSummaryEntity.isManaged) {
map(readReceipts, readReceiptsSummaryEntity.realm)
} else {
realmSessionProvider.withRealm { realm ->
map(readReceipts, realm)
}
}
}
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())
}
}
}

View file

@ -323,13 +323,13 @@ internal class DefaultFileService @Inject constructor(
return FileProvider.getUriForFile(context, authority, targetFile)
}
override fun getCacheSize(): Int {
override fun getCacheSize(): Long {
return downloadFolder.walkTopDown()
.onEnter {
Timber.v("Get size of ${it.absolutePath}")
true
}
.sumOf { it.length().toInt() }
.sumOf { it.length() }
}
override fun clearCache() {

View file

@ -529,7 +529,7 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
private fun getPollEvent(roomId: String, eventId: String): TimelineEvent? {
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")
}
}

View file

@ -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.Optional
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.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.di.SessionDatabase
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.timeline.TimelineEventDataSource
import org.matrix.android.sdk.internal.util.fetchCopyMap
import timber.log.Timber
@ -48,7 +47,7 @@ internal class DefaultRelationService @AssistedInject constructor(
private val eventFactory: LocalEchoEventFactory,
private val findReactionEventForUndoTask: FindReactionEventForUndoTask,
private val fetchEditHistoryTask: FetchEditHistoryTask,
private val timelineEventMapper: TimelineEventMapper,
private val timelineEventDataSource: TimelineEventDataSource,
@SessionDatabase private val monarchy: Monarchy
) : RelationService {
@ -58,14 +57,8 @@ internal class DefaultRelationService @AssistedInject constructor(
}
override fun sendReaction(targetEventId: String, reaction: String): Cancelable {
return if (monarchy
.fetchCopyMap(
{ realm ->
TimelineEventEntity.where(realm, roomId, targetEventId).findFirst()
},
{ entity, _ ->
timelineEventMapper.map(entity)
})
val targetTimelineEvent = timelineEventDataSource.getTimelineEvent(roomId, targetEventId)
return if (targetTimelineEvent
?.annotations
?.reactionsSummary
.orEmpty()

View file

@ -21,37 +21,24 @@ import com.zhuinden.monarchy.Monarchy
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
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.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.TimelineEvent
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.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.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.UserId
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.sync.handler.room.ReadReceiptHandler
import org.matrix.android.sdk.internal.session.sync.handler.room.ThreadsAwarenessHandler
import org.matrix.android.sdk.internal.task.TaskExecutor
internal class DefaultTimelineService @AssistedInject constructor(
@Assisted private val roomId: String,
@UserId private val userId: String,
@SessionDatabase private val monarchy: Monarchy,
private val realmSessionProvider: RealmSessionProvider,
private val timelineInput: TimelineInput,
private val taskExecutor: TaskExecutor,
private val contextOfEventTask: GetContextOfEventTask,
private val eventDecryptor: TimelineEventDecryptor,
private val paginationTask: PaginationTask,
@ -62,7 +49,8 @@ internal class DefaultTimelineService @AssistedInject constructor(
private val threadsAwarenessHandler: ThreadsAwarenessHandler,
private val lightweightSettingsStorage: LightweightSettingsStorage,
private val readReceiptHandler: ReadReceiptHandler,
private val coroutineDispatchers: MatrixCoroutineDispatchers
private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val timelineEventDataSource: TimelineEventDataSource
) : TimelineService {
@AssistedFactory
@ -91,27 +79,15 @@ internal class DefaultTimelineService @AssistedInject constructor(
)
}
override fun getTimeLineEvent(eventId: String): TimelineEvent? {
return realmSessionProvider.withRealm { realm ->
TimelineEventEntity.where(realm, roomId = roomId, eventId = eventId).findFirst()?.let {
timelineEventMapper.map(it)
}
}
override fun getTimelineEvent(eventId: String): TimelineEvent? {
return timelineEventDataSource.getTimelineEvent(roomId, eventId)
}
override fun getTimeLineEventLive(eventId: String): LiveData<Optional<TimelineEvent>> {
return LiveTimelineEvent(monarchy, taskExecutor.executorScope, timelineEventMapper, roomId, eventId)
override fun getTimelineEventLive(eventId: String): LiveData<Optional<TimelineEvent>> {
return timelineEventDataSource.getTimelineEventLive(roomId, eventId)
}
override fun getAttachmentMessages(): 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()
}
return timelineEventDataSource.getAttachmentMessages(roomId)
}
}

View file

@ -46,7 +46,7 @@ internal class RealmSendingEventsDataSource(
private val sendingTimelineEventsListener = RealmChangeListener<RealmList<TimelineEventEntity>> { events ->
uiEchoManager.onSentEventsInDatabase(events.map { it.eventId })
frozenSendingTimelineEvents = sendingTimelineEvents?.freeze()
updateFrozenResults(events)
onEventsUpdated(false)
}
@ -59,10 +59,17 @@ internal class RealmSendingEventsDataSource(
override fun stop() {
sendingTimelineEvents?.removeChangeListener(sendingTimelineEventsListener)
updateFrozenResults(null)
sendingTimelineEvents = 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> {
val builtSendingEvents = mutableListOf<TimelineEvent>()
uiEchoManager.getInMemorySendingEvents()

View file

@ -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()
}
}
}

View file

@ -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.sync.SyncPresence
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.SessionWorkerParams
import org.matrix.android.sdk.internal.worker.WorkerParamsFactory
@ -58,7 +57,6 @@ internal class SyncWorker(context: Context, workerParameters: WorkerParameters,
) : SessionWorkerParams
@Inject lateinit var syncTask: SyncTask
@Inject lateinit var taskExecutor: TaskExecutor
@Inject lateinit var workManagerProvider: WorkManagerProvider
override fun injectWith(injector: SessionComponent) {

View file

@ -1,4 +1,5 @@
include ':vector'
include ':vector-config'
include ':matrix-sdk-android'
include ':library:core-utils'
include ':library:ui-styles'

45
tools/ci/render_test_output.py Executable file
View 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
View file

@ -0,0 +1 @@
/build

View 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"
}
}

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="im.vector.app.config" />

View 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>

View file

@ -5,7 +5,7 @@ apply plugin: 'com.google.android.gms.oss-licenses-plugin'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-parcelize'
apply plugin: 'kotlin-kapt'
apply plugin: 'placeholder-resolver'
apply plugin: 'com.likethesalad.stem'
apply plugin: 'dagger.hilt.android.plugin'
kapt {
@ -132,16 +132,9 @@ android {
versionName "${versionMajor}.${versionMinor}.${versionPatch}-sonar"
buildConfigField "String", "GIT_REVISION", "\"${gitRevision()}\""
resValue "string", "git_revision", "\"${gitRevision()}\""
buildConfigField "String", "GIT_REVISION_DATE", "\"${gitRevisionDate()}\""
resValue "string", "git_revision_date", "\"${gitRevisionDate()}\""
buildConfigField "String", "GIT_BRANCH_NAME", "\"${gitBranchName()}\""
resValue "string", "git_branch_name", "\"${gitBranchName()}\""
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"
@ -153,9 +146,6 @@ android {
// This *must* only be set in trusted environments.
buildConfigField "Boolean", "handleCallAssertedIdentityEvents", "false"
// Indicates whether or not threading support is enabled
buildConfigField "Boolean", "THREADING_ENABLED", "${isThreadingEnabled}"
buildConfigField "Boolean", "enableLocationSharing", "true"
buildConfigField "String", "mapTilerKey", "\"fU3vlMsMn4Jb6dnEIFsx\""
@ -233,7 +223,6 @@ android {
applicationIdSuffix ".debug"
resValue "string", "app_name", "Element dbg"
resValue "bool", "debug_mode", "true"
buildConfigField "boolean", "LOW_PRIVACY_LOG_ENABLE", "false"
// Set to true if you want to enable strict mode in debug
buildConfigField "boolean", "ENABLE_STRICT_MODE_LOGS", "false"
@ -244,7 +233,6 @@ android {
release {
resValue "string", "app_name", "Element"
resValue "bool", "debug_mode", "false"
buildConfigField "boolean", "LOW_PRIVACY_LOG_ENABLE", "false"
buildConfigField "boolean", "ENABLE_STRICT_MODE_LOGS", "false"
@ -332,7 +320,7 @@ android {
}
dependencies {
implementation project(":vector-config")
implementation project(":matrix-sdk-android")
implementation project(":matrix-sdk-android-flow")
implementation project(":library:jsonviewer")
@ -376,7 +364,7 @@ dependencies {
implementation 'com.facebook.stetho:stetho:1.6.0'
// Phone number https://github.com/google/libphonenumber
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.43'
implementation 'com.googlecode.libphonenumber:libphonenumber:8.12.44'
// FlowBinding
implementation libs.github.flowBinding

View file

@ -6,6 +6,7 @@
<issue id="MissingTranslation" severity="ignore" />
<issue id="TypographyEllipsis" severity="error" />
<issue id="ImpliedQuantity" severity="warning" />
<issue id="MissingQuantity" severity="warning" />
<issue id="UnusedQuantity" severity="error" />
<issue id="IconXmlAndPng" severity="error" />
<issue id="IconDipSize" severity="error" />
@ -14,6 +15,39 @@
<issue id="IconExpectedSize" 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 -->
<issue id="ButtonOrder" severity="error" />
<issue id="TextFields" severity="error" />
@ -75,14 +109,6 @@
<issue id="Typos" 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 -->
<issue id="JvmStaticProvidesInObjectDetector" severity="error" />
</lint>

View file

@ -23,8 +23,11 @@ import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import im.vector.app.features.DefaultVectorFeatures
import im.vector.app.features.DefaultVectorOverrides
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.DebugVectorOverrides
@InstallIn(SingletonComponent::class)
@Module
@ -33,6 +36,9 @@ interface FeaturesModule {
@Binds
fun bindFeatures(debugFeatures: DebugVectorFeatures): VectorFeatures
@Binds
fun bindOverrides(debugOverrides: DebugVectorOverrides): VectorOverrides
companion object {
@Provides
@ -44,5 +50,15 @@ interface FeaturesModule {
fun providesDebugVectorFeatures(context: Context, defaultVectorFeatures: DefaultVectorFeatures): DebugVectorFeatures {
return DebugVectorFeatures(context, defaultVectorFeatures)
}
@Provides
fun providesDefaultVectorOverrides(): DefaultVectorOverrides {
return DefaultVectorOverrides()
}
@Provides
fun providesDebugVectorOverrides(context: Context): DebugVectorOverrides {
return DebugVectorOverrides(context)
}
}
}

View file

@ -53,7 +53,7 @@ class DebugFeaturesStateFactory @Inject constructor(
label = "FTUE Personalize profile",
key = DebugFeatureKeys.onboardingPersonalize,
factory = VectorFeatures::isOnboardingPersonalizeEnabled
)
),
))
}

View file

@ -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
}
}
}

View file

@ -43,9 +43,13 @@ class DebugPrivateSettingsFragment : VectorBaseFragment<FragmentDebugPrivateSett
views.forceDialPadTabDisplay.setOnCheckedChangeListener { _, isChecked ->
viewModel.handle(DebugPrivateSettingsViewActions.SetDialPadVisibility(isChecked))
}
views.forceLoginFallback.setOnCheckedChangeListener { _, isChecked ->
viewModel.handle(DebugPrivateSettingsViewActions.SetForceLoginFallbackEnabled(isChecked))
}
}
override fun invalidate() = withState(viewModel) {
views.forceDialPadTabDisplay.isChecked = it.dialPadVisible
views.forceLoginFallback.isChecked = it.forceLoginFallback
}
}

View file

@ -20,4 +20,5 @@ import im.vector.app.core.platform.VectorViewModelAction
sealed class DebugPrivateSettingsViewActions : VectorViewModelAction {
data class SetDialPadVisibility(val force: Boolean) : DebugPrivateSettingsViewActions()
data class SetForceLoginFallbackEnabled(val force: Boolean) : DebugPrivateSettingsViewActions()
}

View file

@ -24,12 +24,12 @@ import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.platform.EmptyViewEvents
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
class DebugPrivateSettingsViewModel @AssistedInject constructor(
@Assisted initialState: DebugPrivateSettingsViewState,
private val vectorDataStore: VectorDataStore
private val debugVectorOverrides: DebugVectorOverrides
) : VectorViewModel<DebugPrivateSettingsViewState, DebugPrivateSettingsViewActions, EmptyViewEvents>(initialState) {
@AssistedFactory
@ -44,22 +44,32 @@ class DebugPrivateSettingsViewModel @AssistedInject constructor(
}
private fun observeVectorDataStore() {
vectorDataStore.forceDialPadDisplayFlow.setOnEach {
debugVectorOverrides.forceDialPad.setOnEach {
copy(
dialPadVisible = it
)
}
debugVectorOverrides.forceLoginFallback.setOnEach {
copy(forceLoginFallback = it)
}
}
override fun handle(action: DebugPrivateSettingsViewActions) {
when (action) {
is DebugPrivateSettingsViewActions.SetDialPadVisibility -> handleSetDialPadVisibility(action)
is DebugPrivateSettingsViewActions.SetDialPadVisibility -> handleSetDialPadVisibility(action)
is DebugPrivateSettingsViewActions.SetForceLoginFallbackEnabled -> handleSetForceLoginFallbackEnabled(action)
}
}
private fun handleSetDialPadVisibility(action: DebugPrivateSettingsViewActions.SetDialPadVisibility) {
viewModelScope.launch {
vectorDataStore.setForceDialPadDisplay(action.force)
debugVectorOverrides.setForceDialPadDisplay(action.force)
}
}
private fun handleSetForceLoginFallbackEnabled(action: DebugPrivateSettingsViewActions.SetForceLoginFallbackEnabled) {
viewModelScope.launch {
debugVectorOverrides.setForceLoginFallback(action.force)
}
}
}

View file

@ -19,5 +19,6 @@ package im.vector.app.features.debug.settings
import com.airbnb.mvrx.MavericksState
data class DebugPrivateSettingsViewState(
val dialPadVisible: Boolean = false
val dialPadVisible: Boolean = false,
val forceLoginFallback: Boolean = false,
) : MavericksState

View file

@ -25,6 +25,12 @@
android:layout_height="wrap_content"
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>
</ScrollView>

View file

@ -213,7 +213,7 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
try {
val session = activeSessionHolder.getSafeActiveSession() ?: return false
val room = session.getRoom(roomId) ?: return false
return room.getTimeLineEvent(eventId) != null
return room.getTimelineEvent(eventId) != null
} catch (e: Exception) {
Timber.tag(loggerTag.value).e(e, "## isEventAlreadyKnown() : failed to check if the event was already defined")
}

View file

@ -99,6 +99,7 @@ import im.vector.app.features.matrixto.MatrixToRoomSpaceFragment
import im.vector.app.features.matrixto.MatrixToUserFragment
import im.vector.app.features.onboarding.ftueauth.FtueAuthAccountCreatedFragment
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.FtueAuthLoginFragment
import im.vector.app.features.onboarding.ftueauth.FtueAuthResetPasswordFragment
@ -479,6 +480,11 @@ interface FragmentModule {
@FragmentKey(FtueAuthAccountCreatedFragment::class)
fun bindFtueAuthAccountCreatedFragment(fragment: FtueAuthAccountCreatedFragment): Fragment
@Binds
@IntoMap
@FragmentKey(FtueAuthChooseDisplayNameFragment::class)
fun bindFtueAuthChooseDisplayNameFragment(fragment: FtueAuthChooseDisplayNameFragment): Fragment
@Binds
@IntoMap
@FragmentKey(UserListFragment::class)

View file

@ -58,6 +58,7 @@ import im.vector.app.features.login.LoginViewModel
import im.vector.app.features.login2.LoginViewModel2
import im.vector.app.features.login2.created.AccountCreatedViewModel
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.poll.create.CreatePollViewModel
import im.vector.app.features.qrcode.QrCodeScannerViewModel
@ -594,4 +595,9 @@ interface MavericksViewModelModule {
@IntoMap
@MavericksViewModelKey(LocationSharingViewModel::class)
fun createLocationSharingViewModelFactory(factory: LocationSharingViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
@Binds
@IntoMap
@MavericksViewModelKey(VectorAttachmentViewerViewModel::class)
fun vectorAttachmentViewerViewModelFactory(factory: VectorAttachmentViewerViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
}

View file

@ -169,22 +169,24 @@ const val POP_BACK_STACK_EXCLUSIVE = 0
fun Fragment.queryExportKeys(userId: String, activityResultLauncher: ActivityResultLauncher<Intent>) {
val timestamp = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(Date())
val appName = getString(R.string.app_name).replace(" ", "-")
selectTxtFileToWrite(
activity = requireActivity(),
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)
)
}
fun Activity.queryExportKeys(userId: String, activityResultLauncher: ActivityResultLauncher<Intent>) {
val timestamp = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(Date())
val appName = getString(R.string.app_name).replace(" ", "-")
selectTxtFileToWrite(
activity = this,
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)
)
}

View file

@ -125,11 +125,11 @@ fun getFileExtension(fileUri: String): String? {
* Size
* ========================================================================================== */
fun getSizeOfFiles(root: File): Int {
fun getSizeOfFiles(root: File): Long {
return root.walkTopDown()
.onEnter {
Timber.v("Get size of ${it.absolutePath}")
true
}
.sumOf { it.length().toInt() }
.sumOf { it.length() }
}

View file

@ -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)
}

View file

@ -40,6 +40,16 @@ data class Interaction(
) : VectorAnalyticsEvent {
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.
*/
@ -52,8 +62,8 @@ data class Interaction(
SpacePanelSwitchSpace,
/**
* User clicked the create room button in the + context menu of the room
* list header in Element Web/Desktop.
* User clicked the create room button in the add existing room to space
* dialog in Element Web/Desktop.
*/
WebAddExistingToSpaceDialogCreateRoomButton,
@ -153,6 +163,12 @@ data class Interaction(
*/
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
* in the room list in Element Web/Desktop.
@ -189,6 +205,12 @@ data class Interaction(
*/
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
* settings dialog in Element Web/Desktop.
@ -214,14 +236,26 @@ data class Interaction(
WebSettingsSidebarTabSpacesCheckbox,
/**
* User clicked the create room button in the + context menu of the room
* list header in Element Web/Desktop.
* User clicked the explore rooms button in the context menu of a space
* 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,
/**
* User clicked the create room button in the + context menu of the room
* list header in Element Web/Desktop.
* User clicked the new room button in the context menu on the space
* home in Element Web/Desktop.
*/
WebSpaceHomeCreateRoomButton,

View file

@ -44,6 +44,11 @@ data class JoinedRoom(
) : VectorAnalyticsEvent {
enum class Trigger {
/**
* Room joined via an invite.
*/
Invite,
/**
* Room joined via a push/desktop notification.
*/
@ -54,6 +59,16 @@ data class JoinedRoom(
*/
RoomDirectory,
/**
* Room joined via its preview.
*/
RoomPreview,
/**
* Room joined via the /join slash command.
*/
SlashCommand,
/**
* Room joined via the space hierarchy view.
*/

View file

@ -105,6 +105,11 @@ data class MobileScreen(
*/
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
* screen.
@ -132,6 +137,16 @@ data class MobileScreen(
*/
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
* room.
@ -180,21 +195,67 @@ data class MobileScreen(
*/
Settings,
/**
* The advanced settings screen (developer mode, rageshake, push
* notification rules)
*/
SettingsAdvanced,
/**
* The settings screen to change the default notification options.
*/
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.
*/
SettingsMentionsAndKeywords,
/**
* The notifications settings screen.
*/
SettingsNotifications,
/**
* The preferences screen (theme, language, editor preferences, etc.
*/
SettingsPreferences,
/**
* The global security settings screen.
*/
SettingsSecurity,
/**
* The calls settings screen.
*/
SettingsVoiceVideo,
/**
* The sidebar shown on mobile with spaces, settings etc.
*/

View file

@ -25,6 +25,18 @@ import im.vector.app.features.analytics.itf.VectorAnalyticsEvent
* Triggered when the user changes rooms.
*/
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.
*/
@ -179,10 +191,36 @@ data class ViewRoom(
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 getProperties(): Map<String, Any>? {
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) }
viaKeyboard?.let { put("viaKeyboard", it) }
}.takeIf { it.isNotEmpty() }

View file

@ -28,6 +28,7 @@ import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.extensions.singletonEntryPoint
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.lookup.CallProtocolsChecker
import im.vector.app.features.call.webrtc.WebRtcCallManager
@ -67,7 +68,8 @@ class HomeDetailViewModel @AssistedInject constructor(
private val callManager: WebRtcCallManager,
private val directRoomHelper: DirectRoomHelper,
private val appStateHandler: AppStateHandler,
private val autoAcceptInvites: AutoAcceptInvites
private val autoAcceptInvites: AutoAcceptInvites,
private val vectorOverrides: VectorOverrides
) : VectorViewModel<HomeDetailViewState, HomeDetailAction, HomeDetailViewEvents>(initialState),
CallProtocolsChecker.Listener {
@ -106,8 +108,7 @@ class HomeDetailViewModel @AssistedInject constructor(
pushCounter = nbOfPush
)
}
vectorDataStore.forceDialPadDisplayFlow.setOnEach { force ->
vectorOverrides.forceDialPad.setOnEach { force ->
copy(
forceDialPadTab = force
)

View file

@ -92,7 +92,6 @@ sealed class RoomDetailAction : VectorViewModelAction {
data class UpdateJoinJitsiCallStatus(val conferenceEvent: ConferenceEvent) : RoomDetailAction()
data class OpenOrCreateDm(val userId: String) : RoomDetailAction()
data class JumpToReadReceipt(val userId: String) : RoomDetailAction()
object QuickActionInvitePeople : RoomDetailAction()
object QuickActionSetAvatar : RoomDetailAction()

View file

@ -17,7 +17,6 @@
package im.vector.app.features.home.room.detail
sealed class RoomDetailPendingAction {
data class OpenOrCreateDm(val userId: String) : RoomDetailPendingAction()
data class JumpToReadReceipt(val userId: String) : RoomDetailPendingAction()
data class MentionUser(val userId: String) : RoomDetailPendingAction()
data class OpenRoom(val roomId: String, val closeCurrentRoom: Boolean = false) : RoomDetailPendingAction()

View file

@ -1247,8 +1247,6 @@ class TimelineFragment @Inject constructor(
timelineViewModel.handle(RoomDetailAction.JumpToReadReceipt(roomDetailPendingAction.userId))
is RoomDetailPendingAction.MentionUser ->
insertUserDisplayNameInTextEditor(roomDetailPendingAction.userId)
is RoomDetailPendingAction.OpenOrCreateDm ->
timelineViewModel.handle(RoomDetailAction.OpenOrCreateDm(roomDetailPendingAction.userId))
is RoomDetailPendingAction.OpenRoom ->
handleOpenRoom(RoomDetailViewEvents.OpenRoom(roomDetailPendingAction.roomId, roomDetailPendingAction.closeCurrentRoom))
}.exhaustive

View file

@ -420,7 +420,6 @@ class TimelineViewModel @AssistedInject constructor(
is RoomDetailAction.RemoveWidget -> handleDeleteWidget(action.widgetId)
is RoomDetailAction.EnsureNativeWidgetAllowed -> handleCheckWidgetAllowed(action)
is RoomDetailAction.CancelSend -> handleCancel(action)
is RoomDetailAction.OpenOrCreateDm -> handleOpenOrCreateDm(action)
is RoomDetailAction.JumpToReadReceipt -> handleJumpToReadReceipt(action)
RoomDetailAction.QuickActionInvitePeople -> handleInvitePeople()
RoomDetailAction.QuickActionSetAvatar -> handleQuickSetAvatar()
@ -500,20 +499,6 @@ class TimelineViewModel @AssistedInject constructor(
_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) {
room.getUserReadReceipt(action.userId)
?.let { handleNavigateToEvent(RoomDetailAction.NavigateToEvent(it, true)) }
@ -745,7 +730,7 @@ class TimelineViewModel @AssistedInject constructor(
}
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)
}
@ -785,7 +770,7 @@ class TimelineViewModel @AssistedInject constructor(
}
// We need to update this with the related m.replace also (to move read receipt)
action.event.annotations?.editSummary?.sourceEvents?.forEach {
room.getTimeLineEvent(it)?.let { event ->
room.getTimelineEvent(it)?.let { event ->
visibleEventsSource.post(RoomDetailAction.TimelineEventTurnsVisible(event))
}
}
@ -813,7 +798,7 @@ class TimelineViewModel @AssistedInject constructor(
notificationDrawerManager.updateEvents { it.clearMemberShipNotificationForRoom(initialState.roomId) }
viewModelScope.launch {
try {
session.leaveRoom(room.roomId)
session.leaveRoom(room.roomId)
} catch (throwable: Throwable) {
_viewEvents.post(RoomDetailViewEvents.Failure(throwable, showInDialog = true))
}
@ -889,7 +874,7 @@ class TimelineViewModel @AssistedInject constructor(
private fun handleResendEvent(action: RoomDetailAction.ResendMessage) {
val targetEventId = action.eventId
room.getTimeLineEvent(targetEventId)?.let {
room.getTimelineEvent(targetEventId)?.let {
// State must be UNDELIVERED or Failed
if (!it.root.sendState.hasFailed()) {
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) {
val targetEventId = action.eventId
room.getTimeLineEvent(targetEventId)?.let {
room.getTimelineEvent(targetEventId)?.let {
// State must be UNDELIVERED or Failed
if (!it.root.sendState.hasFailed()) {
Timber.e("Cannot resend message, it is not failed, Cancel first")
@ -923,7 +908,7 @@ class TimelineViewModel @AssistedInject constructor(
return
}
val targetEventId = action.eventId
room.getTimeLineEvent(targetEventId)?.let {
room.getTimelineEvent(targetEventId)?.let {
// State must be in one of the sending states
if (!it.root.sendState.isSending()) {
Timber.e("Cannot cancel message, it is not sending")
@ -1049,14 +1034,14 @@ class TimelineViewModel @AssistedInject constructor(
private fun handleReRequestKeys(action: RoomDetailAction.ReRequestKeys) {
// 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)
_viewEvents.post(RoomDetailViewEvents.ShowMessage(stringProvider.getString(R.string.e2e_re_request_encryption_key_dialog_content)))
}
}
private fun handleTapOnFailedToDecrypt(action: RoomDetailAction.TapOnFailedToDecrypt) {
room.getTimeLineEvent(action.eventId)?.let {
room.getTimelineEvent(action.eventId)?.let {
val code = when (it.root.mCryptoError) {
MXCryptoError.ErrorType.KEYS_WITHHELD -> {
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
if (LocalEcho.isLocalEchoId(action.eventId)) return
// 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
if (currentVote != action.optionKey) {
room.voteToPoll(action.eventId, action.optionKey)

View file

@ -143,7 +143,7 @@ class MessageComposerViewModel @AssistedInject constructor(
}
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())) }
}
}
@ -175,13 +175,13 @@ class MessageComposerViewModel @AssistedInject constructor(
}
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)) }
}
}
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)) }
}
}
@ -479,7 +479,7 @@ class MessageComposerViewModel @AssistedInject constructor(
if (inReplyTo != null) {
// TODO check if same content?
room.getTimeLineEvent(inReplyTo)?.let {
room.getTimelineEvent(inReplyTo)?.let {
room.editReply(state.sendMode.timelineEvent, it, action.text.toString())
}
} else {
@ -555,17 +555,17 @@ class MessageComposerViewModel @AssistedInject constructor(
sendMode = when (currentDraft) {
is UserDraft.Regular -> SendMode.Regular(currentDraft.content, false)
is UserDraft.Quote -> {
room.getTimeLineEvent(currentDraft.linkedEventId)?.let { timelineEvent ->
room.getTimelineEvent(currentDraft.linkedEventId)?.let { timelineEvent ->
SendMode.Quote(timelineEvent, currentDraft.content)
}
}
is UserDraft.Reply -> {
room.getTimeLineEvent(currentDraft.linkedEventId)?.let { timelineEvent ->
room.getTimelineEvent(currentDraft.linkedEventId)?.let { timelineEvent ->
SendMode.Reply(timelineEvent, currentDraft.content)
}
}
is UserDraft.Edit -> {
room.getTimeLineEvent(currentDraft.linkedEventId)?.let { timelineEvent ->
room.getTimelineEvent(currentDraft.linkedEventId)?.let { timelineEvent ->
SendMode.Edit(timelineEvent, currentDraft.content)
}
}

View file

@ -343,24 +343,6 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
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)) {
// TODO copy images? html? see ClipBoard
add(EventSharedAction.Copy(messageContent!!.body))
@ -382,12 +364,30 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
add(EventSharedAction.ViewEditHistory(informationData))
}
if (canSave(msgType) && messageContent is MessageWithAttachmentContent) {
add(EventSharedAction.Save(timelineEvent.eventId, messageContent))
}
if (canShare(msgType)) {
add(EventSharedAction.Share(timelineEvent.eventId, messageContent!!))
}
if (canSave(msgType) && messageContent is MessageWithAttachmentContent) {
add(EventSharedAction.Save(timelineEvent.eventId, messageContent))
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
))
}
}
}

View file

@ -65,7 +65,7 @@ class VerificationItemFactory @Inject constructor(
?: return ignoredConclusion(params)
// 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)
// If it's not a request ignore this event

View file

@ -86,7 +86,7 @@ class ViewReactionsViewModel @AssistedInject constructor(@Assisted
annotationsSummary.reactionsSummary
.flatMap { reactionsSummary ->
reactionsSummary.sourceEvents.map {
val event = room.getTimeLineEvent(it)
val event = room.getTimelineEvent(it)
?: throw RuntimeException("Your eventId is not valid")
ReactionInfo(
event.root.eventId!!,

View file

@ -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)
}

View file

@ -30,35 +30,33 @@ class AttachmentOverlayView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr), AttachmentEventListener {
var onShareCallback: (() -> Unit)? = null
var onBack: (() -> Unit)? = null
var onPlayPause: ((play: Boolean) -> Unit)? = null
var videoSeekTo: ((progress: Int) -> Unit)? = null
var interactionListener: AttachmentInteractionListener? = null
val views: MergeImageAttachmentOverlayBinding
var isPlaying = false
var suspendSeekBarUpdate = false
private var isPlaying = false
private var suspendSeekBarUpdate = false
init {
inflate(context, R.layout.merge_image_attachment_overlay, this)
views = MergeImageAttachmentOverlayBinding.bind(this)
setBackgroundColor(Color.TRANSPARENT)
views.overlayBackButton.setOnClickListener {
onBack?.invoke()
interactionListener?.onDismiss()
}
views.overlayShareButton.setOnClickListener {
onShareCallback?.invoke()
interactionListener?.onShare()
}
views.overlayDownloadButton.setOnClickListener {
interactionListener?.onDownload()
}
views.overlayPlayPauseButton.setOnClickListener {
onPlayPause?.invoke(!isPlaying)
interactionListener?.onPlayPause(!isPlaying)
}
views.overlaySeekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
if (fromUser) {
videoSeekTo?.invoke(progress)
interactionListener?.videoSeekTo(progress)
}
}

View file

@ -49,14 +49,7 @@ abstract class BaseAttachmentProvider<Type>(
private val stringProvider: StringProvider
) : AttachmentSourceProvider {
interface InteractionListener {
fun onDismissTapped()
fun onShareTapped()
fun onPlayPause(play: Boolean)
fun videoSeekTo(percent: Int)
}
var interactionListener: InteractionListener? = null
var interactionListener: AttachmentInteractionListener? = null
private var overlayView: AttachmentOverlayView? = null
@ -68,18 +61,7 @@ abstract class BaseAttachmentProvider<Type>(
if (position == -1) return null
if (overlayView == null) {
overlayView = AttachmentOverlayView(context)
overlayView?.onBack = {
interactionListener?.onDismissTapped()
}
overlayView?.onShareCallback = {
interactionListener?.onShareTapped()
}
overlayView?.onPlayPause = { play ->
interactionListener?.onPlayPause(play)
}
overlayView?.videoSeekTo = { percent ->
interactionListener?.videoSeekTo(percent)
}
overlayView?.interactionListener = interactionListener
}
val timelineEvent = getTimelineEventAtPosition(position)

View file

@ -81,7 +81,7 @@ class DataAttachmentRoomProvider(
override fun getTimelineEventAtPosition(position: Int): TimelineEvent? {
val item = getItem(position)
return room?.getTimeLineEvent(item.eventId)
return room?.getTimelineEvent(item.eventId)
}
override suspend fun getFileForSharing(position: Int): File? {

View 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