Merge branch 'release/26.03.3'
This commit is contained in:
commit
75841761cc
241 changed files with 2794 additions and 1612 deletions
4
.github/dependabot.yml
vendored
4
.github/dependabot.yml
vendored
|
|
@ -13,6 +13,8 @@ updates:
|
|||
open-pull-requests-limit: 0
|
||||
reviewers:
|
||||
- "element-hq/element-x-android-reviewers"
|
||||
cooldown:
|
||||
default-days: 7
|
||||
# Updates for Gradle dependencies used in the app
|
||||
- package-ecosystem: "gradle"
|
||||
directory: "/"
|
||||
|
|
@ -21,3 +23,5 @@ updates:
|
|||
open-pull-requests-limit: 0
|
||||
reviewers:
|
||||
- "element-hq/element-x-android-reviewers"
|
||||
cooldown:
|
||||
default-days: 7
|
||||
|
|
|
|||
14
.github/workflows/build.yml
vendored
14
.github/workflows/build.yml
vendored
|
|
@ -7,6 +7,8 @@ on:
|
|||
push:
|
||||
branches: [ develop ]
|
||||
|
||||
permissions: {}
|
||||
|
||||
# Enrich gradle.properties for CI/CD
|
||||
env:
|
||||
GRADLE_OPTS: -Dorg.gradle.jvmargs=-Xmx8g -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError -XX:+UseG1GC -Dkotlin.daemon.jvm.options=-Xmx4g
|
||||
|
|
@ -16,6 +18,9 @@ jobs:
|
|||
build:
|
||||
name: Build APKs
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
# For NejcZdovc/comment-pr
|
||||
pull-requests: write
|
||||
strategy:
|
||||
matrix:
|
||||
variant: [debug, release, nightly]
|
||||
|
|
@ -39,18 +44,19 @@ jobs:
|
|||
docker-images: true
|
||||
swap-storage: false
|
||||
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
# Ensure we are building the branch and not the branch after being merged on develop
|
||||
# https://github.com/actions/checkout/issues/881
|
||||
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }}
|
||||
persist-credentials: false
|
||||
- name: Use JDK 21
|
||||
uses: actions/setup-java@v5
|
||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||
with:
|
||||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '21'
|
||||
- name: Configure gradle
|
||||
uses: gradle/actions/setup-gradle@v5
|
||||
uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5.0.2
|
||||
with:
|
||||
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
|
||||
- name: Assemble debug APKs
|
||||
|
|
@ -68,7 +74,7 @@ jobs:
|
|||
run: ./gradlew :app:assembleGplayDebug app:assembleFDroidDebug -PallWarningsAsErrors=true $CI_GRADLE_ARG_PROPERTIES
|
||||
- name: Upload debug APKs
|
||||
if: ${{ matrix.variant == 'debug' }}
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: elementx-debug
|
||||
path: |
|
||||
|
|
|
|||
11
.github/workflows/build_enterprise.yml
vendored
11
.github/workflows/build_enterprise.yml
vendored
|
|
@ -7,6 +7,8 @@ on:
|
|||
push:
|
||||
branches: [ develop ]
|
||||
|
||||
permissions: {}
|
||||
|
||||
# Enrich gradle.properties for CI/CD
|
||||
env:
|
||||
GRADLE_OPTS: -Dorg.gradle.jvmargs=-Xmx8g -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError -XX:+UseG1GC -Dkotlin.daemon.jvm.options=-Xmx4g
|
||||
|
|
@ -41,11 +43,12 @@ jobs:
|
|||
docker-images: true
|
||||
swap-storage: false
|
||||
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
# Ensure we are building the branch and not the branch after being merged on develop
|
||||
# https://github.com/actions/checkout/issues/881
|
||||
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }}
|
||||
persist-credentials: false
|
||||
- name: Add SSH private keys for submodule repositories
|
||||
uses: webfactory/ssh-agent@a6f90b1f127823b31d4d4a8d96047790581349bd # v0.9.1
|
||||
with:
|
||||
|
|
@ -53,12 +56,12 @@ jobs:
|
|||
- name: Clone submodules
|
||||
run: git submodule update --init --recursive
|
||||
- name: Use JDK 21
|
||||
uses: actions/setup-java@v5
|
||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||
with:
|
||||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '21'
|
||||
- name: Configure gradle
|
||||
uses: gradle/actions/setup-gradle@v5
|
||||
uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5.0.2
|
||||
with:
|
||||
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
|
||||
- name: Assemble debug Gplay Enterprise APK
|
||||
|
|
@ -76,7 +79,7 @@ jobs:
|
|||
run: ./gradlew :app:assembleGplayDebug -PallWarningsAsErrors=true $CI_GRADLE_ARG_PROPERTIES
|
||||
- name: Upload debug Enterprise APKs
|
||||
if: ${{ matrix.variant == 'debug' }}
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: elementx-enterprise-debug
|
||||
path: |
|
||||
|
|
|
|||
6
.github/workflows/danger.yml
vendored
6
.github/workflows/danger.yml
vendored
|
|
@ -2,6 +2,8 @@ name: Danger CI
|
|||
|
||||
on: [pull_request, merge_group]
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
|
@ -9,7 +11,9 @@ jobs:
|
|||
# Skip in forks, it doesn't work even with the fallback token
|
||||
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Add SSH private keys for submodule repositories
|
||||
uses: webfactory/ssh-agent@a6f90b1f127823b31d4d4a8d96047790581349bd # v0.9.1
|
||||
with:
|
||||
|
|
|
|||
6
.github/workflows/fork-pr-notice.yml
vendored
6
.github/workflows/fork-pr-notice.yml
vendored
|
|
@ -2,11 +2,13 @@ name: Community PR notice
|
|||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request_target:
|
||||
pull_request_target: # zizmor: ignore[dangerous-triggers]
|
||||
types:
|
||||
- opened
|
||||
- reopened
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
welcome:
|
||||
runs-on: ubuntu-latest
|
||||
|
|
@ -15,7 +17,7 @@ jobs:
|
|||
if: github.event.pull_request.base.repo.full_name != github.event.pull_request.head.repo.full_name
|
||||
steps:
|
||||
- name: Add auto-generated commit warning
|
||||
uses: actions/github-script@v8
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
||||
with:
|
||||
script: |
|
||||
github.rest.issues.createComment({
|
||||
|
|
|
|||
8
.github/workflows/generate_github_pages.yml
vendored
8
.github/workflows/generate_github_pages.yml
vendored
|
|
@ -5,6 +5,8 @@ on:
|
|||
# At 00:00 on every Tuesday UTC
|
||||
- cron: '0 0 * * 2'
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
generate-github-pages:
|
||||
runs-on: ubuntu-latest
|
||||
|
|
@ -14,16 +16,16 @@ jobs:
|
|||
- name: ⏬ Checkout with LFS
|
||||
uses: nschloe/action-cached-lfs-checkout@f46300cd8952454b9f0a21a3d133d4bd5684cfc2 # v1.2.3
|
||||
- name: Use JDK 21
|
||||
uses: actions/setup-java@v5
|
||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||
with:
|
||||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '21'
|
||||
- name: Configure gradle
|
||||
uses: gradle/actions/setup-gradle@v5
|
||||
uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5.0.2
|
||||
with:
|
||||
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
|
||||
- name: Set up Python 3.12
|
||||
uses: actions/setup-python@v6
|
||||
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
with:
|
||||
python-version: 3.14
|
||||
- name: Run World screenshots generation script
|
||||
|
|
|
|||
8
.github/workflows/gradle-wrapper-update.yml
vendored
8
.github/workflows/gradle-wrapper-update.yml
vendored
|
|
@ -5,14 +5,18 @@ on:
|
|||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
update-gradle-wrapper:
|
||||
runs-on: ubuntu-latest
|
||||
# Skip in forks
|
||||
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-java@v5
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||
name: Use JDK 21
|
||||
if: (github.event_name == 'pull_request' && github.event.pull_request.fork == null) || github.event_name == 'workflow_dispatch'
|
||||
with:
|
||||
|
|
|
|||
20
.github/workflows/maestro-local.yml
vendored
20
.github/workflows/maestro-local.yml
vendored
|
|
@ -5,6 +5,8 @@ on:
|
|||
workflow_dispatch:
|
||||
pull_request:
|
||||
|
||||
permissions: {}
|
||||
|
||||
# Enrich gradle.properties for CI/CD
|
||||
env:
|
||||
GRADLE_OPTS: -Dorg.gradle.jvmargs=-Xmx8g -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError -XX:+UseG1GC -Dkotlin.daemon.jvm.options=-Xmx4g
|
||||
|
|
@ -36,18 +38,19 @@ jobs:
|
|||
docker-images: true
|
||||
swap-storage: false
|
||||
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
# Ensure we are building the branch and not the branch after being merged on develop
|
||||
# https://github.com/actions/checkout/issues/881
|
||||
ref: ${{ github.ref }}
|
||||
- uses: actions/setup-java@v5
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||
name: Use JDK 21
|
||||
with:
|
||||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '21'
|
||||
- name: Configure gradle
|
||||
uses: gradle/actions/setup-gradle@v5
|
||||
uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5.0.2
|
||||
with:
|
||||
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
|
||||
- name: Assemble debug APK
|
||||
|
|
@ -57,7 +60,7 @@ jobs:
|
|||
ELEMENT_ANDROID_MAPTILER_LIGHT_MAP_ID: ${{ secrets.MAPTILER_LIGHT_MAP_ID }}
|
||||
ELEMENT_ANDROID_MAPTILER_DARK_MAP_ID: ${{ secrets.MAPTILER_DARK_MAP_ID }}
|
||||
- name: Upload APK as artifact
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: elementx-apk-maestro
|
||||
path: |
|
||||
|
|
@ -75,14 +78,15 @@ jobs:
|
|||
concurrency:
|
||||
group: maestro-test
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
if: (github.event_name == 'pull_request' && github.event.pull_request.fork == null) || github.event_name == 'workflow_dispatch'
|
||||
with:
|
||||
# Ensure we are building the branch and not the branch after being merged on develop
|
||||
# https://github.com/actions/checkout/issues/881
|
||||
ref: ${{ github.ref }}
|
||||
persist-credentials: false
|
||||
- name: Download APK artifact from previous job
|
||||
uses: actions/download-artifact@v8
|
||||
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
|
||||
with:
|
||||
name: elementx-apk-maestro
|
||||
- name: Enable KVM group perms
|
||||
|
|
@ -94,7 +98,7 @@ jobs:
|
|||
run: curl -fsSL "https://get.maestro.mobile.dev" | bash
|
||||
- name: Run Maestro tests in emulator
|
||||
id: maestro_test
|
||||
uses: reactivecircus/android-emulator-runner@v2
|
||||
uses: reactivecircus/android-emulator-runner@b530d96654c385303d652368551fb075bc2f0b6b # v2.35.0
|
||||
continue-on-error: true
|
||||
env:
|
||||
MAESTRO_USERNAME: maestroelement
|
||||
|
|
@ -115,7 +119,7 @@ jobs:
|
|||
script: |
|
||||
.github/workflows/scripts/maestro/maestro-local-with-screen-recording.sh app-gplay-x86_64-debug.apk
|
||||
- name: Upload test results
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: test-results
|
||||
path: |
|
||||
|
|
|
|||
8
.github/workflows/nightly.yml
vendored
8
.github/workflows/nightly.yml
vendored
|
|
@ -6,6 +6,8 @@ on:
|
|||
# Every nights at 4
|
||||
- cron: "0 4 * * *"
|
||||
|
||||
permissions: {}
|
||||
|
||||
env:
|
||||
GRADLE_OPTS: -Dorg.gradle.jvmargs=-Xmx8g -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError -XX:+UseG1GC -Dkotlin.daemon.jvm.options=-Xmx4g
|
||||
CI_GRADLE_ARG_PROPERTIES: --stacktrace --no-daemon -Dsonar.gradle.skipCompile=true --no-configuration-cache
|
||||
|
|
@ -30,9 +32,11 @@ jobs:
|
|||
docker-images: true
|
||||
swap-storage: false
|
||||
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Use JDK 21
|
||||
uses: actions/setup-java@v5
|
||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||
with:
|
||||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '21'
|
||||
|
|
|
|||
18
.github/workflows/nightlyReports.yml
vendored
18
.github/workflows/nightlyReports.yml
vendored
|
|
@ -6,6 +6,8 @@ on:
|
|||
# Every nights at 5
|
||||
- cron: "0 5 * * *"
|
||||
|
||||
permissions: {}
|
||||
|
||||
# Enrich gradle.properties for CI/CD
|
||||
env:
|
||||
GRADLE_OPTS: -Dorg.gradle.jvmargs=-Xmx8g -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError -XX:+UseG1GC -Dkotlin.daemon.jvm.options=-Xmx4g
|
||||
|
|
@ -35,13 +37,13 @@ jobs:
|
|||
uses: nschloe/action-cached-lfs-checkout@f46300cd8952454b9f0a21a3d133d4bd5684cfc2 # v1.2.3
|
||||
|
||||
- name: Use JDK 21
|
||||
uses: actions/setup-java@v5
|
||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||
with:
|
||||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '21'
|
||||
|
||||
- name: Configure gradle
|
||||
uses: gradle/actions/setup-gradle@v5
|
||||
uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5.0.2
|
||||
with:
|
||||
cache-read-only: false
|
||||
|
||||
|
|
@ -56,7 +58,7 @@ jobs:
|
|||
|
||||
- name: ✅ Upload kover report
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: kover-results
|
||||
path: |
|
||||
|
|
@ -74,21 +76,23 @@ jobs:
|
|||
name: Dependency analysis
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Use JDK 21
|
||||
uses: actions/setup-java@v5
|
||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||
with:
|
||||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '21'
|
||||
- name: Configure gradle
|
||||
uses: gradle/actions/setup-gradle@v5
|
||||
uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5.0.2
|
||||
with:
|
||||
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
|
||||
- name: Dependency analysis
|
||||
run: ./gradlew dependencyCheckAnalyze $CI_GRADLE_ARG_PROPERTIES
|
||||
- name: Upload dependency analysis
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: dependency-analysis
|
||||
path: build/reports/dependency-check-report.html
|
||||
|
|
|
|||
4
.github/workflows/post-release.yml
vendored
4
.github/workflows/post-release.yml
vendored
|
|
@ -5,6 +5,8 @@ on:
|
|||
tags:
|
||||
- 'v*'
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
post-release:
|
||||
runs-on: ubuntu-latest
|
||||
|
|
@ -13,7 +15,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Trigger pipeline
|
||||
uses: actions/github-script@v8
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
||||
with:
|
||||
github-token: ${{ secrets.ENTERPRISE_ACTIONS_TOKEN }}
|
||||
script: |
|
||||
|
|
|
|||
10
.github/workflows/pull_request.yml
vendored
10
.github/workflows/pull_request.yml
vendored
|
|
@ -2,11 +2,13 @@ name: Pull Request
|
|||
on:
|
||||
pull_request_target:
|
||||
types: [ opened, edited, labeled, unlabeled, synchronize ]
|
||||
workflow_call:
|
||||
workflow_call: # zizmor: ignore[dangerous-triggers]
|
||||
secrets:
|
||||
ELEMENT_BOT_TOKEN:
|
||||
required: true
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
prevent-blocked:
|
||||
name: Prevent blocked
|
||||
|
|
@ -15,7 +17,7 @@ jobs:
|
|||
pull-requests: read
|
||||
steps:
|
||||
- name: Add notice
|
||||
uses: actions/github-script@v8
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
||||
if: contains(github.event.pull_request.labels.*.name, 'X-Blocked')
|
||||
with:
|
||||
script: |
|
||||
|
|
@ -39,7 +41,7 @@ jobs:
|
|||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN_READ_ORG }}
|
||||
- name: Add label
|
||||
if: steps.teams.outputs.isTeamMember == 'false'
|
||||
uses: actions/github-script@v8
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
||||
with:
|
||||
script: |
|
||||
github.rest.issues.addLabels({
|
||||
|
|
@ -58,7 +60,7 @@ jobs:
|
|||
github.event.pull_request.head.repo.full_name != github.repository
|
||||
steps:
|
||||
- name: Close pull request
|
||||
uses: actions/github-script@v8
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
|
||||
with:
|
||||
script: |
|
||||
github.rest.issues.createComment({
|
||||
|
|
|
|||
110
.github/workflows/quality.yml
vendored
110
.github/workflows/quality.yml
vendored
|
|
@ -7,6 +7,8 @@ on:
|
|||
push:
|
||||
branches: [ main, develop ]
|
||||
|
||||
permissions: {}
|
||||
|
||||
# Enrich gradle.properties for CI/CD
|
||||
env:
|
||||
GRADLE_OPTS: -Dorg.gradle.jvmargs=-Xmx8g -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError -XX:+UseG1GC -Dkotlin.daemon.jvm.options=-Xmx4g
|
||||
|
|
@ -31,7 +33,9 @@ jobs:
|
|||
docker-images: true
|
||||
swap-storage: false
|
||||
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Add SSH private keys for submodule repositories
|
||||
uses: webfactory/ssh-agent@a6f90b1f127823b31d4d4a8d96047790581349bd # v0.9.1
|
||||
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||
|
|
@ -47,9 +51,11 @@ jobs:
|
|||
name: Search for invalid screenshot files
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up Python 3.12
|
||||
uses: actions/setup-python@v6
|
||||
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
with:
|
||||
python-version: 3.14
|
||||
- name: Search for invalid screenshot files
|
||||
|
|
@ -59,18 +65,20 @@ jobs:
|
|||
name: Search for invalid dependencies
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Use JDK 21
|
||||
uses: actions/setup-java@v5
|
||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||
with:
|
||||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '21'
|
||||
- name: Configure gradle
|
||||
uses: gradle/actions/setup-gradle@v5
|
||||
uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5.0.2
|
||||
with:
|
||||
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
|
||||
- name: Set up Python 3.12
|
||||
uses: actions/setup-python@v6
|
||||
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
with:
|
||||
python-version: 3.14
|
||||
- name: Search for invalid dependencies
|
||||
|
|
@ -85,11 +93,12 @@ jobs:
|
|||
group: ${{ github.ref == 'refs/heads/main' && format('check-konsist-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('check-konsist-develop-{0}', github.sha) || format('check-konsist-{0}', github.ref) }}
|
||||
cancel-in-progress: true
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
# Ensure we are building the branch and not the branch after being merged on develop
|
||||
# https://github.com/actions/checkout/issues/881
|
||||
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }}
|
||||
persist-credentials: false
|
||||
- name: Add SSH private keys for submodule repositories
|
||||
uses: webfactory/ssh-agent@a6f90b1f127823b31d4d4a8d96047790581349bd # v0.9.1
|
||||
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||
|
|
@ -99,19 +108,19 @@ jobs:
|
|||
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||
run: git submodule update --init --recursive
|
||||
- name: Use JDK 21
|
||||
uses: actions/setup-java@v5
|
||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||
with:
|
||||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '21'
|
||||
- name: Configure gradle
|
||||
uses: gradle/actions/setup-gradle@v5
|
||||
uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5.0.2
|
||||
with:
|
||||
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
|
||||
- name: Run Konsist tests
|
||||
run: ./gradlew :tests:konsist:testDebugUnitTest $CI_GRADLE_ARG_PROPERTIES --no-daemon
|
||||
- name: Upload reports
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: konsist-report
|
||||
path: |
|
||||
|
|
@ -125,11 +134,12 @@ jobs:
|
|||
group: ${{ github.ref == 'refs/heads/main' && format('check-compose-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('check-compose-develop-{0}', github.sha) || format('check-compose-{0}', github.ref) }}
|
||||
cancel-in-progress: true
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
# Ensure we are building the branch and not the branch after being merged on develop
|
||||
# https://github.com/actions/checkout/issues/881
|
||||
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }}
|
||||
persist-credentials: false
|
||||
- name: Add SSH private keys for submodule repositories
|
||||
uses: webfactory/ssh-agent@a6f90b1f127823b31d4d4a8d96047790581349bd # v0.9.1
|
||||
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||
|
|
@ -139,12 +149,12 @@ jobs:
|
|||
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||
run: git submodule update --init --recursive
|
||||
- name: Use JDK 21
|
||||
uses: actions/setup-java@v5
|
||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||
with:
|
||||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '21'
|
||||
- name: Configure gradle
|
||||
uses: gradle/actions/setup-gradle@v5
|
||||
uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5.0.2
|
||||
with:
|
||||
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
|
||||
- name: Run compose tests
|
||||
|
|
@ -158,11 +168,12 @@ jobs:
|
|||
group: ${{ github.ref == 'refs/heads/main' && format('check-lint-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('check-lint-develop-{0}', github.sha) || format('check-lint-{0}', github.ref) }}
|
||||
cancel-in-progress: true
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
# Ensure we are building the branch and not the branch after being merged on develop
|
||||
# https://github.com/actions/checkout/issues/881
|
||||
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }}
|
||||
persist-credentials: false
|
||||
- name: Add SSH private keys for submodule repositories
|
||||
uses: webfactory/ssh-agent@a6f90b1f127823b31d4d4a8d96047790581349bd # v0.9.1
|
||||
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||
|
|
@ -172,12 +183,12 @@ jobs:
|
|||
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||
run: git submodule update --init --recursive
|
||||
- name: Use JDK 21
|
||||
uses: actions/setup-java@v5
|
||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||
with:
|
||||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '21'
|
||||
- name: Configure gradle
|
||||
uses: gradle/actions/setup-gradle@v5
|
||||
uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5.0.2
|
||||
with:
|
||||
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
|
||||
- name: Build Gplay Debug
|
||||
|
|
@ -188,7 +199,7 @@ jobs:
|
|||
run: ./gradlew :app:lintGplayDebug :app:lintFdroidDebug lintDebug $CI_GRADLE_ARG_PROPERTIES --continue
|
||||
- name: Upload reports
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: linting-report
|
||||
path: |
|
||||
|
|
@ -202,11 +213,12 @@ jobs:
|
|||
group: ${{ github.ref == 'refs/heads/main' && format('check-detekt-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('check-detekt-develop-{0}', github.sha) || format('check-detekt-{0}', github.ref) }}
|
||||
cancel-in-progress: true
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
# Ensure we are building the branch and not the branch after being merged on develop
|
||||
# https://github.com/actions/checkout/issues/881
|
||||
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }}
|
||||
persist-credentials: false
|
||||
- name: Add SSH private keys for submodule repositories
|
||||
uses: webfactory/ssh-agent@a6f90b1f127823b31d4d4a8d96047790581349bd # v0.9.1
|
||||
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||
|
|
@ -216,19 +228,19 @@ jobs:
|
|||
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||
run: git submodule update --init --recursive
|
||||
- name: Use JDK 21
|
||||
uses: actions/setup-java@v5
|
||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||
with:
|
||||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '21'
|
||||
- name: Configure gradle
|
||||
uses: gradle/actions/setup-gradle@v5
|
||||
uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5.0.2
|
||||
with:
|
||||
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
|
||||
- name: Run Detekt
|
||||
run: ./gradlew detekt $CI_GRADLE_ARG_PROPERTIES --no-daemon
|
||||
- name: Upload reports
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: detekt-report
|
||||
path: |
|
||||
|
|
@ -242,11 +254,12 @@ jobs:
|
|||
group: ${{ github.ref == 'refs/heads/main' && format('check-ktlint-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('check-ktlint-develop-{0}', github.sha) || format('check-ktlint-{0}', github.ref) }}
|
||||
cancel-in-progress: true
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
# Ensure we are building the branch and not the branch after being merged on develop
|
||||
# https://github.com/actions/checkout/issues/881
|
||||
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }}
|
||||
persist-credentials: false
|
||||
- name: Add SSH private keys for submodule repositories
|
||||
uses: webfactory/ssh-agent@a6f90b1f127823b31d4d4a8d96047790581349bd # v0.9.1
|
||||
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||
|
|
@ -256,37 +269,38 @@ jobs:
|
|||
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||
run: git submodule update --init --recursive
|
||||
- name: Use JDK 21
|
||||
uses: actions/setup-java@v5
|
||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||
with:
|
||||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '21'
|
||||
- name: Configure gradle
|
||||
uses: gradle/actions/setup-gradle@v5
|
||||
uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5.0.2
|
||||
with:
|
||||
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
|
||||
- name: Run Ktlint check
|
||||
run: ./gradlew ktlintCheck $CI_GRADLE_ARG_PROPERTIES
|
||||
- name: Upload reports
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: ktlint-report
|
||||
path: |
|
||||
**/build/reports/**/*.*
|
||||
|
||||
knit:
|
||||
name: Knit checks
|
||||
docs:
|
||||
name: Doc checks
|
||||
runs-on: ubuntu-latest
|
||||
# Allow all jobs on main and develop. Just one per PR.
|
||||
concurrency:
|
||||
group: ${{ github.ref == 'refs/heads/main' && format('check-knit-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('check-knit-develop-{0}', github.sha) || format('check-knit-{0}', github.ref) }}
|
||||
group: ${{ github.ref == 'refs/heads/main' && format('check-docs-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('check-docs-develop-{0}', github.sha) || format('check-docs-{0}', github.ref) }}
|
||||
cancel-in-progress: true
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
# Ensure we are building the branch and not the branch after being merged on develop
|
||||
# https://github.com/actions/checkout/issues/881
|
||||
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }}
|
||||
persist-credentials: false
|
||||
- name: Add SSH private keys for submodule repositories
|
||||
uses: webfactory/ssh-agent@a6f90b1f127823b31d4d4a8d96047790581349bd # v0.9.1
|
||||
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||
|
|
@ -295,17 +309,9 @@ jobs:
|
|||
- name: Clone submodules
|
||||
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||
run: git submodule update --init --recursive
|
||||
- name: Use JDK 21
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '21'
|
||||
- name: Configure gradle
|
||||
uses: gradle/actions/setup-gradle@v5
|
||||
with:
|
||||
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
|
||||
- name: Run Knit
|
||||
run: ./gradlew knitCheck $CI_GRADLE_ARG_PROPERTIES
|
||||
- name: Run docs check
|
||||
# This is equivalent to `./gradlew checkDocs`, but we avoid having to install java and gradle
|
||||
run: python3 ./tools/docs/generate_toc.py --verify ./*.md docs/**/*.md
|
||||
|
||||
# Note: to auto fix issues you can use the following command:
|
||||
# shellcheck -f diff <files> | git apply
|
||||
|
|
@ -313,25 +319,39 @@ jobs:
|
|||
name: Check shell scripts
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Run shellcheck
|
||||
uses: ludeeus/action-shellcheck@2.0.0
|
||||
uses: ludeeus/action-shellcheck@00cae500b08a931fb5698e11e79bfbd38e612a38 # v2.0.0
|
||||
with:
|
||||
severity: warning
|
||||
|
||||
zizmor:
|
||||
name: Run zizmor
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
security-events: write # Required for upload-sarif (used by zizmor-action) to upload SARIF files.
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: zizmorcore/zizmor-action@71321a20a9ded102f6e9ce5718a2fcec2c4f70d8 # v0.5.2
|
||||
|
||||
upload_reports:
|
||||
name: Project Check Suite
|
||||
runs-on: ubuntu-latest
|
||||
needs: [konsist, lint, ktlint, detekt]
|
||||
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
# Ensure we are building the branch and not the branch after being merged on develop
|
||||
# https://github.com/actions/checkout/issues/881
|
||||
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }}
|
||||
persist-credentials: false
|
||||
- name: Download reports from previous jobs
|
||||
uses: actions/download-artifact@v8
|
||||
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
|
||||
- name: Prepare Danger
|
||||
if: always()
|
||||
run: |
|
||||
|
|
|
|||
6
.github/workflows/recordScreenshots.yml
vendored
6
.github/workflows/recordScreenshots.yml
vendored
|
|
@ -5,6 +5,8 @@ on:
|
|||
pull_request:
|
||||
types: [ labeled ]
|
||||
|
||||
permissions: {}
|
||||
|
||||
# Enrich gradle.properties for CI/CD
|
||||
env:
|
||||
GRADLE_OPTS: -Dorg.gradle.jvmargs=-Xmx8g -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError -XX:+UseG1GC -Dkotlin.daemon.jvm.options=-Xmx4g -Dsonar.gradle.skipCompile=true
|
||||
|
|
@ -48,13 +50,13 @@ jobs:
|
|||
with:
|
||||
persist-credentials: false
|
||||
- name: ☕️ Use JDK 21
|
||||
uses: actions/setup-java@v5
|
||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||
with:
|
||||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '21'
|
||||
# Add gradle cache, this should speed up the process
|
||||
- name: Configure gradle
|
||||
uses: gradle/actions/setup-gradle@v5
|
||||
uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5.0.2
|
||||
with:
|
||||
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
|
||||
- name: Record screenshots
|
||||
|
|
|
|||
32
.github/workflows/release.yml
vendored
32
.github/workflows/release.yml
vendored
|
|
@ -5,6 +5,8 @@ on:
|
|||
push:
|
||||
branches: [ main ]
|
||||
|
||||
permissions: {}
|
||||
|
||||
# Enrich gradle.properties for CI/CD
|
||||
env:
|
||||
GRADLE_OPTS: -Dorg.gradle.jvmargs=-Xmx8g -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError -XX:+UseG1GC -Dkotlin.daemon.jvm.options=-Xmx4g
|
||||
|
|
@ -32,14 +34,16 @@ jobs:
|
|||
docker-images: true
|
||||
swap-storage: false
|
||||
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Use JDK 21
|
||||
uses: actions/setup-java@v5
|
||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||
with:
|
||||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '21'
|
||||
- name: Configure gradle
|
||||
uses: gradle/actions/setup-gradle@v5
|
||||
uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5.0.2
|
||||
- name: Create app bundle
|
||||
env:
|
||||
ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }}
|
||||
|
|
@ -53,7 +57,7 @@ jobs:
|
|||
ELEMENT_CALL_RAGESHAKE_URL: ${{ secrets.ELEMENT_CALL_RAGESHAKE_URL }}
|
||||
run: ./gradlew bundleGplayRelease $CI_GRADLE_ARG_PROPERTIES
|
||||
- name: Upload bundle as artifact
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: elementx-app-gplay-bundle-unsigned
|
||||
path: |
|
||||
|
|
@ -67,7 +71,9 @@ jobs:
|
|||
group: ${{ format('build-release-main-enterprise-{0}', github.sha) }}
|
||||
cancel-in-progress: true
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Add SSH private keys for submodule repositories
|
||||
uses: webfactory/ssh-agent@a6f90b1f127823b31d4d4a8d96047790581349bd # v0.9.1
|
||||
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||
|
|
@ -76,12 +82,12 @@ jobs:
|
|||
- name: Clone submodules
|
||||
run: git submodule update --init --recursive
|
||||
- name: Use JDK 21
|
||||
uses: actions/setup-java@v5
|
||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||
with:
|
||||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '21'
|
||||
- name: Configure gradle
|
||||
uses: gradle/actions/setup-gradle@v5
|
||||
uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5.0.2
|
||||
- name: Create Enterprise app bundle
|
||||
env:
|
||||
ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }}
|
||||
|
|
@ -89,7 +95,7 @@ jobs:
|
|||
ELEMENT_ANDROID_MAPTILER_DARK_MAP_ID: ${{ secrets.MAPTILER_DARK_MAP_ID }}
|
||||
run: ./gradlew bundleGplayRelease $CI_GRADLE_ARG_PROPERTIES
|
||||
- name: Upload bundle as artifact
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: elementx-enterprise-app-gplay-bundle-unsigned
|
||||
path: |
|
||||
|
|
@ -116,14 +122,16 @@ jobs:
|
|||
docker-images: true
|
||||
swap-storage: false
|
||||
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Use JDK 21
|
||||
uses: actions/setup-java@v5
|
||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||
with:
|
||||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '21'
|
||||
- name: Configure gradle
|
||||
uses: gradle/actions/setup-gradle@v5
|
||||
uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5.0.2
|
||||
- name: Create APKs
|
||||
env:
|
||||
ELEMENT_ANDROID_MAPTILER_API_KEY: ${{ secrets.MAPTILER_KEY }}
|
||||
|
|
@ -131,7 +139,7 @@ jobs:
|
|||
ELEMENT_ANDROID_MAPTILER_DARK_MAP_ID: ${{ secrets.MAPTILER_DARK_MAP_ID }}
|
||||
run: ./gradlew assembleFdroidRelease $CI_GRADLE_ARG_PROPERTIES
|
||||
- name: Upload apks as artifact
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: elementx-app-fdroid-apks-unsigned
|
||||
path: |
|
||||
|
|
|
|||
9
.github/workflows/sonar.yml
vendored
9
.github/workflows/sonar.yml
vendored
|
|
@ -7,6 +7,8 @@ on:
|
|||
push:
|
||||
branches: [ main, develop ]
|
||||
|
||||
permissions: {}
|
||||
|
||||
# Enrich gradle.properties for CI/CD
|
||||
env:
|
||||
GRADLE_OPTS: -Dorg.gradle.jvmargs=-Xmx8g -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError -XX:+UseG1GC -Dkotlin.daemon.jvm.options=-Xmx4g
|
||||
|
|
@ -36,18 +38,19 @@ jobs:
|
|||
docker-images: true
|
||||
swap-storage: false
|
||||
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
# Ensure we are building the branch and not the branch after being merged on develop
|
||||
# https://github.com/actions/checkout/issues/881
|
||||
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }}
|
||||
persist-credentials: false
|
||||
- name: Use JDK 21
|
||||
uses: actions/setup-java@v5
|
||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||
with:
|
||||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '21'
|
||||
- name: Configure gradle
|
||||
uses: gradle/actions/setup-gradle@v5
|
||||
uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5.0.2
|
||||
with:
|
||||
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
|
||||
- name: Build debug code and test fixtures
|
||||
|
|
|
|||
4
.github/workflows/stale-issues.yml
vendored
4
.github/workflows/stale-issues.yml
vendored
|
|
@ -4,13 +4,15 @@ on:
|
|||
schedule:
|
||||
- cron: "30 1 * * *"
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
steps:
|
||||
- uses: actions/stale@v10
|
||||
- uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0
|
||||
with:
|
||||
only-labels: "X-Needs-Info"
|
||||
days-before-issue-stale: 30
|
||||
|
|
|
|||
12
.github/workflows/sync-localazy.yml
vendored
12
.github/workflows/sync-localazy.yml
vendored
|
|
@ -5,24 +5,28 @@ on:
|
|||
# At 00:00 on every Monday UTC
|
||||
- cron: '0 0 * * 1'
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
sync-localazy:
|
||||
runs-on: ubuntu-latest
|
||||
# Skip in forks
|
||||
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Use JDK 21
|
||||
uses: actions/setup-java@v5
|
||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||
with:
|
||||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '21'
|
||||
- name: Configure gradle
|
||||
uses: gradle/actions/setup-gradle@v5
|
||||
uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5.0.2
|
||||
with:
|
||||
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
|
||||
- name: Set up Python 3.12
|
||||
uses: actions/setup-python@v6
|
||||
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
with:
|
||||
python-version: 3.14
|
||||
- name: Setup Localazy
|
||||
|
|
|
|||
8
.github/workflows/sync-sas-strings.yml
vendored
8
.github/workflows/sync-sas-strings.yml
vendored
|
|
@ -5,6 +5,8 @@ on:
|
|||
# At 00:00 on every Monday UTC
|
||||
- cron: '0 0 * * 1'
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
sync-sas-strings:
|
||||
runs-on: ubuntu-latest
|
||||
|
|
@ -12,9 +14,11 @@ jobs:
|
|||
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||
# No concurrency required, runs every time on a schedule.
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up Python 3.12
|
||||
uses: actions/setup-python@v6
|
||||
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
with:
|
||||
python-version: 3.14
|
||||
- name: Install Prerequisite dependencies
|
||||
|
|
|
|||
10
.github/workflows/tests.yml
vendored
10
.github/workflows/tests.yml
vendored
|
|
@ -7,6 +7,8 @@ on:
|
|||
push:
|
||||
branches: [ main, develop ]
|
||||
|
||||
permissions: {}
|
||||
|
||||
# Enrich gradle.properties for CI/CD
|
||||
env:
|
||||
GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx7g -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError" -Dkotlin.daemon.jvm.options=-Xmx2g -XX:+UseG1GC
|
||||
|
|
@ -61,12 +63,12 @@ jobs:
|
|||
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||
run: git submodule update --init --recursive
|
||||
- name: ☕️ Use JDK 21
|
||||
uses: actions/setup-java@v5
|
||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||
with:
|
||||
distribution: 'temurin' # See 'Supported distributions' for available options
|
||||
java-version: '21'
|
||||
- name: Configure gradle
|
||||
uses: gradle/actions/setup-gradle@v5
|
||||
uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5.0.2
|
||||
with:
|
||||
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
|
||||
|
||||
|
|
@ -75,7 +77,7 @@ jobs:
|
|||
|
||||
- name: 🚫 Upload kover failed coverage reports
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: kover-error-report
|
||||
path: |
|
||||
|
|
@ -87,7 +89,7 @@ jobs:
|
|||
|
||||
- name: 🚫 Upload test results on error
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: tests-and-screenshot-tests-results
|
||||
path: |
|
||||
|
|
|
|||
4
.github/workflows/triage-incoming.yml
vendored
4
.github/workflows/triage-incoming.yml
vendored
|
|
@ -4,11 +4,13 @@ on:
|
|||
issues:
|
||||
types: [ opened ]
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
triage-new-issues:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/add-to-project@v1.0.2
|
||||
- uses: actions/add-to-project@244f685bbc3b7adfa8466e08b698b5577571133e # v1.0.2
|
||||
with:
|
||||
project-url: https://github.com/orgs/element-hq/projects/91
|
||||
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
|
|
|||
18
.github/workflows/triage-labelled.yml
vendored
18
.github/workflows/triage-labelled.yml
vendored
|
|
@ -4,6 +4,8 @@ on:
|
|||
issues:
|
||||
types: [labeled]
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
move_element_x_issues:
|
||||
name: ElementX issues to ElementX project board
|
||||
|
|
@ -12,7 +14,7 @@ jobs:
|
|||
if: >
|
||||
github.repository == 'element-hq/element-x-android'
|
||||
steps:
|
||||
- uses: actions/add-to-project@v1.0.2
|
||||
- uses: actions/add-to-project@244f685bbc3b7adfa8466e08b698b5577571133e # v1.0.2
|
||||
with:
|
||||
project-url: https://github.com/orgs/element-hq/projects/43
|
||||
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
|
@ -21,14 +23,16 @@ jobs:
|
|||
name: Move triaged needs info issues on board
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/add-to-project@v1.0.2
|
||||
- uses: actions/add-to-project@244f685bbc3b7adfa8466e08b698b5577571133e # v1.0.2
|
||||
id: addItem
|
||||
with:
|
||||
project-url: https://github.com/orgs/element-hq/projects/91
|
||||
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
labeled: X-Needs-Info
|
||||
- name: Print itemId
|
||||
run: echo ${{ steps.addItem.outputs.itemId }}
|
||||
run: echo ${STEPS_ADDITEM_OUTPUTS_ITEMID}
|
||||
env:
|
||||
STEPS_ADDITEM_OUTPUTS_ITEMID: ${{ steps.addItem.outputs.itemId }}
|
||||
- uses: kalgurn/update-project-item-status@31e54df46a2cdaef4f85c31ac839fbcd2fd7c3a2 # 0.0.3
|
||||
if: ${{ steps.addItem.outputs.itemId }}
|
||||
with:
|
||||
|
|
@ -43,7 +47,7 @@ jobs:
|
|||
if: >
|
||||
contains(github.event.issue.labels.*.name, 'Team: Element X Feature')
|
||||
steps:
|
||||
- uses: actions/add-to-project@v1.0.2
|
||||
- uses: actions/add-to-project@244f685bbc3b7adfa8466e08b698b5577571133e # v1.0.2
|
||||
with:
|
||||
project-url: https://github.com/orgs/element-hq/projects/73
|
||||
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
|
@ -54,7 +58,7 @@ jobs:
|
|||
if: >
|
||||
contains(github.event.issue.labels.*.name, 'Team: Verticals Feature')
|
||||
steps:
|
||||
- uses: actions/add-to-project@v1.0.2
|
||||
- uses: actions/add-to-project@244f685bbc3b7adfa8466e08b698b5577571133e # v1.0.2
|
||||
with:
|
||||
project-url: https://github.com/orgs/element-hq/projects/57
|
||||
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
|
@ -66,7 +70,7 @@ jobs:
|
|||
contains(github.event.issue.labels.*.name, 'Team: QA') ||
|
||||
contains(github.event.issue.labels.*.name, 'X-Needs-Signoff')
|
||||
steps:
|
||||
- uses: actions/add-to-project@v1.0.2
|
||||
- uses: actions/add-to-project@244f685bbc3b7adfa8466e08b698b5577571133e # v1.0.2
|
||||
with:
|
||||
project-url: https://github.com/orgs/element-hq/projects/69
|
||||
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
|
@ -77,7 +81,7 @@ jobs:
|
|||
if: >
|
||||
contains(github.event.issue.labels.*.name, 'X-Needs-Signoff')
|
||||
steps:
|
||||
- uses: actions/add-to-project@v1.0.2
|
||||
- uses: actions/add-to-project@244f685bbc3b7adfa8466e08b698b5577571133e # v1.0.2
|
||||
with:
|
||||
project-url: https://github.com/orgs/element-hq/projects/89
|
||||
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
|
|
|||
2
.github/workflows/validate-lfs.yml
vendored
2
.github/workflows/validate-lfs.yml
vendored
|
|
@ -2,6 +2,8 @@ name: Validate Git LFS
|
|||
|
||||
on: [pull_request, merge_group]
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
|
|
|||
53
CHANGES.md
53
CHANGES.md
|
|
@ -1,3 +1,56 @@
|
|||
Changes in Element X v26.03.2
|
||||
=============================
|
||||
|
||||
## Hotfix release
|
||||
|
||||
This release is out of our normal release cycle because we detected an important issue that could happen when instantiating the cryptographic DB and would result in the room sync not working.
|
||||
|
||||
## What's Changed
|
||||
### 🙌 Improvements
|
||||
* Floating toolbar by @bmarty in https://github.com/element-hq/element-x-android/pull/6147
|
||||
### 🐛 Bugfixes
|
||||
* Ensure that redacted event from encrypted room does not trigger a fallback notification by @bmarty in https://github.com/element-hq/element-x-android/pull/6241
|
||||
* Add `MediaSource.safeUrl` for removing invalid fragment part from URLs by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6035
|
||||
### 🗣 Translations
|
||||
* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/6269
|
||||
### 🧱 Build
|
||||
* Fix nightly CI issues by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6263
|
||||
* CI: Add failed tests to summary by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6271
|
||||
* Make 'room list catch-up' analytics transaction network aware by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6233
|
||||
* Adjust the build-rust-sdk script to allow non-interactive use by @andybalaam in https://github.com/element-hq/element-x-android/pull/6281
|
||||
### Dependency upgrades
|
||||
* Update metro to v0.11.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6245
|
||||
* Update dependency com.posthog:posthog-android to v3.34.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6251
|
||||
* Update metro to v0.11.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6255
|
||||
* Update coil to v3.4.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6243
|
||||
* Update dependency io.element.android:element-call-embedded to v0.17.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6244
|
||||
* Update dependency com.posthog:posthog-android to v3.34.2 - autoclosed by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6254
|
||||
* Update dependencyAnalysis to v3.6.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6256
|
||||
* Update GitHub Artifact Actions (major) by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6260
|
||||
* Update dependency com.google.firebase:firebase-bom to v34.10.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6262
|
||||
* Update dependency androidx.compose:compose-bom to v2026.02.01 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6267
|
||||
* Update dependency com.posthog:posthog-android to v3.34.3 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6272
|
||||
* Update metro to v0.11.2 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6270
|
||||
* Update dependencyAnalysis to v3.6.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6259
|
||||
* Sync compound tokens https://github.com/element-hq/compound-design-tokens/releases/tag/v6.10.1 by @bmarty in https://github.com/element-hq/element-x-android/pull/6273
|
||||
* Update dependency io.sentry:sentry-android to v8.34.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6280
|
||||
* Update dependency org.matrix.rustcomponents:sdk-android to v26.03.4 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6282
|
||||
* Update dependency org.matrix.rustcomponents:sdk-android to v26.03.05 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6287
|
||||
* Update plugin ktlint to v14.1.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6288
|
||||
* Update dependency org.unifiedpush.android:connector to v3.3.2 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6285
|
||||
### Others
|
||||
* Add some DB optimizations by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6249
|
||||
* Check if network access if blocked when fetching notifications by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6247
|
||||
* Bottom bar iteration by @bmarty in https://github.com/element-hq/element-x-android/pull/6264
|
||||
* Use `ShareIntentHandler` early to avoid distributing the whole intent by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6274
|
||||
* Simplify push notification flow by using locally stored values for pending pushes by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6258
|
||||
* Fix typed text becoming invisible when composing long messages by @timurgilfanov in https://github.com/element-hq/element-x-android/pull/6284
|
||||
|
||||
## New Contributors
|
||||
* @timurgilfanov made their first contribution in https://github.com/element-hq/element-x-android/pull/6284
|
||||
|
||||
**Full Changelog**: https://github.com/element-hq/element-x-android/compare/v26.03.0...v26.03.2
|
||||
|
||||
Changes in Element X v26.03.0
|
||||
=============================
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
* [Code quality](#code-quality)
|
||||
* [detekt](#detekt)
|
||||
* [ktlint](#ktlint)
|
||||
* [knit](#knit)
|
||||
* [checkDocs](#checkdocs)
|
||||
* [lint](#lint)
|
||||
* [Unit tests](#unit-tests)
|
||||
* [konsist](#konsist)
|
||||
|
|
@ -123,13 +123,13 @@ Note that you can run
|
|||
|
||||
For ktlint to fix some detected errors for you (you still have to check and commit the fix of course)
|
||||
|
||||
#### knit
|
||||
#### checkDocs
|
||||
|
||||
[knit](https://github.com/Kotlin/kotlinx-knit) is a tool which checks markdown files on the project. Also it generates/updates the table of content (toc) of the markdown files.
|
||||
`checkDocs` is a Gradle task which checks markdown files on the project to ensure their table of contents is up to date. It uses `tools/docs/generate_toc.py --verify` under the hood, and has a counterpart `generateDocsToc` task which runs `tools/docs/generate_toc.py` to update the table of contents of markdown files.
|
||||
|
||||
So everytime the toc should be updated, just run
|
||||
<pre>
|
||||
./gradlew knit
|
||||
./gradlew generateDocsToc
|
||||
</pre>
|
||||
|
||||
and commit the changes.
|
||||
|
|
@ -137,7 +137,7 @@ and commit the changes.
|
|||
The CI will check that markdown files are up to date by running
|
||||
|
||||
<pre>
|
||||
./gradlew knitCheck
|
||||
./gradlew checkDocs
|
||||
</pre>
|
||||
|
||||
#### lint
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ plugins {
|
|||
alias(libs.plugins.kotlin.android)
|
||||
// When using precompiled plugins, we need to apply the firebase plugin like this
|
||||
id(libs.plugins.firebaseAppDistribution.get().pluginId)
|
||||
alias(libs.plugins.knit)
|
||||
id("kotlin-parcelize")
|
||||
alias(libs.plugins.licensee)
|
||||
alias(libs.plugins.kotlin.serialization)
|
||||
|
|
@ -250,26 +249,6 @@ androidComponents {
|
|||
configureLicensesTasks(reportingExtension)
|
||||
}
|
||||
|
||||
// Knit
|
||||
apply {
|
||||
plugin("kotlinx-knit")
|
||||
}
|
||||
|
||||
knit {
|
||||
files = fileTree(project.rootDir) {
|
||||
include(
|
||||
"**/*.md",
|
||||
"**/*.kt",
|
||||
"*/*.kts",
|
||||
)
|
||||
exclude(
|
||||
"**/build/**",
|
||||
"*/.gradle/**",
|
||||
"**/CHANGES.md",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
setupDependencyInjection()
|
||||
|
||||
dependencies {
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
|||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import com.bumble.appyx.core.integration.NodeHost
|
||||
import com.bumble.appyx.core.integrationpoint.NodeActivity
|
||||
import com.bumble.appyx.core.plugin.NodeReadyObserver
|
||||
import io.element.android.compound.colors.SemanticColorsLightDark
|
||||
|
|
@ -35,6 +34,7 @@ import io.element.android.features.lockscreen.api.LockScreenEntryPoint
|
|||
import io.element.android.features.lockscreen.api.LockScreenLockState
|
||||
import io.element.android.features.lockscreen.api.LockScreenService
|
||||
import io.element.android.features.lockscreen.api.handleSecureFlag
|
||||
import io.element.android.libraries.architecture.appyx.DebugNavStateNodeHost
|
||||
import io.element.android.libraries.architecture.bindings
|
||||
import io.element.android.libraries.core.log.logger.LoggerTag
|
||||
import io.element.android.libraries.designsystem.theme.ElementThemeApp
|
||||
|
|
@ -100,7 +100,9 @@ class MainActivity : NodeActivity() {
|
|||
|
||||
@Composable
|
||||
private fun MainNodeHost() {
|
||||
NodeHost(integrationPoint = appyxV1IntegrationPoint) {
|
||||
// TODO this is a temporary helper to capture the nav state in a more readable format for crash reports
|
||||
// Revert to `NodeHost` once this is fixed
|
||||
DebugNavStateNodeHost(integrationPoint = appyxV1IntegrationPoint) {
|
||||
MainNode(
|
||||
it,
|
||||
plugins = listOf(
|
||||
|
|
@ -110,7 +112,7 @@ class MainActivity : NodeActivity() {
|
|||
mainNode = node
|
||||
mainNode.handleIntent(intent)
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
context = applicationContext
|
||||
)
|
||||
|
|
|
|||
|
|
@ -175,12 +175,23 @@ tasks.register("runQualityChecks") {
|
|||
tasks.findByName("ktlintCheck")?.let { dependsOn(it) }
|
||||
// tasks.findByName("buildHealth")?.let { dependsOn(it) }
|
||||
}
|
||||
dependsOn(":app:knitCheck")
|
||||
|
||||
dependsOn("checkDocs")
|
||||
// Make sure all checks run even if some fail
|
||||
gradle.startParameter.isContinueOnFailure = true
|
||||
}
|
||||
|
||||
// Register Markdown documentation check task.
|
||||
tasks.register("checkDocs", Exec::class.java) {
|
||||
inputs.files("./*.md", "docs/**/*.md")
|
||||
commandLine("python3", "tools/docs/generate_toc.py", "--verify", *inputs.files.map { it.path }.toTypedArray())
|
||||
}
|
||||
|
||||
// Register Markdown documentation TOC generation task.
|
||||
tasks.register("generateDocsToc", Exec::class.java) {
|
||||
inputs.files("./*.md", "docs/**/*.md")
|
||||
commandLine("python3", "tools/docs/generate_toc.py", *inputs.files.map { it.path }.toTypedArray())
|
||||
}
|
||||
|
||||
// Make sure to delete old screenshots before recording new ones
|
||||
subprojects {
|
||||
val snapshotsDir = File("${project.projectDir}/src/test/snapshots")
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ This document explains how to install Element X Android from a Github Release.
|
|||
* [I already have the application on my phone](#i-already-have-the-application-on-my-phone)
|
||||
* [Installing from the App Bundle](#installing-from-the-app-bundle)
|
||||
* [Requirements](#requirements)
|
||||
* [Steps](#steps)
|
||||
* [I already have the application on my phone](#i-already-have-the-application-on-my-phone)
|
||||
* [Steps](#steps-1)
|
||||
* [I already have the application on my phone](#i-already-have-the-application-on-my-phone-1)
|
||||
|
||||
<!--- END -->
|
||||
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@
|
|||
|
||||
<!--- TOC -->
|
||||
|
||||
* [Installing from GitHub](#installing-from-github)
|
||||
* [Create a GitHub token](#create-a-github-token)
|
||||
* [Provide artifact URL](#provide-artifact-url)
|
||||
* [Next steps](#next-steps)
|
||||
* [Future improvement](#future-improvement)
|
||||
* [Installing from GitHub](#installing-from-github)
|
||||
* [Create a GitHub token](#create-a-github-token)
|
||||
* [Provide artifact URL](#provide-artifact-url)
|
||||
* [Next steps](#next-steps)
|
||||
* [Future improvement](#future-improvement)
|
||||
|
||||
<!--- END -->
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
* [Stop Synapse](#stop-synapse)
|
||||
* [Troubleshoot](#troubleshoot)
|
||||
* [Android Emulator does cannot reach the homeserver](#android-emulator-does-cannot-reach-the-homeserver)
|
||||
* [Tests partially run but some fail with "Unable to contact localhost:8080"](#tests-partially-run-but-some-fail-with-"unable-to-contact-localhost8080")
|
||||
* [Tests partially run but some fail with "Unable to contact localhost:8080"](#tests-partially-run-but-some-fail-with-unable-to-contact-localhost8080)
|
||||
* [virtualenv command fails](#virtualenv-command-fails)
|
||||
|
||||
<!--- END -->
|
||||
|
|
|
|||
|
|
@ -5,11 +5,11 @@ This document aims to describe how Element android displays notifications to the
|
|||
<!--- TOC -->
|
||||
|
||||
* [Prerequisites Knowledge](#prerequisites-knowledge)
|
||||
* [How does a matrix client get a message from a homeserver?](#how-does-a-matrix-client-get-a-message-from-a-homeserver?)
|
||||
* [How does a matrix client get a message from a homeserver?](#how-does-a-matrix-client-get-a-message-from-a-homeserver)
|
||||
* [How does a mobile app receives push notification](#how-does-a-mobile-app-receives-push-notification)
|
||||
* [Push VS Notification](#push-vs-notification)
|
||||
* [Push in the matrix federated world](#push-in-the-matrix-federated-world)
|
||||
* [How does the homeserver know when to notify a client?](#how-does-the-homeserver-know-when-to-notify-a-client?)
|
||||
* [How does the homeserver know when to notify a client?](#how-does-the-homeserver-know-when-to-notify-a-client)
|
||||
* [Push vs privacy, and mitigation](#push-vs-privacy-and-mitigation)
|
||||
* [Background processing limitations](#background-processing-limitations)
|
||||
* [Element Notification implementations](#element-notification-implementations)
|
||||
|
|
|
|||
|
|
@ -3,23 +3,23 @@
|
|||
<!--- TOC -->
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Who should read this document?](#who-should-read-this-document?)
|
||||
* [Who should read this document?](#who-should-read-this-document)
|
||||
* [Submitting PR](#submitting-pr)
|
||||
* [Who can submit pull requests?](#who-can-submit-pull-requests?)
|
||||
* [Who can submit pull requests?](#who-can-submit-pull-requests)
|
||||
* [Humans](#humans)
|
||||
* [Draft PR?](#draft-pr?)
|
||||
* [Draft PR?](#draft-pr)
|
||||
* [Base branch](#base-branch)
|
||||
* [PR Review Assignment](#pr-review-assignment)
|
||||
* [PR review time](#pr-review-time)
|
||||
* [Re-request PR review](#re-request-pr-review)
|
||||
* [When create split PR?](#when-create-split-pr?)
|
||||
* [When create split PR?](#when-create-split-pr)
|
||||
* [Avoid fixing other unrelated issue in a big PR](#avoid-fixing-other-unrelated-issue-in-a-big-pr)
|
||||
* [Bots](#bots)
|
||||
* [Renovate](#renovate)
|
||||
* [Gradle wrapper](#gradle-wrapper)
|
||||
* [Sync analytics plan](#sync-analytics-plan)
|
||||
* [Reviewing PR](#reviewing-pr)
|
||||
* [Who can review pull requests?](#who-can-review-pull-requests?)
|
||||
* [Who can review pull requests?](#who-can-review-pull-requests)
|
||||
* [What to have in mind when reviewing a PR](#what-to-have-in-mind-when-reviewing-a-pr)
|
||||
* [Rules](#rules)
|
||||
* [Check the form](#check-the-form)
|
||||
|
|
@ -29,7 +29,7 @@
|
|||
* [Check the commit](#check-the-commit)
|
||||
* [Check the substance](#check-the-substance)
|
||||
* [Make a dedicated meeting to review the PR](#make-a-dedicated-meeting-to-review-the-pr)
|
||||
* [What happen to the issue(s)?](#what-happen-to-the-issues?)
|
||||
* [What happen to the issue(s)?](#what-happen-to-the-issues)
|
||||
* [Merge conflict](#merge-conflict)
|
||||
* [When and who can merge PR](#when-and-who-can-merge-pr)
|
||||
* [Merge type](#merge-type)
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 1fd0d297d944186e3af2773e1c5db2938d60f74b
|
||||
Subproject commit cdde60c158ecd0987a3ba6fd79a4617551aff463
|
||||
2
fastlane/metadata/android/en-US/changelogs/202603030.txt
Normal file
2
fastlane/metadata/android/en-US/changelogs/202603030.txt
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
Main changes in this version: bug fixes and improvements.
|
||||
Full changelog: https://github.com/element-hq/element-x-android/releases
|
||||
|
|
@ -26,9 +26,10 @@ sealed interface CallType : NodeInputs, Parcelable {
|
|||
data class RoomCall(
|
||||
val sessionId: SessionId,
|
||||
val roomId: RoomId,
|
||||
val isAudioCall: Boolean
|
||||
) : CallType {
|
||||
override fun toString(): String {
|
||||
return "RoomCall(sessionId=$sessionId, roomId=$roomId)"
|
||||
return "RoomCall(sessionId=$sessionId, roomId=$roomId, isAudioCall=$isAudioCall)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ class DefaultElementCallEntryPoint(
|
|||
expirationTimestamp = expirationTimestamp,
|
||||
notificationChannelId = notificationChannelId,
|
||||
textContent = textContent,
|
||||
audioOnly = callType.isAudioCall
|
||||
)
|
||||
activeCallManager.registerIncomingCall(notificationData = incomingCallNotificationData)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,4 +29,5 @@ data class CallNotificationData(
|
|||
val textContent: String?,
|
||||
// Expiration timestamp in millis since epoch
|
||||
val expirationTimestamp: Long,
|
||||
val audioOnly: Boolean,
|
||||
) : Parcelable
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ class RingingCallNotificationCreator(
|
|||
timestamp: Long,
|
||||
expirationTimestamp: Long,
|
||||
textContent: String?,
|
||||
audioOnly: Boolean,
|
||||
): Notification? {
|
||||
val matrixClient = matrixClientProvider.getOrRestore(sessionId).getOrNull() ?: return null
|
||||
val imageLoader = imageLoaderHolder.get(matrixClient)
|
||||
|
|
@ -88,7 +89,7 @@ class RingingCallNotificationCreator(
|
|||
.setImportant(true)
|
||||
.build()
|
||||
|
||||
val answerIntent = IntentProvider.getPendingIntent(context, CallType.RoomCall(sessionId, roomId))
|
||||
val answerIntent = IntentProvider.getPendingIntent(context, CallType.RoomCall(sessionId, roomId, isAudioCall = audioOnly))
|
||||
val notificationData = CallNotificationData(
|
||||
sessionId = sessionId,
|
||||
roomId = roomId,
|
||||
|
|
@ -101,6 +102,7 @@ class RingingCallNotificationCreator(
|
|||
timestamp = timestamp,
|
||||
textContent = textContent,
|
||||
expirationTimestamp = expirationTimestamp,
|
||||
audioOnly = audioOnly,
|
||||
)
|
||||
|
||||
val declineIntent = PendingIntentCompat.getBroadcast(
|
||||
|
|
@ -127,7 +129,11 @@ class RingingCallNotificationCreator(
|
|||
.setSmallIcon(CommonDrawables.ic_notification)
|
||||
.setPriority(NotificationCompat.PRIORITY_MAX)
|
||||
.setCategory(NotificationCompat.CATEGORY_CALL)
|
||||
.setStyle(NotificationCompat.CallStyle.forIncomingCall(caller, declineIntent, answerIntent).setIsVideo(true))
|
||||
.setStyle(
|
||||
NotificationCompat.CallStyle
|
||||
.forIncomingCall(caller, declineIntent, answerIntent)
|
||||
.setIsVideo(!audioOnly)
|
||||
)
|
||||
.addPerson(caller)
|
||||
.setAutoCancel(true)
|
||||
.setWhen(timestamp)
|
||||
|
|
|
|||
|
|
@ -45,8 +45,8 @@ class DeclineCallBroadcastReceiver : BroadcastReceiver() {
|
|||
callType = CallType.RoomCall(
|
||||
sessionId = notificationData.sessionId,
|
||||
roomId = notificationData.roomId,
|
||||
),
|
||||
notificationData = notificationData,
|
||||
isAudioCall = notificationData.audioOnly
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright (c) 2026 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.call.impl.ui
|
||||
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import io.element.android.features.call.impl.notifications.CallNotificationData
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
|
||||
open class CallNotificationDataProvider : PreviewParameterProvider<CallNotificationData> {
|
||||
override val values: Sequence<CallNotificationData>
|
||||
get() = sequenceOf(
|
||||
aCallNotificationData(
|
||||
audioOnly = false
|
||||
),
|
||||
aCallNotificationData(
|
||||
audioOnly = true
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
internal fun aCallNotificationData(
|
||||
audioOnly: Boolean
|
||||
): CallNotificationData {
|
||||
return CallNotificationData(
|
||||
sessionId = SessionId("@alice:matrix.org"),
|
||||
roomId = RoomId("!1234:matrix.org"),
|
||||
eventId = EventId("\$asdadadsad:matrix.org"),
|
||||
senderId = UserId("@bob:matrix.org"),
|
||||
roomName = "A room",
|
||||
senderName = "Bob",
|
||||
avatarUrl = null,
|
||||
notificationChannelId = "incoming_call",
|
||||
timestamp = 0L,
|
||||
textContent = null,
|
||||
expirationTimestamp = 1000L,
|
||||
audioOnly = audioOnly
|
||||
)
|
||||
}
|
||||
|
|
@ -226,6 +226,7 @@ class CallScreenPresenter(
|
|||
sessionId = inputs.sessionId,
|
||||
roomId = inputs.roomId,
|
||||
clientId = UUID.randomUUID().toString(),
|
||||
isAudioCall = inputs.isAudioCall,
|
||||
languageTag = languageTag,
|
||||
theme = theme,
|
||||
).getOrThrow()
|
||||
|
|
|
|||
|
|
@ -112,7 +112,13 @@ class IncomingCallActivity : AppCompatActivity() {
|
|||
}
|
||||
|
||||
private fun onAnswer(notificationData: CallNotificationData) {
|
||||
elementCallEntryPoint.startCall(CallType.RoomCall(notificationData.sessionId, notificationData.roomId))
|
||||
elementCallEntryPoint.startCall(
|
||||
CallType.RoomCall(
|
||||
notificationData.sessionId,
|
||||
notificationData.roomId,
|
||||
isAudioCall = notificationData.audioOnly
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun onCancel() {
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ import androidx.compose.ui.graphics.vector.ImageVector
|
|||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
|
|
@ -45,10 +46,6 @@ import io.element.android.libraries.designsystem.preview.ElementPreview
|
|||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.theme.components.Icon
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
|
||||
/**
|
||||
|
|
@ -103,7 +100,7 @@ internal fun IncomingCallScreen(
|
|||
ActionButton(
|
||||
size = 64.dp,
|
||||
onClick = { onAnswer(notificationData) },
|
||||
icon = CompoundIcons.VoiceCallSolid(),
|
||||
icon = if (notificationData.audioOnly) CompoundIcons.VoiceCallSolid() else CompoundIcons.VideoCallSolid(),
|
||||
title = stringResource(CommonStrings.action_accept),
|
||||
backgroundColor = ElementTheme.colors.iconSuccessPrimary,
|
||||
borderColor = ElementTheme.colors.borderSuccessSubtle
|
||||
|
|
@ -163,21 +160,11 @@ private fun ActionButton(
|
|||
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun IncomingCallScreenPreview() = ElementPreview {
|
||||
internal fun IncomingCallScreenPreview(
|
||||
@PreviewParameter(CallNotificationDataProvider::class) state: CallNotificationData,
|
||||
) = ElementPreview {
|
||||
IncomingCallScreen(
|
||||
notificationData = CallNotificationData(
|
||||
sessionId = SessionId("@alice:matrix.org"),
|
||||
roomId = RoomId("!1234:matrix.org"),
|
||||
eventId = EventId("\$asdadadsad:matrix.org"),
|
||||
senderId = UserId("@bob:matrix.org"),
|
||||
roomName = "A room",
|
||||
senderName = "Bob",
|
||||
avatarUrl = null,
|
||||
notificationChannelId = "incoming_call",
|
||||
timestamp = 0L,
|
||||
textContent = null,
|
||||
expirationTimestamp = 1000L,
|
||||
),
|
||||
notificationData = state,
|
||||
onAnswer = {},
|
||||
onCancel = {},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -146,6 +146,7 @@ class DefaultActiveCallManager(
|
|||
callType = CallType.RoomCall(
|
||||
sessionId = notificationData.sessionId,
|
||||
roomId = notificationData.roomId,
|
||||
isAudioCall = notificationData.audioOnly,
|
||||
),
|
||||
callState = CallState.Ringing(notificationData),
|
||||
)
|
||||
|
|
@ -273,6 +274,7 @@ class DefaultActiveCallManager(
|
|||
timestamp = notificationData.timestamp,
|
||||
textContent = notificationData.textContent,
|
||||
expirationTimestamp = notificationData.expirationTimestamp,
|
||||
audioOnly = notificationData.audioOnly,
|
||||
) ?: return
|
||||
runCatchingExceptions {
|
||||
notificationManagerCompat.notify(
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ interface CallWidgetProvider {
|
|||
suspend fun getWidget(
|
||||
sessionId: SessionId,
|
||||
roomId: RoomId,
|
||||
isAudioCall: Boolean,
|
||||
clientId: String,
|
||||
languageTag: String?,
|
||||
theme: String?,
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ class DefaultCallWidgetProvider(
|
|||
override suspend fun getWidget(
|
||||
sessionId: SessionId,
|
||||
roomId: RoomId,
|
||||
isAudioCall: Boolean,
|
||||
clientId: String,
|
||||
languageTag: String?,
|
||||
theme: String?,
|
||||
|
|
@ -50,6 +51,7 @@ class DefaultCallWidgetProvider(
|
|||
baseUrl = baseUrl,
|
||||
encrypted = isEncrypted,
|
||||
direct = room.isDm(),
|
||||
isAudioCall = isAudioCall,
|
||||
hasActiveCall = roomInfo.hasRoomCall,
|
||||
)
|
||||
val callUrl = room.generateWidgetWebViewUrl(
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ class DefaultElementCallEntryPointTest {
|
|||
@Test
|
||||
fun `startCall - starts ElementCallActivity setup with the needed extras`() = runTest {
|
||||
val entryPoint = createEntryPoint()
|
||||
entryPoint.startCall(CallType.RoomCall(A_SESSION_ID, A_ROOM_ID))
|
||||
entryPoint.startCall(CallType.RoomCall(A_SESSION_ID, A_ROOM_ID, isAudioCall = false))
|
||||
|
||||
val expectedIntent = Intent(InstrumentationRegistry.getInstrumentation().targetContext, ElementCallActivity::class.java)
|
||||
val intent = shadowOf(RuntimeEnvironment.getApplication()).nextStartedActivity
|
||||
|
|
@ -53,7 +53,7 @@ class DefaultElementCallEntryPointTest {
|
|||
val entryPoint = createEntryPoint(activeCallManager = activeCallManager)
|
||||
|
||||
entryPoint.handleIncomingCall(
|
||||
callType = CallType.RoomCall(A_SESSION_ID, A_ROOM_ID),
|
||||
callType = CallType.RoomCall(A_SESSION_ID, A_ROOM_ID, isAudioCall = false),
|
||||
eventId = AN_EVENT_ID,
|
||||
senderId = A_USER_ID_2,
|
||||
roomName = "roomName",
|
||||
|
|
|
|||
|
|
@ -65,7 +65,33 @@ class RingingCallNotificationCreatorTest {
|
|||
getUserIconLambda.assertions().isCalledOnce()
|
||||
}
|
||||
|
||||
private suspend fun RingingCallNotificationCreator.createTestNotification() = createNotification(
|
||||
@Test
|
||||
fun `createNotification - use the correct style for video call`() = runTest {
|
||||
val notificationCreator = createRingingCallNotificationCreator(
|
||||
matrixClientProvider = FakeMatrixClientProvider(getClient = { Result.success(FakeMatrixClient()) }),
|
||||
)
|
||||
|
||||
val notification = notificationCreator.createTestNotification()
|
||||
assertThat(notification?.category).isEqualTo("call")
|
||||
|
||||
val acceptAction = notification?.actions?.get(1)
|
||||
assertThat(acceptAction?.title?.toString()).isEqualTo("Video")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `createNotification - use the correct style for audio call`() = runTest {
|
||||
val notificationCreator = createRingingCallNotificationCreator(
|
||||
matrixClientProvider = FakeMatrixClientProvider(getClient = { Result.success(FakeMatrixClient()) }),
|
||||
)
|
||||
|
||||
val notification = notificationCreator.createTestNotification(audioOnly = true)
|
||||
assertThat(notification?.category).isEqualTo("call")
|
||||
|
||||
val acceptAction = notification?.actions?.get(1)
|
||||
assertThat(acceptAction?.title?.toString()).isEqualTo("Answer")
|
||||
}
|
||||
|
||||
private suspend fun RingingCallNotificationCreator.createTestNotification(audioOnly: Boolean = false) = createNotification(
|
||||
sessionId = A_SESSION_ID,
|
||||
roomId = A_ROOM_ID,
|
||||
eventId = AN_EVENT_ID,
|
||||
|
|
@ -77,6 +103,7 @@ class RingingCallNotificationCreatorTest {
|
|||
timestamp = 0L,
|
||||
expirationTimestamp = 20L,
|
||||
textContent = "textContent",
|
||||
audioOnly = audioOnly
|
||||
)
|
||||
|
||||
private fun createRingingCallNotificationCreator(
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ class CallScreenPresenterTest {
|
|||
val analyticsLambda = lambdaRecorder<MobileScreen.ScreenName, Unit> {}
|
||||
val joinedCallLambda = lambdaRecorder<CallType, Unit> {}
|
||||
val presenter = createCallScreenPresenter(
|
||||
callType = CallType.RoomCall(A_SESSION_ID, A_ROOM_ID),
|
||||
callType = CallType.RoomCall(A_SESSION_ID, A_ROOM_ID, false),
|
||||
widgetDriver = widgetDriver,
|
||||
widgetProvider = widgetProvider,
|
||||
screenTracker = FakeScreenTracker(analyticsLambda),
|
||||
|
|
@ -123,7 +123,7 @@ class CallScreenPresenterTest {
|
|||
fun `present - set message interceptor, send and receive messages`() = runTest {
|
||||
val widgetDriver = FakeMatrixWidgetDriver()
|
||||
val presenter = createCallScreenPresenter(
|
||||
callType = CallType.RoomCall(A_SESSION_ID, A_ROOM_ID),
|
||||
callType = CallType.RoomCall(A_SESSION_ID, A_ROOM_ID, false),
|
||||
widgetDriver = widgetDriver,
|
||||
screenTracker = FakeScreenTracker {},
|
||||
)
|
||||
|
|
@ -154,7 +154,7 @@ class CallScreenPresenterTest {
|
|||
val navigator = FakeCallScreenNavigator()
|
||||
val widgetDriver = FakeMatrixWidgetDriver()
|
||||
val presenter = createCallScreenPresenter(
|
||||
callType = CallType.RoomCall(A_SESSION_ID, A_ROOM_ID),
|
||||
callType = CallType.RoomCall(A_SESSION_ID, A_ROOM_ID, false),
|
||||
widgetDriver = widgetDriver,
|
||||
navigator = navigator,
|
||||
dispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true),
|
||||
|
|
@ -188,7 +188,7 @@ class CallScreenPresenterTest {
|
|||
val navigator = FakeCallScreenNavigator()
|
||||
val widgetDriver = FakeMatrixWidgetDriver()
|
||||
val presenter = createCallScreenPresenter(
|
||||
callType = CallType.RoomCall(A_SESSION_ID, A_ROOM_ID),
|
||||
callType = CallType.RoomCall(A_SESSION_ID, A_ROOM_ID, false),
|
||||
widgetDriver = widgetDriver,
|
||||
navigator = navigator,
|
||||
dispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true),
|
||||
|
|
@ -223,7 +223,7 @@ class CallScreenPresenterTest {
|
|||
val navigator = FakeCallScreenNavigator()
|
||||
val widgetDriver = FakeMatrixWidgetDriver()
|
||||
val presenter = createCallScreenPresenter(
|
||||
callType = CallType.RoomCall(A_SESSION_ID, A_ROOM_ID),
|
||||
callType = CallType.RoomCall(A_SESSION_ID, A_ROOM_ID, false),
|
||||
widgetDriver = widgetDriver,
|
||||
navigator = navigator,
|
||||
dispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true),
|
||||
|
|
@ -260,7 +260,7 @@ class CallScreenPresenterTest {
|
|||
val navigator = FakeCallScreenNavigator()
|
||||
val widgetDriver = FakeMatrixWidgetDriver()
|
||||
val presenter = createCallScreenPresenter(
|
||||
callType = CallType.RoomCall(A_SESSION_ID, A_ROOM_ID),
|
||||
callType = CallType.RoomCall(A_SESSION_ID, A_ROOM_ID, false),
|
||||
widgetDriver = widgetDriver,
|
||||
navigator = navigator,
|
||||
dispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true),
|
||||
|
|
@ -300,7 +300,7 @@ class CallScreenPresenterTest {
|
|||
val matrixClient = FakeMatrixClient(syncService = syncService)
|
||||
val appForegroundStateService = FakeAppForegroundStateService()
|
||||
val presenter = createCallScreenPresenter(
|
||||
callType = CallType.RoomCall(A_SESSION_ID, A_ROOM_ID),
|
||||
callType = CallType.RoomCall(A_SESSION_ID, A_ROOM_ID, false),
|
||||
widgetDriver = widgetDriver,
|
||||
navigator = navigator,
|
||||
dispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true),
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ class CallTypeTest {
|
|||
CallType.RoomCall(
|
||||
sessionId = A_SESSION_ID,
|
||||
roomId = A_ROOM_ID,
|
||||
isAudioCall = false,
|
||||
).getSessionId()
|
||||
).isEqualTo(A_SESSION_ID)
|
||||
}
|
||||
|
|
@ -38,7 +39,7 @@ class CallTypeTest {
|
|||
|
||||
@Test
|
||||
fun `RoomCall stringification does not contain the URL`() {
|
||||
assertThat(CallType.RoomCall(A_SESSION_ID, A_ROOM_ID).toString())
|
||||
.isEqualTo("RoomCall(sessionId=$A_SESSION_ID, roomId=$A_ROOM_ID)")
|
||||
assertThat(CallType.RoomCall(A_SESSION_ID, A_ROOM_ID, false).toString())
|
||||
.isEqualTo("RoomCall(sessionId=$A_SESSION_ID, roomId=$A_ROOM_ID, isAudioCall=false)")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ class DefaultActiveCallManagerTest {
|
|||
callType = CallType.RoomCall(
|
||||
sessionId = callNotificationData.sessionId,
|
||||
roomId = callNotificationData.roomId,
|
||||
isAudioCall = false,
|
||||
),
|
||||
callState = CallState.Ringing(callNotificationData)
|
||||
)
|
||||
|
|
@ -91,6 +92,28 @@ class DefaultActiveCallManagerTest {
|
|||
verify { notificationManagerCompat.notify(notificationId, any()) }
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@Test
|
||||
fun `registerIncomingCall - sets the incoming audio call as active`() = runTest {
|
||||
setupShadowPowerManager()
|
||||
val notificationManagerCompat = mockk<NotificationManagerCompat>(relaxed = true)
|
||||
val manager = createActiveCallManager(notificationManagerCompat = notificationManagerCompat)
|
||||
|
||||
val callNotificationData = aCallNotificationData(audioOnly = true)
|
||||
manager.registerIncomingCall(callNotificationData)
|
||||
|
||||
assertThat(manager.activeCall.value).isEqualTo(
|
||||
ActiveCall(
|
||||
callType = CallType.RoomCall(
|
||||
sessionId = callNotificationData.sessionId,
|
||||
roomId = callNotificationData.roomId,
|
||||
isAudioCall = true,
|
||||
),
|
||||
callState = CallState.Ringing(callNotificationData)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@Test
|
||||
fun `registerIncomingCall - when there is an already active call adds missed call notification`() = runTest {
|
||||
|
|
@ -165,7 +188,7 @@ class DefaultActiveCallManagerTest {
|
|||
assertThat(manager.activeCall.value).isNotNull()
|
||||
assertThat(manager.activeWakeLock?.isHeld).isTrue()
|
||||
|
||||
manager.hangUpCall(CallType.RoomCall(notificationData.sessionId, notificationData.roomId))
|
||||
manager.hangUpCall(CallType.RoomCall(notificationData.sessionId, notificationData.roomId, false))
|
||||
assertThat(manager.activeCall.value).isNull()
|
||||
assertThat(manager.activeWakeLock?.isHeld).isFalse()
|
||||
|
||||
|
|
@ -192,7 +215,7 @@ class DefaultActiveCallManagerTest {
|
|||
val notificationData = aCallNotificationData(roomId = A_ROOM_ID)
|
||||
manager.registerIncomingCall(notificationData)
|
||||
|
||||
manager.hangUpCall(CallType.RoomCall(notificationData.sessionId, notificationData.roomId))
|
||||
manager.hangUpCall(CallType.RoomCall(notificationData.sessionId, notificationData.roomId, false))
|
||||
|
||||
coVerify {
|
||||
room.declineCall(notificationEventId = notificationData.eventId)
|
||||
|
|
@ -219,7 +242,7 @@ class DefaultActiveCallManagerTest {
|
|||
val notificationData = aCallNotificationData(roomId = A_ROOM_ID)
|
||||
// Do not register the incoming call, so the manager doesn't know about it
|
||||
manager.hangUpCall(
|
||||
callType = CallType.RoomCall(notificationData.sessionId, notificationData.roomId),
|
||||
callType = CallType.RoomCall(notificationData.sessionId, notificationData.roomId, false),
|
||||
notificationData = notificationData,
|
||||
)
|
||||
coVerify {
|
||||
|
|
@ -321,12 +344,13 @@ class DefaultActiveCallManagerTest {
|
|||
val manager = createActiveCallManager(notificationManagerCompat = notificationManagerCompat)
|
||||
assertThat(manager.activeCall.value).isNull()
|
||||
|
||||
manager.joinedCall(CallType.RoomCall(A_SESSION_ID, A_ROOM_ID))
|
||||
manager.joinedCall(CallType.RoomCall(A_SESSION_ID, A_ROOM_ID, true))
|
||||
assertThat(manager.activeCall.value).isEqualTo(
|
||||
ActiveCall(
|
||||
callType = CallType.RoomCall(
|
||||
sessionId = A_SESSION_ID,
|
||||
roomId = A_ROOM_ID,
|
||||
isAudioCall = true,
|
||||
),
|
||||
callState = CallState.InCall,
|
||||
)
|
||||
|
|
@ -429,6 +453,7 @@ class DefaultActiveCallManagerTest {
|
|||
callType = CallType.RoomCall(
|
||||
sessionId = callNotificationData.sessionId,
|
||||
roomId = callNotificationData.roomId,
|
||||
isAudioCall = false,
|
||||
),
|
||||
callState = CallState.Ringing(callNotificationData)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ class DefaultCallWidgetProviderTest {
|
|||
@Test
|
||||
fun `getWidget - fails if the session does not exist`() = runTest {
|
||||
val provider = createProvider(matrixClientProvider = FakeMatrixClientProvider { Result.failure(Exception("Session not found")) })
|
||||
assertThat(provider.getWidget(A_SESSION_ID, A_ROOM_ID, "clientId", "languageTag", "theme").isFailure).isTrue()
|
||||
assertThat(provider.getWidget(A_SESSION_ID, A_ROOM_ID, false, "clientId", "languageTag", "theme").isFailure).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -40,7 +40,7 @@ class DefaultCallWidgetProviderTest {
|
|||
givenGetRoomResult(A_ROOM_ID, null)
|
||||
}
|
||||
val provider = createProvider(matrixClientProvider = FakeMatrixClientProvider { Result.success(client) })
|
||||
assertThat(provider.getWidget(A_SESSION_ID, A_ROOM_ID, "clientId", "languageTag", "theme").isFailure).isTrue()
|
||||
assertThat(provider.getWidget(A_SESSION_ID, A_ROOM_ID, true, "clientId", "languageTag", "theme").isFailure).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -52,7 +52,7 @@ class DefaultCallWidgetProviderTest {
|
|||
givenGetRoomResult(A_ROOM_ID, room)
|
||||
}
|
||||
val provider = createProvider(matrixClientProvider = FakeMatrixClientProvider { Result.success(client) })
|
||||
assertThat(provider.getWidget(A_SESSION_ID, A_ROOM_ID, "clientId", "languageTag", "theme").isFailure).isTrue()
|
||||
assertThat(provider.getWidget(A_SESSION_ID, A_ROOM_ID, false, "clientId", "languageTag", "theme").isFailure).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -65,7 +65,7 @@ class DefaultCallWidgetProviderTest {
|
|||
givenGetRoomResult(A_ROOM_ID, room)
|
||||
}
|
||||
val provider = createProvider(matrixClientProvider = FakeMatrixClientProvider { Result.success(client) })
|
||||
assertThat(provider.getWidget(A_SESSION_ID, A_ROOM_ID, "clientId", "languageTag", "theme").isFailure).isTrue()
|
||||
assertThat(provider.getWidget(A_SESSION_ID, A_ROOM_ID, false, "clientId", "languageTag", "theme").isFailure).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -78,7 +78,7 @@ class DefaultCallWidgetProviderTest {
|
|||
givenGetRoomResult(A_ROOM_ID, room)
|
||||
}
|
||||
val provider = createProvider(matrixClientProvider = FakeMatrixClientProvider { Result.success(client) })
|
||||
assertThat(provider.getWidget(A_SESSION_ID, A_ROOM_ID, "clientId", "languageTag", "theme").getOrNull()).isNotNull()
|
||||
assertThat(provider.getWidget(A_SESSION_ID, A_ROOM_ID, false, "clientId", "languageTag", "theme").getOrNull()).isNotNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -101,7 +101,7 @@ class DefaultCallWidgetProviderTest {
|
|||
matrixClientProvider = FakeMatrixClientProvider { Result.success(client) },
|
||||
activeRoomsHolder = activeRoomsHolder
|
||||
)
|
||||
assertThat(provider.getWidget(A_SESSION_ID, A_ROOM_ID, "clientId", "languageTag", "theme").isSuccess).isTrue()
|
||||
assertThat(provider.getWidget(A_SESSION_ID, A_ROOM_ID, false, "clientId", "languageTag", "theme").isSuccess).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -122,7 +122,7 @@ class DefaultCallWidgetProviderTest {
|
|||
callWidgetSettingsProvider = settingsProvider,
|
||||
appPreferencesStore = preferencesStore,
|
||||
)
|
||||
provider.getWidget(A_SESSION_ID, A_ROOM_ID, "clientId", "languageTag", "theme")
|
||||
provider.getWidget(A_SESSION_ID, A_ROOM_ID, false, "clientId", "languageTag", "theme")
|
||||
|
||||
assertThat(settingsProvider.providedBaseUrls).containsExactly("https://custom.element.io")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ class FakeCallWidgetProvider(
|
|||
override suspend fun getWidget(
|
||||
sessionId: SessionId,
|
||||
roomId: RoomId,
|
||||
isAudioCall: Boolean,
|
||||
clientId: String,
|
||||
languageTag: String?,
|
||||
theme: String?
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ fun aCallNotificationData(
|
|||
timestamp: Long = 0L,
|
||||
expirationTimestamp: Long = 30_000L,
|
||||
textContent: String? = null,
|
||||
audioOnly: Boolean = false,
|
||||
): CallNotificationData = CallNotificationData(
|
||||
sessionId = sessionId,
|
||||
roomId = roomId,
|
||||
|
|
@ -45,4 +46,5 @@ fun aCallNotificationData(
|
|||
timestamp = timestamp,
|
||||
expirationTimestamp = expirationTimestamp,
|
||||
textContent = textContent,
|
||||
audioOnly = audioOnly
|
||||
)
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
<string name="screen_create_room_action_create_room">"Naujas kambarys"</string>
|
||||
<string name="screen_create_room_add_people_title">"Pakviesti žmonių"</string>
|
||||
<string name="screen_create_room_error_creating_room">"Kuriant kambarį įvyko klaida"</string>
|
||||
<string name="screen_create_room_private_option_description">"Į šį kambarį gali patekti tik pakviesti žmonės. Visi pranešimai yra užšifruoti nuo pradžios iki galo."</string>
|
||||
<string name="screen_create_room_private_option_description">"Tik visapusiškai pakviestieji asmenys gali jungtis."</string>
|
||||
<string name="screen_create_room_public_option_description">"Bet kas gali rasti šį kambarį.
|
||||
Tai galite bet kada pakeisti kambario nustatymuose."</string>
|
||||
<string name="screen_create_room_topic_label">"Tema (nebūtina)"</string>
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ Du kan endre dette når som helst i rominnstillingene."</string>
|
|||
<string name="screen_create_room_public_option_title">"Offentlig"</string>
|
||||
<string name="screen_create_room_room_access_section_knocking_option_description">"Alle kan be om å få bli med, men en administrator eller moderator må godta forespørselen."</string>
|
||||
<string name="screen_create_room_room_access_section_knocking_option_title">"Be om å bli med"</string>
|
||||
<string name="screen_create_room_room_access_section_knocking_restricted_option_description">"Alle i %1$s kan bli med, mens alle andre må be om tilgang."</string>
|
||||
<string name="screen_create_room_room_access_section_knocking_restricted_option_title">"Be om å få bli med"</string>
|
||||
<string name="screen_create_room_room_access_section_private_option_description">"Bare inviterte personer kan bli med."</string>
|
||||
<string name="screen_create_room_room_access_section_private_option_title">"Privat"</string>
|
||||
|
|
@ -27,6 +28,7 @@ Du kan endre dette når som helst i rominnstillingene."</string>
|
|||
<string name="screen_create_room_room_address_section_title">"Adresse"</string>
|
||||
<string name="screen_create_room_room_visibility_section_title">"Romsynlighet"</string>
|
||||
<string name="screen_create_room_space_selection_no_space_description">"(ingen område)"</string>
|
||||
<string name="screen_create_room_space_selection_no_space_option">"Skal ikke legges til et område"</string>
|
||||
<string name="screen_create_room_space_selection_no_space_title">"Ingen områder valgt"</string>
|
||||
<string name="screen_create_room_space_selection_sheet_title">"Legg til området"</string>
|
||||
<string name="screen_create_room_topic_label">"Emne (valgfritt)"</string>
|
||||
|
|
|
|||
|
|
@ -12,5 +12,5 @@
|
|||
<string name="screen_identity_waiting_on_other_device">"Odotetaan toista laitetta…"</string>
|
||||
<string name="screen_notification_optin_subtitle">"Voit muuttaa asetuksia myöhemmin."</string>
|
||||
<string name="screen_notification_optin_title">"Salli ilmoitukset ja älä koskaan missaa viestejä"</string>
|
||||
<string name="screen_session_verification_enter_recovery_key">"Syötä palautusavain"</string>
|
||||
<string name="screen_session_verification_enter_recovery_key">"Anna palautusavain"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
<string name="banner_set_up_recovery_submit">"Ota palautus käyttöön"</string>
|
||||
<string name="banner_set_up_recovery_title">"Ota palautus käyttöön tilisi suojaamiseksi"</string>
|
||||
<string name="confirm_recovery_key_banner_message">"Vahvista palautusavaimesi, jotta pääset edelleen käyttämään avainten säilytystä ja viestihistoriaa."</string>
|
||||
<string name="confirm_recovery_key_banner_primary_button_title">"Syötä palautusavaimesi"</string>
|
||||
<string name="confirm_recovery_key_banner_primary_button_title">"Anna palautusavaimesi"</string>
|
||||
<string name="confirm_recovery_key_banner_secondary_button_title">"Unohditko palautusavaimesi?"</string>
|
||||
<string name="confirm_recovery_key_banner_title">"Avainten säilytys ei ole synkronoitu"</string>
|
||||
<string name="full_screen_intent_banner_message">"Salli koko näytön ilmoitukset, kun laite on lukittu, jos et halua koskaan missata tärkeää puhelua."</string>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_join_room_ban_by_message">"Du bannades av %1$s."</string>
|
||||
<string name="screen_join_room_ban_message">"Du bannades från det här rummet"</string>
|
||||
<string name="screen_join_room_ban_message">"Du bannades"</string>
|
||||
<string name="screen_join_room_ban_reason">"Anledning: %1$s."</string>
|
||||
<string name="screen_join_room_cancel_knock_action">"Avbryt begäran"</string>
|
||||
<string name="screen_join_room_cancel_knock_alert_confirmation">"Ja, avbryt"</string>
|
||||
|
|
@ -12,7 +12,7 @@
|
|||
<string name="screen_join_room_decline_and_block_alert_title">"Avvisa inbjudan och blockera"</string>
|
||||
<string name="screen_join_room_decline_and_block_button_title">"Avvisa och blockera"</string>
|
||||
<string name="screen_join_room_fail_message">"Misslyckades att gå med."</string>
|
||||
<string name="screen_join_room_fail_reason">"Detta rum är antingen endast för inbjudna eller så kan det finnas begränsningar för åtkomst på utrymmesnivå."</string>
|
||||
<string name="screen_join_room_fail_reason">"Du måste antingen bli inbjuden att gå med eller så kan det finnas åtkomstbegränsningar."</string>
|
||||
<string name="screen_join_room_forget_action">"Glöm"</string>
|
||||
<string name="screen_join_room_invite_required_message">"Du behöver en inbjudan för att gå med"</string>
|
||||
<string name="screen_join_room_join_action">"Gå med"</string>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
<string name="screen_link_new_device_desktop_submit">"Valmis skannaamaan"</string>
|
||||
<string name="screen_link_new_device_desktop_title">"Avaa %1$s pöytätietokoneella saadaksesi QR-koodin"</string>
|
||||
<string name="screen_link_new_device_enter_number_error_numbers_do_not_match">"Numerot eivät täsmää"</string>
|
||||
<string name="screen_link_new_device_enter_number_notice">"Syötä 2-numeroinen koodi"</string>
|
||||
<string name="screen_link_new_device_enter_number_notice">"Kirjoita 2-numeroinen koodi"</string>
|
||||
<string name="screen_link_new_device_enter_number_subtitle">"Tämä varmistaa, että yhteys toiseen laitteeseesi on turvallinen."</string>
|
||||
<string name="screen_link_new_device_enter_number_title">"Kirjoita toisessa laitteessa näkyvä numero"</string>
|
||||
<string name="screen_link_new_device_error_app_not_supported_subtitle">"Palveluntarjoajasi ei tue %1$s -sovellusta"</string>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<string name="screen_link_new_device_desktop_step1">"%1$s uygulamasını bir dizüstü veya masaüstü bilgisayarda açın"</string>
|
||||
<string name="screen_link_new_device_desktop_step3">"QR kodunu bu cihazla tarayın"</string>
|
||||
<string name="screen_link_new_device_desktop_submit">"Taramaya hazır"</string>
|
||||
<string name="screen_link_new_device_desktop_title">"QR kodu almak için %1$s uygulamasını masaüstü bilgisayarda açın%1$s%1$s"</string>
|
||||
<string name="screen_link_new_device_desktop_title">"QR kodu almak için %1$s uygulamasını masaüstü bilgisayarda açın"</string>
|
||||
<string name="screen_link_new_device_enter_number_error_numbers_do_not_match">"Sayılar uyuşmuyor"</string>
|
||||
<string name="screen_link_new_device_enter_number_notice">"2 haneli kodu girin"</string>
|
||||
<string name="screen_link_new_device_enter_number_subtitle">"Bu işlem, diğer cihazla olan bağlantının güvenli olduğunu doğrular."</string>
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@
|
|||
<string name="screen_login_error_invalid_user_id">"Tämä ei ole kelvollinen käyttäjätunnus. Odotettu muoto: \'@käyttäjä:kotipalvelin.fi\'"</string>
|
||||
<string name="screen_login_error_refresh_tokens">"Tämä palvelin on määritetty käyttämään refresh tokeneja. Näitä ei tueta salasanapohjaisen kirjautumisen kanssa."</string>
|
||||
<string name="screen_login_error_unsupported_authentication">"Valitsemasi kotipalvelin ei tue salasana- tai OIDC-kirjautumista. Ota yhteyttä palvelimesi ylläpitäjään tai valitse toinen kotipalvelin."</string>
|
||||
<string name="screen_login_form_header">"Syötä tietosi"</string>
|
||||
<string name="screen_login_form_header">"Anna tietosi"</string>
|
||||
<string name="screen_login_subtitle">"Matrix on avoin verkko turvallista, hajautettua viestintää varten."</string>
|
||||
<string name="screen_login_title">"Tervetuloa takaisin!"</string>
|
||||
<string name="screen_login_title_with_homeserver">"Kirjaudu sisään %1$s -palvelimelle"</string>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_account_provider_change">"Keisti paskyros teikėją"</string>
|
||||
<string name="screen_account_provider_form_hint">"Pagrindinio serverio adresas"</string>
|
||||
<string name="screen_account_provider_form_notice">"Įveskite paieškos terminą arba domeno adresą."</string>
|
||||
<string name="screen_account_provider_form_subtitle">"Ieškokite bendrovės, bendruomenės arba privataus serverio."</string>
|
||||
<string name="screen_account_provider_form_title">"Rasti paskyros teikėją"</string>
|
||||
<string name="screen_account_provider_signin_subtitle">"Čia bus saugomi Jūsų pokalbiai - panašiai kaip el. pašto paslaugų teikėjas saugo Jūsų el. laiškus."</string>
|
||||
|
|
|
|||
|
|
@ -272,10 +272,11 @@ class MessagesFlowNode(
|
|||
backstack.push(NavTarget.EditPoll(Timeline.Mode.Live, eventId))
|
||||
}
|
||||
|
||||
override fun navigateToRoomCall(roomId: RoomId) {
|
||||
override fun navigateToRoomCall(roomId: RoomId, isAudioCall: Boolean) {
|
||||
val callType = CallType.RoomCall(
|
||||
sessionId = sessionId,
|
||||
roomId = roomId,
|
||||
isAudioCall = isAudioCall
|
||||
)
|
||||
analyticsService.captureInteraction(Interaction.Name.MobileRoomCallButton)
|
||||
elementCallEntryPoint.startCall(callType)
|
||||
|
|
@ -488,10 +489,11 @@ class MessagesFlowNode(
|
|||
backstack.push(NavTarget.EditPoll(Timeline.Mode.Thread(navTarget.threadRootId), eventId))
|
||||
}
|
||||
|
||||
override fun navigateToRoomCall(roomId: RoomId) {
|
||||
override fun navigateToRoomCall(roomId: RoomId, isAudioCall: Boolean) {
|
||||
val callType = CallType.RoomCall(
|
||||
sessionId = sessionId,
|
||||
roomId = roomId,
|
||||
isAudioCall = isAudioCall
|
||||
)
|
||||
analyticsService.captureInteraction(Interaction.Name.MobileRoomCallButton)
|
||||
elementCallEntryPoint.startCall(callType)
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ class MessagesNode(
|
|||
fun navigateToSendLocation()
|
||||
fun navigateToCreatePoll()
|
||||
fun navigateToEditPoll(eventId: EventId)
|
||||
fun navigateToRoomCall(roomId: RoomId)
|
||||
fun navigateToRoomCall(roomId: RoomId, isAudioCall: Boolean)
|
||||
fun navigateToThread(threadRootId: ThreadId, focusedEventId: EventId?)
|
||||
fun navigateToRoomDetails()
|
||||
fun navigateToPinnedMessagesList()
|
||||
|
|
@ -279,7 +279,9 @@ class MessagesNode(
|
|||
},
|
||||
onSendLocationClick = callback::navigateToSendLocation,
|
||||
onCreatePollClick = callback::navigateToCreatePoll,
|
||||
onJoinCallClick = { callback.navigateToRoomCall(room.roomId) },
|
||||
onJoinCallClick = { isAudioCall ->
|
||||
callback.navigateToRoomCall(room.roomId, isAudioCall)
|
||||
},
|
||||
onViewAllPinnedMessagesClick = callback::navigateToPinnedMessagesList,
|
||||
modifier = modifier,
|
||||
knockRequestsBannerView = {
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ fun MessagesView(
|
|||
onLinkClick: (String, Boolean) -> Unit,
|
||||
onSendLocationClick: () -> Unit,
|
||||
onCreatePollClick: () -> Unit,
|
||||
onJoinCallClick: () -> Unit,
|
||||
onJoinCallClick: (isAudioCall: Boolean) -> Unit,
|
||||
onViewAllPinnedMessagesClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
forceJumpToBottomVisibility: Boolean = false,
|
||||
|
|
@ -423,7 +423,7 @@ private fun MessagesViewContent(
|
|||
onMessageLongClick: (TimelineItem.Event) -> Unit,
|
||||
onSendLocationClick: () -> Unit,
|
||||
onCreatePollClick: () -> Unit,
|
||||
onJoinCallClick: () -> Unit,
|
||||
onJoinCallClick: (isAudioCall: Boolean) -> Unit,
|
||||
onViewAllPinnedMessagesClick: () -> Unit,
|
||||
forceJumpToBottomVisibility: Boolean,
|
||||
onSwipeToReply: (TimelineItem.Event) -> Unit,
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ class ThreadedMessagesNode(
|
|||
fun navigateToSendLocation()
|
||||
fun navigateToCreatePoll()
|
||||
fun navigateToEditPoll(eventId: EventId)
|
||||
fun navigateToRoomCall(roomId: RoomId)
|
||||
fun navigateToRoomCall(roomId: RoomId, isAudioCall: Boolean)
|
||||
fun navigateToThread(threadRootId: ThreadId, focusedEventId: EventId?)
|
||||
}
|
||||
|
||||
|
|
@ -281,7 +281,9 @@ class ThreadedMessagesNode(
|
|||
},
|
||||
onSendLocationClick = callback::navigateToSendLocation,
|
||||
onCreatePollClick = callback::navigateToCreatePoll,
|
||||
onJoinCallClick = { callback.navigateToRoomCall(room.roomId) },
|
||||
onJoinCallClick = { isAudioCall ->
|
||||
callback.navigateToRoomCall(room.roomId, isAudioCall)
|
||||
},
|
||||
onViewAllPinnedMessagesClick = {},
|
||||
modifier = modifier,
|
||||
knockRequestsBannerView = {},
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ fun TimelineView(
|
|||
onReactionLongClick: (emoji: String, TimelineItem.Event) -> Unit,
|
||||
onMoreReactionsClick: (TimelineItem.Event) -> Unit,
|
||||
onReadReceiptClick: (TimelineItem.Event) -> Unit,
|
||||
onJoinCallClick: () -> Unit,
|
||||
onJoinCallClick: (isAudioCall: Boolean) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
lazyListState: LazyListState = rememberLazyListState(),
|
||||
forceJumpToBottomVisibility: Boolean = false,
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ package io.element.android.features.messages.impl.timeline.components
|
|||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.size
|
||||
|
|
@ -35,7 +36,7 @@ import io.element.android.libraries.ui.strings.CommonStrings
|
|||
@Composable
|
||||
internal fun CallMenuItem(
|
||||
roomCallState: RoomCallState,
|
||||
onJoinCallClick: () -> Unit,
|
||||
onJoinCallClick: (isAudioCall: Boolean) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
when (roomCallState) {
|
||||
|
|
@ -52,7 +53,7 @@ internal fun CallMenuItem(
|
|||
is RoomCallState.OnGoing -> {
|
||||
OnGoingCallMenuItem(
|
||||
roomCallState = roomCallState,
|
||||
onJoinCallClick = onJoinCallClick,
|
||||
onJoinCallClick = { onJoinCallClick(roomCallState.isAudioCall) },
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
|
@ -62,18 +63,31 @@ internal fun CallMenuItem(
|
|||
@Composable
|
||||
private fun StandByCallMenuItem(
|
||||
roomCallState: RoomCallState.StandBy,
|
||||
onJoinCallClick: () -> Unit,
|
||||
onJoinCallClick: (isAudioCall: Boolean) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
IconButton(
|
||||
modifier = modifier,
|
||||
onClick = onJoinCallClick,
|
||||
enabled = roomCallState.canStartCall,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = CompoundIcons.VideoCallSolid(),
|
||||
contentDescription = stringResource(CommonStrings.a11y_start_call),
|
||||
)
|
||||
Row(modifier = modifier) {
|
||||
// Only show voice call in DMs
|
||||
if (roomCallState.isDM) {
|
||||
IconButton(
|
||||
onClick = { onJoinCallClick(true) },
|
||||
enabled = roomCallState.canStartCall,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = CompoundIcons.VoiceCallSolid(),
|
||||
contentDescription = stringResource(CommonStrings.a11y_start_voice_call),
|
||||
)
|
||||
}
|
||||
}
|
||||
IconButton(
|
||||
onClick = { onJoinCallClick(false) },
|
||||
enabled = roomCallState.canStartCall,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = CompoundIcons.VideoCallSolid(),
|
||||
contentDescription = stringResource(CommonStrings.a11y_start_call),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -96,7 +110,11 @@ private fun OnGoingCallMenuItem(
|
|||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(20.dp),
|
||||
imageVector = CompoundIcons.VideoCallSolid(),
|
||||
imageVector = if (roomCallState.isAudioCall) {
|
||||
CompoundIcons.VoiceCallSolid()
|
||||
} else {
|
||||
CompoundIcons.VideoCallSolid()
|
||||
},
|
||||
contentDescription = null
|
||||
)
|
||||
Spacer(Modifier.width(8.dp))
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ internal fun TimelineItemCallNotifyView(
|
|||
event: TimelineItem.Event,
|
||||
roomCallState: RoomCallState,
|
||||
onLongClick: (TimelineItem.Event) -> Unit,
|
||||
onJoinCallClick: () -> Unit,
|
||||
onJoinCallClick: (isAudioCall: Boolean) -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Row(
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ internal fun TimelineItemRow(
|
|||
onMoreReactionsClick: (TimelineItem.Event) -> Unit,
|
||||
onReadReceiptClick: (TimelineItem.Event) -> Unit,
|
||||
onSwipeToReply: (TimelineItem.Event) -> Unit,
|
||||
onJoinCallClick: () -> Unit,
|
||||
onJoinCallClick: (isAudioCall: Boolean) -> Unit,
|
||||
eventSink: (TimelineEvent.TimelineItemEvent) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
eventContentView: @Composable (TimelineItem.Event, Modifier, (ContentAvoidingLayoutData) -> Unit) -> Unit =
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ internal fun MessagesViewTopBar(
|
|||
dmUserIdentityState: IdentityState?,
|
||||
sharedHistoryIcon: SharedHistoryIcon,
|
||||
onRoomDetailsClick: () -> Unit,
|
||||
onJoinCallClick: () -> Unit,
|
||||
onJoinCallClick: (isAudioCall: Boolean) -> Unit,
|
||||
onBackClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -19,8 +19,13 @@
|
|||
<string name="screen_room_attachment_source_camera_video">"Įrašyti vaizdo įrašą"</string>
|
||||
<string name="screen_room_attachment_source_files">"Priedas"</string>
|
||||
<string name="screen_room_attachment_source_gallery">"Nuotraukų ir vaizdo įrašų biblioteka"</string>
|
||||
<string name="screen_room_invite_again_alert_message">"Ar norėtumėte juos pakviesti atgal?"</string>
|
||||
<string name="screen_room_invite_again_alert_title">"Šiame pokalbyje esate vieni."</string>
|
||||
<string name="screen_room_retry_send_menu_send_again_action">"Siųsti vėl"</string>
|
||||
<string name="screen_room_retry_send_menu_title">"Jūsų žinutė nepavyko išsiųsti."</string>
|
||||
<string name="screen_room_timeline_beginning_of_room">"Tai yra %1$s pradžia."</string>
|
||||
<string name="screen_room_timeline_beginning_of_room_no_name">"Tai yra šio pokalbio pradžia."</string>
|
||||
<string name="screen_room_timeline_no_permission_to_post">"Neturite leidimą skelbti šiame kambaryje."</string>
|
||||
<string name="screen_room_timeline_read_marker_title">"Naujų"</string>
|
||||
<plurals name="screen_room_timeline_state_changes">
|
||||
<item quantity="one">"%1$d kambario pakeitimas"</item>
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ import io.element.android.features.messages.impl.timeline.components.receipt.aRe
|
|||
import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetEvent
|
||||
import io.element.android.features.messages.impl.timeline.model.TimelineItem
|
||||
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent
|
||||
import io.element.android.features.roomcall.api.aStandByCallState
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.room.tombstone.SuccessorRoom
|
||||
|
|
@ -71,6 +72,7 @@ import io.element.android.tests.testutils.EventsRecorder
|
|||
import io.element.android.tests.testutils.assertNoNodeWithText
|
||||
import io.element.android.tests.testutils.clickOn
|
||||
import io.element.android.tests.testutils.ensureCalledOnce
|
||||
import io.element.android.tests.testutils.ensureCalledOnceWithParam
|
||||
import io.element.android.tests.testutils.pressBack
|
||||
import io.element.android.tests.testutils.setSafeContent
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
|
@ -122,7 +124,7 @@ class MessagesViewTest {
|
|||
val state = aMessagesState(
|
||||
eventSink = eventsRecorder
|
||||
)
|
||||
ensureCalledOnce { callback ->
|
||||
ensureCalledOnceWithParam(false) { callback ->
|
||||
rule.setMessagesView(
|
||||
state = state,
|
||||
onJoinCallClick = callback,
|
||||
|
|
@ -132,6 +134,23 @@ class MessagesViewTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on join voice call invoke expected callback`() {
|
||||
val eventsRecorder = EventsRecorder<MessagesEvent>(expectEvents = false)
|
||||
val state = aMessagesState(
|
||||
eventSink = eventsRecorder,
|
||||
roomCallState = aStandByCallState(isDM = true)
|
||||
)
|
||||
ensureCalledOnceWithParam(true) { callback ->
|
||||
rule.setMessagesView(
|
||||
state = state,
|
||||
onJoinCallClick = callback,
|
||||
)
|
||||
val joinVoiceCallContentDescription = rule.activity.getString(CommonStrings.a11y_start_voice_call)
|
||||
rule.onNodeWithContentDescription(joinVoiceCallContentDescription).performClick()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking on an Event invoke expected callback`() {
|
||||
val eventsRecorder = EventsRecorder<MessagesEvent>(expectEvents = false)
|
||||
|
|
@ -609,7 +628,7 @@ private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setMessa
|
|||
onLinkClick: (String, Boolean) -> Unit = EnsureNeverCalledWithTwoParams(),
|
||||
onSendLocationClick: () -> Unit = EnsureNeverCalled(),
|
||||
onCreatePollClick: () -> Unit = EnsureNeverCalled(),
|
||||
onJoinCallClick: () -> Unit = EnsureNeverCalled(),
|
||||
onJoinCallClick: (Boolean) -> Unit = EnsureNeverCalledWithParam(),
|
||||
onViewAllPinnedMessagesClick: () -> Unit = EnsureNeverCalled(),
|
||||
) {
|
||||
setSafeContent {
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ import io.element.android.libraries.matrix.api.timeline.Timeline
|
|||
import io.element.android.libraries.matrix.api.timeline.item.event.MessageShield
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import io.element.android.tests.testutils.EnsureNeverCalled
|
||||
import io.element.android.tests.testutils.EnsureNeverCalledWithParam
|
||||
import io.element.android.tests.testutils.EnsureNeverCalledWithTwoParams
|
||||
import io.element.android.tests.testutils.EventsRecorder
|
||||
|
|
@ -186,7 +185,7 @@ private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setTimel
|
|||
onReactionLongClick: (emoji: String, TimelineItem.Event) -> Unit = EnsureNeverCalledWithTwoParams(),
|
||||
onMoreReactionsClick: (TimelineItem.Event) -> Unit = EnsureNeverCalledWithParam(),
|
||||
onReadReceiptClick: (TimelineItem.Event) -> Unit = EnsureNeverCalledWithParam(),
|
||||
onJoinCallClick: () -> Unit = EnsureNeverCalled(),
|
||||
onJoinCallClick: (Boolean) -> Unit = EnsureNeverCalledWithParam(),
|
||||
forceJumpToBottomVisibility: Boolean = false,
|
||||
) {
|
||||
setSafeContent(clearAndroidUiDispatcher = true) {
|
||||
|
|
|
|||
|
|
@ -24,5 +24,11 @@ interface NetworkMonitor {
|
|||
/**
|
||||
* Checks if the active network is being blocked by Doze, even if it's available.
|
||||
*/
|
||||
fun isNetworkBlocked(): Boolean
|
||||
val isNetworkBlocked: StateFlow<Boolean>
|
||||
|
||||
/**
|
||||
* A flow indicating whether the app is running in an air-gapped environment.
|
||||
* An air-gapped environment is an environment that is not connected to the internet, and where the app can only communicate with a limited set of servers.
|
||||
*/
|
||||
val isInAirGappedEnvironment: StateFlow<Boolean>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import extension.setupDependencyInjection
|
||||
import extension.testCommonDependencies
|
||||
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
|
|
@ -23,4 +24,8 @@ dependencies {
|
|||
implementation(projects.libraries.core)
|
||||
implementation(projects.libraries.di)
|
||||
api(projects.features.networkmonitor.api)
|
||||
|
||||
testCommonDependencies(libs)
|
||||
testImplementation(projects.libraries.matrix.test)
|
||||
testImplementation(projects.features.networkmonitor.test)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,18 +13,21 @@ package io.element.android.features.networkmonitor.impl
|
|||
import android.content.Context
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.Network
|
||||
import android.net.NetworkCapabilities
|
||||
import android.net.NetworkRequest
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
import dev.zacsweers.metro.SingleIn
|
||||
import io.element.android.features.networkmonitor.api.NetworkMonitor
|
||||
import io.element.android.features.networkmonitor.api.NetworkStatus
|
||||
import io.element.android.libraries.core.meta.BuildMeta
|
||||
import io.element.android.libraries.di.annotations.AppCoroutineScope
|
||||
import io.element.android.libraries.di.annotations.ApplicationContext
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.channels.trySendBlocking
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
|
|
@ -39,13 +42,13 @@ import java.util.concurrent.atomic.AtomicInteger
|
|||
@SingleIn(AppScope::class)
|
||||
class DefaultNetworkMonitor(
|
||||
@ApplicationContext context: Context,
|
||||
@AppCoroutineScope
|
||||
appCoroutineScope: CoroutineScope,
|
||||
@AppCoroutineScope appCoroutineScope: CoroutineScope,
|
||||
private val buildMeta: BuildMeta,
|
||||
) : NetworkMonitor {
|
||||
private val connectivityManager: ConnectivityManager = context.getSystemService(ConnectivityManager::class.java)
|
||||
private val blockedNetworkBlockedChecker = NetworkBlockedChecker(connectivityManager)
|
||||
|
||||
override fun isNetworkBlocked(): Boolean = blockedNetworkBlockedChecker.isNetworkBlocked()
|
||||
override val isNetworkBlocked = MutableStateFlow(NetworkBlockedChecker(connectivityManager).isNetworkBlocked())
|
||||
override val isInAirGappedEnvironment = MutableStateFlow(false)
|
||||
|
||||
override val connectivity: StateFlow<NetworkStatus> = callbackFlow {
|
||||
|
||||
|
|
@ -63,6 +66,27 @@ class DefaultNetworkMonitor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun onBlockedStatusChanged(network: Network, blocked: Boolean) {
|
||||
Timber.d("Network ${network.networkHandle} blocked status changed: $blocked.")
|
||||
if (network.networkHandle == connectivityManager.activeNetwork?.networkHandle) {
|
||||
// If the network is blocked, it means that Doze is preventing the app from using the network, even if it's available.
|
||||
isNetworkBlocked.value = blocked
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
|
||||
if (!buildMeta.isEnterpriseBuild) {
|
||||
// The air-gapped environment detection is only relevant for the enterprise build.
|
||||
return
|
||||
}
|
||||
|
||||
if (network.networkHandle == connectivityManager.activeNetwork?.networkHandle) {
|
||||
// If the network doesn't have the NET_CAPABILITY_VALIDATED capability, it means that the network is not able to reach the internet
|
||||
// (according to Google), which is a common case in air-gapped environments.
|
||||
isInAirGappedEnvironment.value = !networkCapabilities.capabilities.contains(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAvailable(network: Network) {
|
||||
if (activeNetworksCount.incrementAndGet() > 0) {
|
||||
trySendBlocking(NetworkStatus.Connected)
|
||||
|
|
|
|||
|
|
@ -14,10 +14,10 @@ import android.net.ConnectivityManager
|
|||
import android.net.NetworkInfo
|
||||
|
||||
/**
|
||||
* Helper to check if the active network in [ConnectivityManager] is blocked.
|
||||
* Helper to synchronously check if the active network in [ConnectivityManager] is blocked.
|
||||
*
|
||||
* This is extracted to its own class because it uses deprecated APIs (but the only ones that are reliable)
|
||||
* and we don't want to suppress deprecations everywhere.
|
||||
* and we don't want to suppress deprecations everywhere in the file this would be called.
|
||||
*/
|
||||
class NetworkBlockedChecker(
|
||||
private val connectivityManager: ConnectivityManager,
|
||||
|
|
|
|||
|
|
@ -14,8 +14,16 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
|||
|
||||
class FakeNetworkMonitor(
|
||||
initialStatus: NetworkStatus = NetworkStatus.Connected,
|
||||
private val isNetworkBlockedLambda: () -> Boolean = { false },
|
||||
) : NetworkMonitor {
|
||||
override val connectivity = MutableStateFlow(initialStatus)
|
||||
override fun isNetworkBlocked(): Boolean = isNetworkBlockedLambda()
|
||||
override val isNetworkBlocked = MutableStateFlow(false)
|
||||
override val isInAirGappedEnvironment = MutableStateFlow(false)
|
||||
|
||||
fun givenNetworkBlocked(isBlocked: Boolean) {
|
||||
isNetworkBlocked.value = isBlocked
|
||||
}
|
||||
|
||||
fun givenIsInAirGappedEnvironment(isInAirGapped: Boolean) {
|
||||
isInAirGappedEnvironment.value = isInAirGapped
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@
|
|||
package io.element.android.features.rageshake.impl.crash
|
||||
|
||||
import android.os.Build
|
||||
import android.os.TransactionTooLargeException
|
||||
import io.element.android.libraries.architecture.appyx.lastCapturedNavState
|
||||
import io.element.android.libraries.core.data.tryOrNull
|
||||
import timber.log.Timber
|
||||
import java.io.PrintWriter
|
||||
|
|
@ -61,6 +63,13 @@ class VectorUncaughtExceptionHandler(
|
|||
val sw = StringWriter()
|
||||
val pw = PrintWriter(sw, true)
|
||||
throwable.printStackTrace(pw)
|
||||
|
||||
if (throwable is RuntimeException && throwable.cause is TransactionTooLargeException) {
|
||||
pw.append('\n')
|
||||
pw.append(lastCapturedNavState)
|
||||
Timber.v(lastCapturedNavState)
|
||||
}
|
||||
|
||||
append(sw.buffer.toString())
|
||||
}
|
||||
Timber.e("FATAL EXCEPTION $bugDescription")
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_room_change_permissions_room_details">"Redaguoti kambarį"</string>
|
||||
<string name="screen_room_change_permissions_room_details">"Redaguoti informaciją"</string>
|
||||
<plurals name="screen_room_member_list_header_title">
|
||||
<item quantity="one">"%1$d asmuo"</item>
|
||||
<item quantity="few">"%1$d asmenys"</item>
|
||||
|
|
|
|||
|
|
@ -18,10 +18,12 @@ sealed interface RoomCallState {
|
|||
|
||||
data class StandBy(
|
||||
val canStartCall: Boolean,
|
||||
val isDM: Boolean,
|
||||
) : RoomCallState
|
||||
|
||||
data class OnGoing(
|
||||
val canJoinCall: Boolean,
|
||||
val isAudioCall: Boolean,
|
||||
val isUserInTheCall: Boolean,
|
||||
val isUserLocallyInTheCall: Boolean,
|
||||
) : RoomCallState
|
||||
|
|
|
|||
|
|
@ -14,9 +14,11 @@ open class RoomCallStateProvider : PreviewParameterProvider<RoomCallState> {
|
|||
override val values: Sequence<RoomCallState> = sequenceOf(
|
||||
aStandByCallState(),
|
||||
aStandByCallState(canStartCall = false),
|
||||
aStandByCallState(canStartCall = false, isDM = true),
|
||||
anOngoingCallState(),
|
||||
anOngoingCallState(canJoinCall = false),
|
||||
anOngoingCallState(canJoinCall = true, isUserInTheCall = true),
|
||||
anOngoingCallState(canJoinCall = true, isAudioCall = true),
|
||||
RoomCallState.Unavailable,
|
||||
)
|
||||
}
|
||||
|
|
@ -25,14 +27,18 @@ fun anOngoingCallState(
|
|||
canJoinCall: Boolean = true,
|
||||
isUserInTheCall: Boolean = false,
|
||||
isUserLocallyInTheCall: Boolean = isUserInTheCall,
|
||||
isAudioCall: Boolean = false,
|
||||
) = RoomCallState.OnGoing(
|
||||
canJoinCall = canJoinCall,
|
||||
isUserInTheCall = isUserInTheCall,
|
||||
isUserLocallyInTheCall = isUserLocallyInTheCall,
|
||||
isAudioCall = isAudioCall
|
||||
)
|
||||
|
||||
fun aStandByCallState(
|
||||
canStartCall: Boolean = true,
|
||||
isDM: Boolean = false,
|
||||
) = RoomCallState.StandBy(
|
||||
canStartCall = canStartCall,
|
||||
isDM
|
||||
)
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import io.element.android.features.enterprise.api.SessionEnterpriseService
|
|||
import io.element.android.features.roomcall.api.RoomCallState
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||
import io.element.android.libraries.matrix.api.room.isDm
|
||||
import io.element.android.libraries.matrix.api.room.powerlevels.canCall
|
||||
import io.element.android.libraries.matrix.api.room.powerlevels.permissionsAsState
|
||||
|
||||
|
|
@ -56,8 +57,13 @@ class RoomCallStatePresenter(
|
|||
canJoinCall = canJoinCall,
|
||||
isUserInTheCall = isUserInTheCall,
|
||||
isUserLocallyInTheCall = isUserLocallyInTheCall,
|
||||
// TODO resolve intent while the call is ongoing
|
||||
isAudioCall = false
|
||||
)
|
||||
else -> RoomCallState.StandBy(
|
||||
canStartCall = canJoinCall,
|
||||
isDM = roomInfo.isDm
|
||||
)
|
||||
else -> RoomCallState.StandBy(canStartCall = canJoinCall)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ class RoomCallStatePresenterTest {
|
|||
assertThat(initialState).isEqualTo(
|
||||
RoomCallState.StandBy(
|
||||
canStartCall = false,
|
||||
isDM = false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
@ -79,6 +80,28 @@ class RoomCallStatePresenterTest {
|
|||
assertThat(initialState).isEqualTo(
|
||||
RoomCallState.StandBy(
|
||||
canStartCall = true,
|
||||
isDM = false
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - initial state - when is direct room`() = runTest {
|
||||
val room = FakeJoinedRoom(
|
||||
baseRoom = FakeBaseRoom(
|
||||
initialRoomInfo = aRoomInfo(isDirect = true),
|
||||
roomPermissions = roomPermissions(true),
|
||||
)
|
||||
)
|
||||
val presenter = createRoomCallStatePresenter(joinedRoom = room)
|
||||
presenter.test {
|
||||
skipItems(1)
|
||||
val initialState = awaitItem()
|
||||
assertThat(initialState).isEqualTo(
|
||||
RoomCallState.StandBy(
|
||||
canStartCall = true,
|
||||
isDM = true
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
@ -98,6 +121,7 @@ class RoomCallStatePresenterTest {
|
|||
assertThat(awaitItem()).isEqualTo(
|
||||
RoomCallState.OnGoing(
|
||||
canJoinCall = false,
|
||||
isAudioCall = false,
|
||||
isUserInTheCall = false,
|
||||
isUserLocallyInTheCall = false,
|
||||
)
|
||||
|
|
@ -125,6 +149,7 @@ class RoomCallStatePresenterTest {
|
|||
assertThat(awaitItem()).isEqualTo(
|
||||
RoomCallState.OnGoing(
|
||||
canJoinCall = true,
|
||||
isAudioCall = false,
|
||||
isUserInTheCall = true,
|
||||
isUserLocallyInTheCall = false,
|
||||
)
|
||||
|
|
@ -155,6 +180,7 @@ class RoomCallStatePresenterTest {
|
|||
assertThat(awaitItem()).isEqualTo(
|
||||
RoomCallState.OnGoing(
|
||||
canJoinCall = true,
|
||||
isAudioCall = false,
|
||||
isUserInTheCall = true,
|
||||
isUserLocallyInTheCall = true,
|
||||
)
|
||||
|
|
@ -187,6 +213,7 @@ class RoomCallStatePresenterTest {
|
|||
assertThat(awaitItem()).isEqualTo(
|
||||
RoomCallState.OnGoing(
|
||||
canJoinCall = true,
|
||||
isAudioCall = false,
|
||||
isUserInTheCall = true,
|
||||
isUserLocallyInTheCall = true,
|
||||
)
|
||||
|
|
@ -195,6 +222,7 @@ class RoomCallStatePresenterTest {
|
|||
assertThat(awaitItem()).isEqualTo(
|
||||
RoomCallState.OnGoing(
|
||||
canJoinCall = true,
|
||||
isAudioCall = false,
|
||||
isUserInTheCall = true,
|
||||
isUserLocallyInTheCall = false,
|
||||
)
|
||||
|
|
@ -208,6 +236,7 @@ class RoomCallStatePresenterTest {
|
|||
assertThat(awaitItem()).isEqualTo(
|
||||
RoomCallState.OnGoing(
|
||||
canJoinCall = true,
|
||||
isAudioCall = false,
|
||||
isUserInTheCall = false,
|
||||
isUserLocallyInTheCall = false,
|
||||
)
|
||||
|
|
@ -221,6 +250,7 @@ class RoomCallStatePresenterTest {
|
|||
assertThat(awaitItem()).isEqualTo(
|
||||
RoomCallState.StandBy(
|
||||
canStartCall = true,
|
||||
isDM = false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ import io.element.android.libraries.matrix.api.core.EventId
|
|||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias
|
||||
import io.element.android.libraries.matrix.api.notification.CallIntent
|
||||
import io.element.android.libraries.matrix.api.permalink.PermalinkData
|
||||
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||
import io.element.android.libraries.matrix.api.verification.VerificationRequest
|
||||
|
|
@ -223,10 +224,11 @@ class RoomDetailsFlowNode(
|
|||
backstack.push(NavTarget.RoomMemberDetails(userId))
|
||||
}
|
||||
|
||||
override fun navigateToRoomCall() {
|
||||
override fun navigateToRoomCall(callIntent: CallIntent) {
|
||||
val inputs = CallType.RoomCall(
|
||||
sessionId = room.sessionId,
|
||||
roomId = room.roomId,
|
||||
isAudioCall = callIntent == CallIntent.AUDIO
|
||||
)
|
||||
analyticsService.captureInteraction(Interaction.Name.MobileRoomCallButton)
|
||||
elementCallEntryPoint.startCall(inputs)
|
||||
|
|
@ -284,8 +286,14 @@ class RoomDetailsFlowNode(
|
|||
callback.navigateToRoom(roomId, emptyList())
|
||||
}
|
||||
|
||||
override fun startCall(dmRoomId: RoomId) {
|
||||
elementCallEntryPoint.startCall(CallType.RoomCall(roomId = dmRoomId, sessionId = room.sessionId))
|
||||
override fun startCall(dmRoomId: RoomId, callIntent: CallIntent) {
|
||||
elementCallEntryPoint.startCall(
|
||||
CallType.RoomCall(
|
||||
roomId = dmRoomId,
|
||||
sessionId = room.sessionId,
|
||||
isAudioCall = callIntent == CallIntent.AUDIO
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun startVerifyUserFlow(userId: UserId) {
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import io.element.android.libraries.architecture.appyx.launchMolecule
|
|||
import io.element.android.libraries.architecture.callback
|
||||
import io.element.android.libraries.di.RoomScope
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.notification.CallIntent
|
||||
import io.element.android.libraries.matrix.api.room.BaseRoom
|
||||
import io.element.android.services.analytics.api.AnalyticsService
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
|
@ -59,7 +60,7 @@ class RoomDetailsNode(
|
|||
fun navigateToKnockRequestsList()
|
||||
fun navigateToSecurityAndPrivacy()
|
||||
fun navigateToRoomMemberDetails(userId: UserId)
|
||||
fun navigateToRoomCall()
|
||||
fun navigateToRoomCall(callIntent: CallIntent)
|
||||
fun navigateToReportRoom()
|
||||
fun navigateToSelectNewOwnersWhenLeaving()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ import io.element.android.libraries.designsystem.utils.snackbar.rememberSnackbar
|
|||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.notification.CallIntent
|
||||
import io.element.android.libraries.matrix.api.room.RoomMember
|
||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||
import io.element.android.libraries.matrix.api.room.getBestName
|
||||
|
|
@ -105,7 +106,7 @@ fun RoomDetailsView(
|
|||
openPollHistory: () -> Unit,
|
||||
openMediaGallery: () -> Unit,
|
||||
openAdminSettings: () -> Unit,
|
||||
onJoinCallClick: () -> Unit,
|
||||
onJoinCallClick: (CallIntent) -> Unit,
|
||||
onPinnedMessagesClick: () -> Unit,
|
||||
onKnockRequestsClick: () -> Unit,
|
||||
onSecurityAndPrivacyClick: () -> Unit,
|
||||
|
|
@ -327,7 +328,7 @@ private fun MainActionsSection(
|
|||
state: RoomDetailsState,
|
||||
onShareRoom: () -> Unit,
|
||||
onInvitePeople: () -> Unit,
|
||||
onCall: () -> Unit,
|
||||
onCall: (callIntent: CallIntent) -> Unit,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
|
|
@ -356,10 +357,19 @@ private fun MainActionsSection(
|
|||
}
|
||||
if (state.roomCallState.hasPermissionToJoin()) {
|
||||
// TODO Improve the view depending on all the cases here?
|
||||
if (state.roomType is RoomDetailsType.Dm) {
|
||||
// As per design, only show voice call in DM
|
||||
MainActionButton(
|
||||
title = stringResource(CommonStrings.action_call),
|
||||
imageVector = CompoundIcons.VoiceCall(),
|
||||
onClick = { onCall(CallIntent.AUDIO) },
|
||||
)
|
||||
}
|
||||
|
||||
MainActionButton(
|
||||
title = stringResource(CommonStrings.action_call),
|
||||
title = stringResource(CommonStrings.common_video),
|
||||
imageVector = CompoundIcons.VideoCall(),
|
||||
onClick = onCall,
|
||||
onClick = { onCall(CallIntent.VIDEO) },
|
||||
)
|
||||
}
|
||||
if (state.roomType is RoomDetailsType.Room) {
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import io.element.android.libraries.architecture.inputs
|
|||
import io.element.android.libraries.di.RoomScope
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.notification.CallIntent
|
||||
import io.element.android.libraries.matrix.api.permalink.PermalinkBuilder
|
||||
import io.element.android.services.analytics.api.AnalyticsService
|
||||
|
||||
|
|
@ -67,8 +68,8 @@ class RoomMemberDetailsNode(
|
|||
callback.navigateToRoom(roomId)
|
||||
}
|
||||
|
||||
fun onStartCall(roomId: RoomId) {
|
||||
callback.startCall(roomId)
|
||||
fun onStartCall(roomId: RoomId, callIntent: CallIntent) {
|
||||
callback.startCall(roomId, callIntent)
|
||||
}
|
||||
|
||||
val state = presenter.present()
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_room_change_permissions_room_details">"Redaguoti kambarį"</string>
|
||||
<string name="screen_room_change_permissions_room_details">"Redaguoti informaciją"</string>
|
||||
<string name="screen_room_details_add_topic_title">"Pridėti temą"</string>
|
||||
<string name="screen_room_details_edit_room_title">"Redaguoti kambarį"</string>
|
||||
<string name="screen_room_details_edit_room_title">"Redaguoti informaciją"</string>
|
||||
<string name="screen_room_details_edition_error">"Įvyko nežinoma klaida ir informacijos pakeisti nepavyko."</string>
|
||||
<string name="screen_room_details_edition_error_title">"Nepavyko atnaujinti kambario"</string>
|
||||
<string name="screen_room_details_encryption_enabled_subtitle">"Žinutės yra užrakintos. Tik Jūs ir gavėjai turite unikalius raktus joms atrakinti."</string>
|
||||
|
|
|
|||
|
|
@ -50,6 +50,8 @@
|
|||
<string name="screen_room_details_error_loading_notification_settings">"Ett fel uppstod vid laddning av aviseringsinställningar."</string>
|
||||
<string name="screen_room_details_error_muting">"Misslyckades att tysta det här rummet, vänligen pröva igen."</string>
|
||||
<string name="screen_room_details_error_unmuting">"Misslyckades att avtysta det här rummet, vänligen pröva igen."</string>
|
||||
<string name="screen_room_details_invite_people_dont_close">"Stäng inte appen förrän det är klart."</string>
|
||||
<string name="screen_room_details_invite_people_preparing">"Förbereder inbjudningar …"</string>
|
||||
<string name="screen_room_details_invite_people_title">"Bjud in personer"</string>
|
||||
<string name="screen_room_details_leave_conversation_title">"Lämna konversation"</string>
|
||||
<string name="screen_room_details_leave_room_title">"Lämna rum"</string>
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
|
|||
import io.element.android.features.roomdetails.impl.members.aRoomMember
|
||||
import io.element.android.features.userprofile.shared.aUserProfileState
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.notification.CallIntent
|
||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||
import io.element.android.libraries.testtags.TestTags
|
||||
|
|
@ -121,7 +122,25 @@ class RoomDetailsViewTest {
|
|||
|
||||
@Test
|
||||
fun `click on call invokes expected callback`() {
|
||||
ensureCalledOnce { callback ->
|
||||
ensureCalledOnceWithParam(CallIntent.AUDIO) { callback ->
|
||||
rule.setRoomDetailView(
|
||||
state = aRoomDetailsState(
|
||||
eventSink = EventsRecorder(expectEvents = false),
|
||||
canInvite = true,
|
||||
roomType = RoomDetailsType.Dm(
|
||||
aRoomMember(UserId("@me:local.org")),
|
||||
aRoomMember(UserId("@other:local.org"))
|
||||
),
|
||||
),
|
||||
onJoinCallClick = callback,
|
||||
)
|
||||
rule.clickOn(CommonStrings.action_call)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `click on video call invokes expected callback`() {
|
||||
ensureCalledOnceWithParam(CallIntent.VIDEO) { callback ->
|
||||
rule.setRoomDetailView(
|
||||
state = aRoomDetailsState(
|
||||
eventSink = EventsRecorder(expectEvents = false),
|
||||
|
|
@ -129,7 +148,7 @@ class RoomDetailsViewTest {
|
|||
),
|
||||
onJoinCallClick = callback,
|
||||
)
|
||||
rule.clickOn(CommonStrings.action_call)
|
||||
rule.clickOn(CommonStrings.common_video)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -343,7 +362,7 @@ private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setRoomD
|
|||
openPollHistory: () -> Unit = EnsureNeverCalled(),
|
||||
openMediaGallery: () -> Unit = EnsureNeverCalled(),
|
||||
openAdminSettings: () -> Unit = EnsureNeverCalled(),
|
||||
onJoinCallClick: () -> Unit = EnsureNeverCalled(),
|
||||
onJoinCallClick: (CallIntent) -> Unit = EnsureNeverCalledWithParam(),
|
||||
onPinnedMessagesClick: () -> Unit = EnsureNeverCalled(),
|
||||
onKnockRequestsClick: () -> Unit = EnsureNeverCalled(),
|
||||
onSecurityAndPrivacyClick: () -> Unit = EnsureNeverCalled(),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_room_details_edit_room_title">"Redaguoti kambarį"</string>
|
||||
<string name="screen_room_details_edit_room_title">"Redaguoti informaciją"</string>
|
||||
<string name="screen_room_details_edition_error">"Įvyko nežinoma klaida ir informacijos pakeisti nepavyko."</string>
|
||||
<string name="screen_room_details_edition_error_title">"Nepavyko atnaujinti kambario"</string>
|
||||
<string name="screen_room_details_updating_room">"Atnaujinamas kambarys…"</string>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
<string name="screen_chat_backup_key_storage_toggle_title">"Salli avainten säilytys"</string>
|
||||
<string name="screen_chat_backup_recovery_action_change">"Vaihda palautusavain"</string>
|
||||
<string name="screen_chat_backup_recovery_action_change_description">"Palauta kryptografinen identiteettisi ja viestihistoriasi palautusavaimella, jos olet menettänyt kaikki nykyiset laitteesi."</string>
|
||||
<string name="screen_chat_backup_recovery_action_confirm">"Syötä palautusavain"</string>
|
||||
<string name="screen_chat_backup_recovery_action_confirm">"Anna palautusavain"</string>
|
||||
<string name="screen_chat_backup_recovery_action_confirm_description">"Avainten säilytys ei ole tällä hetkellä synkronoitu."</string>
|
||||
<string name="screen_chat_backup_recovery_action_setup">"Ota palautus käyttöön"</string>
|
||||
<string name="screen_chat_backup_recovery_action_setup_description">"Pääset käsiksi salattuihin viesteihisi, jos menetät kaikki laitteesi tai olet kirjautunut ulos %1$s -sovelluksesta kaikkialla."</string>
|
||||
|
|
@ -43,10 +43,10 @@
|
|||
<string name="screen_recovery_key_confirm_error_content">"Yritä uudelleen vahvistaaksesi pääsyn avainten säilytykseen."</string>
|
||||
<string name="screen_recovery_key_confirm_error_title">"Väärä palautusavain"</string>
|
||||
<string name="screen_recovery_key_confirm_key_description">"Jos sinulla on turva-avain tai turvalause, sekin toimii."</string>
|
||||
<string name="screen_recovery_key_confirm_key_placeholder">"Syötä…"</string>
|
||||
<string name="screen_recovery_key_confirm_key_placeholder">"Kirjoita…"</string>
|
||||
<string name="screen_recovery_key_confirm_lost_recovery_key">"Hukkasitko palautusavaimesi?"</string>
|
||||
<string name="screen_recovery_key_confirm_success">"Palautusavain vahvistettu"</string>
|
||||
<string name="screen_recovery_key_confirm_title">"Syötä palautusavaimesi"</string>
|
||||
<string name="screen_recovery_key_confirm_title">"Anna palautusavaimesi"</string>
|
||||
<string name="screen_recovery_key_copied_to_clipboard">"Palautusavain kopioitu"</string>
|
||||
<string name="screen_recovery_key_generating_key">"Luodaan…"</string>
|
||||
<string name="screen_recovery_key_save_action">"Tallenna palautusavain"</string>
|
||||
|
|
@ -64,7 +64,7 @@
|
|||
<string name="screen_reset_encryption_confirmation_alert_subtitle">"Tätä prosessia ei voi peruuttaa."</string>
|
||||
<string name="screen_reset_encryption_confirmation_alert_title">"Haluatko varmasti nollata identiteettisi?"</string>
|
||||
<string name="screen_reset_encryption_password_error">"Tapahtui tuntematon virhe. Tarkista, että tilisi salasana on oikein ja yritä uudelleen."</string>
|
||||
<string name="screen_reset_encryption_password_placeholder">"Syötä…"</string>
|
||||
<string name="screen_reset_encryption_password_placeholder">"Kirjoita…"</string>
|
||||
<string name="screen_reset_encryption_password_subtitle">"Vahvista, että haluat nollata identiteettisi."</string>
|
||||
<string name="screen_reset_encryption_password_title">"Kirjoita tilisi salasana jatkaaksesi"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -8,13 +8,16 @@
|
|||
</plurals>
|
||||
<string name="screen_leave_space_subtitle">"Velg rommene du vil forlate, som du ikke er den eneste administratoren for:"</string>
|
||||
<string name="screen_leave_space_subtitle_last_admin">"Du må tildele en annen administrator for dette området før du kan forlate det."</string>
|
||||
<string name="screen_leave_space_subtitle_last_owner">"Du er den eneste eieren av%1$s. Du må overføre eierskapet til noen andre før du drar."</string>
|
||||
<string name="screen_leave_space_subtitle_only_last_admin">"Du vil ikke bli fjernet fra følgende rom fordi du er den eneste administratoren:"</string>
|
||||
<string name="screen_leave_space_title">"Forlat %1$s?"</string>
|
||||
<string name="screen_leave_space_title_last_admin">"Du er den eneste administratoren for %1$s"</string>
|
||||
<string name="screen_leave_space_title_last_owner">"Overfør eierskap"</string>
|
||||
<string name="screen_space_add_room_action">"Rom"</string>
|
||||
<string name="screen_space_add_rooms_room_access_description">"Hvis du legger til et rom, vil det ikke påvirke tilgangen til rommet. For å endre tilgangen, gå til Rominnstillinger > Sikkerhet og personvern."</string>
|
||||
<string name="screen_space_empty_state_title">"Legg til ditt første rom"</string>
|
||||
<string name="screen_space_menu_action_members">"Vis medlemmer"</string>
|
||||
<string name="screen_space_remove_rooms_confirmation_content">"Fjerning av et rom vil ikke påvirke tilgangen til rommet. For å endre tilgangen, gå til Rominformasjon > Personvern og sikkerhet."</string>
|
||||
<plurals name="screen_space_remove_rooms_confirmation_title">
|
||||
<item quantity="one">"Fjern %1$d rom fra %2$s"</item>
|
||||
<item quantity="other">"Fjern %1$d rommene fra %2$s"</item>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_leave_space_choose_owners_action">"Välj ägare"</string>
|
||||
<string name="screen_leave_space_title">"Lämna %1$s?"</string>
|
||||
<string name="screen_space_settings_leave_space">"Lämna utrymmet"</string>
|
||||
<string name="screen_space_settings_roles_and_permissions">"Roller och behörigheter"</string>
|
||||
<string name="screen_space_settings_security_and_privacy">"Säkerhet och sekretess"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
<string name="screen_start_chat_error_starting_chat">"Keskustelun aloituksessa tapahtui virhe"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_action">"Liity huoneeseen osoitteella"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_invalid_address">"Osoite ei ole kelvollinen"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_placeholder">"Syötä…"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_placeholder">"Kirjoita…"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_found">"Täsmäävä huone löytyi"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_room_not_found">"Huonetta ei löytynyt"</string>
|
||||
<string name="screen_start_chat_join_room_by_address_supporting_text">"esim. #huoneen-nimi:matrix.org"</string>
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ import io.element.android.libraries.matrix.api.core.EventId
|
|||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.notification.CallIntent
|
||||
import io.element.android.libraries.matrix.api.verification.VerificationRequest
|
||||
import io.element.android.libraries.mediaviewer.api.MediaViewerEntryPoint
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
|
@ -83,8 +84,14 @@ class UserProfileFlowNode(
|
|||
callback.navigateToRoom(roomId)
|
||||
}
|
||||
|
||||
override fun startCall(dmRoomId: RoomId) {
|
||||
elementCallEntryPoint.startCall(CallType.RoomCall(sessionId = sessionId, roomId = dmRoomId))
|
||||
override fun startCall(dmRoomId: RoomId, callIntent: CallIntent) {
|
||||
elementCallEntryPoint.startCall(
|
||||
CallType.RoomCall(
|
||||
sessionId = sessionId,
|
||||
roomId = dmRoomId,
|
||||
isAudioCall = callIntent == CallIntent.AUDIO
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun startVerifyUserFlow(userId: UserId) {
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue