Merge branch 'develop' into feature-oled-black
This commit is contained in:
commit
d0dcbab750
1505 changed files with 14143 additions and 9545 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
|
||||
|
|
|
|||
15
.github/pull_request_template.md
vendored
15
.github/pull_request_template.md
vendored
|
|
@ -1,4 +1,12 @@
|
|||
<!-- Please read [CONTRIBUTING.md](https://github.com/element-hq/element-x-android/blob/develop/CONTRIBUTING.md) before submitting your pull request -->
|
||||
<!--
|
||||
|
||||
Please read [CONTRIBUTING.md](https://github.com/element-hq/element-x-android/blob/develop/CONTRIBUTING.md) before submitting your pull request.
|
||||
|
||||
Are you adding a new feature? Keep in mind that it needs to be added to [the iOS client](https://github.com/element-hq/element-x-ios) too, unless it's related to an Android OS only behaviour.
|
||||
|
||||
**IMPORTANT:** if you are adding new screens or modifying existing ones, this needs acceptance from the product and design teams before being merged. For this, it's better to start with a [feature request issue](https://github.com/element-hq/element-x-android/issues/new?template=enhancement.yml) describing the change you want to make and the motivation behind it instead of directly creating a pull request. This will allow the product and design teams to give feedback on the change before you start working on it, and avoid you doing work that might end up being rejected.
|
||||
|
||||
-->
|
||||
|
||||
## Content
|
||||
|
||||
|
|
@ -44,10 +52,13 @@ Uncomment this markdown table below and edit the last line `|||`:
|
|||
|
||||
<!-- Depending on the Pull Request content, it can be acceptable if some of the following checkboxes stay unchecked. -->
|
||||
|
||||
- This PR was made with the help of AI:
|
||||
- [ ] Yes. In this case, please request a review by Copilot.
|
||||
- [ ] No.
|
||||
- [ ] Changes have been tested on an Android device or Android emulator with API 24
|
||||
- [ ] UI change has been tested on both light and dark themes
|
||||
- [ ] Accessibility has been taken into account. See https://github.com/element-hq/element-x-android/blob/develop/CONTRIBUTING.md#accessibility
|
||||
- [ ] Pull request is based on the develop branch
|
||||
- [ ] Pull request title will be used in the release note, it clearly define what will change for the user
|
||||
- [ ] Pull request title will be used in the release note, it clearly defines what will change for the user
|
||||
- [ ] Pull request includes screenshots or videos if containing UI changes
|
||||
- [ ] You've made a self review of your PR
|
||||
|
|
|
|||
30
.github/renovate.json
vendored
30
.github/renovate.json
vendored
|
|
@ -1,30 +0,0 @@
|
|||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"config:recommended"
|
||||
],
|
||||
"labels": [
|
||||
"PR-Dependencies"
|
||||
],
|
||||
"ignoreDeps": [
|
||||
"string:app_name",
|
||||
"gradle"
|
||||
],
|
||||
"packageRules": [
|
||||
{
|
||||
"groupName": "kotlin",
|
||||
"matchPackageNames": [
|
||||
"/^org.jetbrains.kotlin/",
|
||||
"/^com.google.devtools.ksp/",
|
||||
"/^androidx.compose.compiler/"
|
||||
]
|
||||
},
|
||||
{
|
||||
"versioning": "semver",
|
||||
"matchPackageNames": [
|
||||
"/^org.maplibre/",
|
||||
"/^org.jetbrains.kotlinx:kotlinx-datetime/"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
47
.github/renovate.json5
vendored
Normal file
47
.github/renovate.json5
vendored
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"config:recommended",
|
||||
],
|
||||
"labels": [
|
||||
"PR-Dependencies",
|
||||
],
|
||||
"ignoreDeps": [
|
||||
"string:app_name",
|
||||
"gradle",
|
||||
],
|
||||
"minimumReleaseAge": "7 days",
|
||||
"packageRules": [
|
||||
{
|
||||
"matchPackageNames": [
|
||||
"org.matrix.rustcomponents:sdk-android",
|
||||
"/^io.element.android/",
|
||||
],
|
||||
"minimumReleaseAge": null,
|
||||
},
|
||||
{
|
||||
"groupName": "kotlin",
|
||||
"matchPackageNames": [
|
||||
"/^org.jetbrains.kotlin/",
|
||||
"/^com.google.devtools.ksp/",
|
||||
"/^androidx.compose.compiler/",
|
||||
],
|
||||
},
|
||||
{
|
||||
"versioning": "semver",
|
||||
"matchPackageNames": [
|
||||
"/^org.maplibre/",
|
||||
"/^org.jetbrains.kotlinx:kotlinx-datetime/",
|
||||
],
|
||||
},
|
||||
{
|
||||
// Limit PostHog Android upgrade to one PR per month, the first day of the month
|
||||
"matchPackageNames": [
|
||||
"com.posthog:posthog-android",
|
||||
],
|
||||
"schedule": [
|
||||
"* * 1 * *",
|
||||
]
|
||||
},
|
||||
],
|
||||
}
|
||||
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: |
|
||||
|
|
|
|||
13
.github/workflows/build_enterprise.yml
vendored
13
.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,24 +43,25 @@ 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
|
||||
uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0
|
||||
with:
|
||||
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
|
||||
- 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: |
|
||||
|
|
|
|||
8
.github/workflows/danger.yml
vendored
8
.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,9 +11,11 @@ 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
|
||||
uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0
|
||||
with:
|
||||
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
|
||||
- name: Clone submodules
|
||||
|
|
|
|||
10
.github/workflows/fork-pr-notice.yml
vendored
10
.github/workflows/fork-pr-notice.yml
vendored
|
|
@ -2,20 +2,25 @@ 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
|
||||
permissions:
|
||||
# Require to comment the PR.
|
||||
pull-requests: write
|
||||
name: Welcome comment
|
||||
# Only display it if base repo (upstream) is different from HEAD repo (possibly a fork)
|
||||
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({
|
||||
|
|
@ -24,6 +29,7 @@ jobs:
|
|||
repo: context.repo.repo,
|
||||
body: `Thank you for your contribution! Here are a few things to check in the PR to ensure it's reviewed as quickly as possible:
|
||||
|
||||
- If your pull request adds a feature or modifies the UI, this should have an equivalent pull request in the [Element X iOS repo](https://github.com/element-hq/element-x-ios) unless it only affects an Android-only behaviour or is behind a disabled feature flag, since we need parity in both clients to consider a feature done. It will also need to be approved by our product and design teams before being merged, so it's usually a good idea to discuss the changes in a Github issue first and then start working on them once the approach has been validated.
|
||||
- Your branch should be based on \`origin/develop\`, at least when it was created.
|
||||
- The title of the PR will be used for release notes, so it needs to describe the change visible to the user.
|
||||
- The test pass locally running \`./gradlew test\`.
|
||||
|
|
|
|||
10
.github/workflows/generate_github_pages.yml
vendored
10
.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
|
||||
|
|
@ -12,18 +14,18 @@ jobs:
|
|||
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||
steps:
|
||||
- name: ⏬ Checkout with LFS
|
||||
uses: nschloe/action-cached-lfs-checkout@f46300cd8952454b9f0a21a3d133d4bd5684cfc2 # v1.2.3
|
||||
uses: nschloe/action-cached-lfs-checkout@1c185ad576953eab13e35ffe1bffef437d97e9d2 # v1.2.4
|
||||
- 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@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
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@e89f39f1abbbd05b1113a29cf4db69e7540cae5a # v2.37.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'
|
||||
|
|
|
|||
20
.github/workflows/nightlyReports.yml
vendored
20
.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
|
||||
|
|
@ -32,16 +34,16 @@ jobs:
|
|||
swap-storage: false
|
||||
|
||||
- name: ⏬ Checkout with LFS
|
||||
uses: nschloe/action-cached-lfs-checkout@f46300cd8952454b9f0a21a3d133d4bd5684cfc2 # v1.2.3
|
||||
uses: nschloe/action-cached-lfs-checkout@1c185ad576953eab13e35ffe1bffef437d97e9d2 # v1.2.4
|
||||
|
||||
- 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: |
|
||||
|
|
|
|||
13
.github/workflows/pull_request.yml
vendored
13
.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({
|
||||
|
|
@ -52,13 +54,16 @@ jobs:
|
|||
close-if-fork-develop:
|
||||
name: Forbid develop branch fork contributions
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
# Require to comment and close the PR.
|
||||
pull-requests: write
|
||||
if: >
|
||||
github.event.action == 'opened' &&
|
||||
github.event.pull_request.head.ref == 'develop' &&
|
||||
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({
|
||||
|
|
|
|||
124
.github/workflows/quality.yml
vendored
124
.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,9 +33,11 @@ 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
|
||||
uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0
|
||||
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||
with:
|
||||
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
|
||||
|
|
@ -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,13 +93,14 @@ 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
|
||||
uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0
|
||||
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||
with:
|
||||
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
|
||||
|
|
@ -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,13 +134,14 @@ 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
|
||||
uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0
|
||||
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||
with:
|
||||
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
|
||||
|
|
@ -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,13 +168,14 @@ 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
|
||||
uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0
|
||||
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||
with:
|
||||
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
|
||||
|
|
@ -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,13 +213,14 @@ 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
|
||||
uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0
|
||||
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||
with:
|
||||
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
|
||||
|
|
@ -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,13 +254,14 @@ 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
|
||||
uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0
|
||||
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||
with:
|
||||
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
|
||||
|
|
@ -256,56 +269,49 @@ 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
|
||||
uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0
|
||||
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||
with:
|
||||
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
|
||||
- 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@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
- name: Prepare Danger
|
||||
if: always()
|
||||
run: |
|
||||
|
|
|
|||
13
.github/workflows/recordScreenshots.yml
vendored
13
.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
|
||||
|
|
@ -12,6 +14,9 @@ env:
|
|||
|
||||
jobs:
|
||||
record:
|
||||
permissions:
|
||||
# Need write permissions on PRs to remove the label "Record-Screenshots"
|
||||
pull-requests: write
|
||||
name: Record screenshots on branch ${{ github.event.pull_request.head.ref || github.ref_name }}
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'workflow_dispatch' || github.event.label.name == 'Record-Screenshots'
|
||||
|
|
@ -38,23 +43,23 @@ jobs:
|
|||
labels: Record-Screenshots
|
||||
- name: ⏬ Checkout with LFS (PR)
|
||||
if: github.event.label.name == 'Record-Screenshots'
|
||||
uses: nschloe/action-cached-lfs-checkout@f46300cd8952454b9f0a21a3d133d4bd5684cfc2 # v1.2.3
|
||||
uses: nschloe/action-cached-lfs-checkout@1c185ad576953eab13e35ffe1bffef437d97e9d2 # v1.2.4
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.ref || github.ref }}
|
||||
- name: ⏬ Checkout with LFS (Branch)
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
uses: nschloe/action-cached-lfs-checkout@f46300cd8952454b9f0a21a3d133d4bd5684cfc2 # v1.2.3
|
||||
uses: nschloe/action-cached-lfs-checkout@1c185ad576953eab13e35ffe1bffef437d97e9d2 # v1.2.4
|
||||
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
|
||||
|
|
|
|||
34
.github/workflows/release.yml
vendored
34
.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,21 +71,23 @@ 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
|
||||
uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0
|
||||
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||
with:
|
||||
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
|
||||
- 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: |
|
||||
|
|
|
|||
11
.github/workflows/sonar.yml
vendored
11
.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,22 +38,23 @@ 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
|
||||
run: ./gradlew assembleDebug createFullJarDebugTestFixtures :app:createFullJarGplayDebugTestFixtures $CI_GRADLE_ARG_PROPERTIES
|
||||
run: ./gradlew assembleGplayDebug createFullJarDebugTestFixtures :app:createFullJarGplayDebugTestFixtures $CI_GRADLE_ARG_PROPERTIES
|
||||
- name: 🔊 Publish results to Sonar
|
||||
env:
|
||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||
|
|
|
|||
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
|
||||
|
|
|
|||
18
.github/workflows/tests.yml
vendored
18
.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
|
||||
|
|
@ -47,13 +49,13 @@ jobs:
|
|||
sudo swapon /mnt/swapfile
|
||||
sudo swapon --show
|
||||
- name: ⏬ Checkout with LFS
|
||||
uses: nschloe/action-cached-lfs-checkout@f46300cd8952454b9f0a21a3d133d4bd5684cfc2 # v1.2.3
|
||||
uses: nschloe/action-cached-lfs-checkout@1c185ad576953eab13e35ffe1bffef437d97e9d2 # v1.2.4
|
||||
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 }}
|
||||
- name: Add SSH private keys for submodule repositories
|
||||
uses: webfactory/ssh-agent@a6f90b1f127823b31d4d4a8d96047790581349bd # v0.9.1
|
||||
uses: webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555 # v0.10.0
|
||||
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
|
||||
with:
|
||||
ssh-private-key: ${{ secrets.ELEMENT_ENTERPRISE_DEPLOY_KEY }}
|
||||
|
|
@ -61,21 +63,21 @@ 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: ⚙️ Check coverage for debug variant (includes unit & screenshot tests)
|
||||
run: ./gradlew testDebugUnitTest :tests:uitests:verifyPaparazziDebug :app:koverXmlReportGplayDebug :app:koverHtmlReportGplayDebug :app:koverVerifyAll $CI_GRADLE_ARG_PROPERTIES
|
||||
run: ./gradlew testDebugUnitTest :tests:uitests:verifyPaparazziDebug :koverXmlReportMerged :koverHtmlReportMerged :koverVerifyAll $CI_GRADLE_ARG_PROPERTIES
|
||||
|
||||
- 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: |
|
||||
|
|
@ -110,5 +112,5 @@ jobs:
|
|||
with:
|
||||
fail_ci_if_error: true
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
files: app/build/reports/kover/reportGplayDebug.xml
|
||||
files: build/reports/kover/reportMerged.xml
|
||||
verbose: true
|
||||
|
|
|
|||
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 }}
|
||||
|
|
|
|||
4
.github/workflows/validate-lfs.yml
vendored
4
.github/workflows/validate-lfs.yml
vendored
|
|
@ -2,12 +2,14 @@ name: Validate Git LFS
|
|||
|
||||
on: [pull_request, merge_group]
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
name: Validate
|
||||
steps:
|
||||
- uses: nschloe/action-cached-lfs-checkout@f46300cd8952454b9f0a21a3d133d4bd5684cfc2 # v1.2.3
|
||||
- uses: nschloe/action-cached-lfs-checkout@1c185ad576953eab13e35ffe1bffef437d97e9d2 # v1.2.4
|
||||
|
||||
- run: |
|
||||
./tools/git/validate_lfs.sh
|
||||
|
|
|
|||
4
.idea/kotlinc.xml
generated
4
.idea/kotlinc.xml
generated
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="KotlinJpsPluginSettings">
|
||||
<option name="version" value="2.3.10" />
|
||||
<option name="version" value="2.3.20" />
|
||||
</component>
|
||||
</project>
|
||||
</project>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
appId: ${MAESTRO_APP_ID}
|
||||
---
|
||||
- extendedWaitUntil:
|
||||
visible: "Enter recovery key"
|
||||
visible: "Use recovery key"
|
||||
timeout: 30000
|
||||
- takeScreenshot: build/maestro/150-Verify
|
||||
- tapOn: "Enter recovery key"
|
||||
- tapOn: "Use recovery key"
|
||||
- tapOn:
|
||||
id: "verification-recovery_key"
|
||||
- inputText: ${MAESTRO_RECOVERY_KEY}
|
||||
|
|
|
|||
118
AGENTS.md
Normal file
118
AGENTS.md
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
# AGENTS.md — Element X Android
|
||||
|
||||
> **Repo:** `element-hq/element-x-android` — Android Matrix client (Compose UI + `matrix-rust-sdk`).
|
||||
|
||||
---
|
||||
|
||||
## Strong Conventions
|
||||
|
||||
PRs must meet these rules.
|
||||
|
||||
### Code Style
|
||||
|
||||
- Style enforced by **Editor config** (`.editorconfig`).
|
||||
- Set "Hard wrap at" to 160 chars in Android Studio.
|
||||
|
||||
### PII & Logging
|
||||
|
||||
- We use **Timber** for logging. Never use `android.util.Log`.
|
||||
- **Never log secrets, passwords, keys, or user content** (e.g. message bodies).
|
||||
- Matrix IDs (User IDs, Room IDs, Event IDs) are safe to log.
|
||||
|
||||
### Strings & Localisation
|
||||
|
||||
- Default localisation: `en` (en-GB strings), shared with Element X iOS via [Localazy](https://localazy.com/p/element).
|
||||
- **Never edit `localazy.xml`** — it is auto-generated and overwritten.
|
||||
- New English strings go in **`temporary.xml`**. The core team imports these to Localazy.
|
||||
- **Key naming**:
|
||||
- Cross-screen verbs: `action_` (e.g., `action_copy`).
|
||||
- Common nouns/other: `common_` (e.g., `common_error`).
|
||||
- Accessibility: `a11y_`.
|
||||
- Screen-specific: `screen_<name>_<key>` (e.g., `screen_onboarding_welcome_title`).
|
||||
- Errors: `error_` prefix.
|
||||
- Platform-specific: `_ios` or `_android` suffix.
|
||||
- Placeholders: Use numbered form `%1$s`, `%2$d`.
|
||||
|
||||
### Previews
|
||||
|
||||
- Create previews for **all main states** of a Composable.
|
||||
- Use `@PreviewsDayNight` for consistency.
|
||||
- Use `PreviewParameterProvider` (e.g., `FooStateProvider`) to provide states.
|
||||
- Wrap previews in `ElementPreview { ... }`.
|
||||
|
||||
---
|
||||
|
||||
## Pull Request Guidelines
|
||||
|
||||
- Use sentence-style commit/PR messages (no conventional commits).
|
||||
- Apply exactly **one** `PR-` label for changelog categorization.
|
||||
- PR title = changelog entry — make it descriptive; no "Fixes #…" prefixes.
|
||||
- Include screenshots or screen recordings for any UI changes.
|
||||
- Keep PRs focused; split changes over 1000 lines.
|
||||
|
||||
---
|
||||
|
||||
## Project Structure
|
||||
|
||||
### Build System
|
||||
|
||||
Common Gradle tasks:
|
||||
- Build: `./gradlew assembleDebug`
|
||||
- Unit Tests: `./gradlew test`
|
||||
- Lint: `./gradlew lint`
|
||||
- Format: `./gradlew ktlintFormat`
|
||||
- Update Docs TOC: `./gradlew generateDocsToc`
|
||||
|
||||
### Gradle Modules
|
||||
|
||||
Features follow a 3-module structure:
|
||||
- `features/foo/api`: Public interfaces and data classes.
|
||||
- `features/foo/impl`: Internal implementation, Presenter, and View.
|
||||
- `features/foo/test`: Test fakes and utilities.
|
||||
|
||||
---
|
||||
|
||||
## Architecture: Appyx + Molecule
|
||||
|
||||
We use [Appyx](https://bumble-tech.github.io/appyx/) for navigation and [Molecule](https://github.com/cashapp/molecule) for Presenters.
|
||||
|
||||
### Files Per Screen (`Foo`)
|
||||
|
||||
| File | Purpose |
|
||||
| :--- | :--- |
|
||||
| `FooNode.kt` | Appyx Node: Handles navigation and wires the Presenter to the View. |
|
||||
| `FooPresenter.kt` | A `@Composable` function that produces `FooState` from `FooEvent`s. |
|
||||
| `FooView.kt` | Stateless Composable rendering the UI from `FooState`. |
|
||||
| `FooState.kt` | Data class representing the immutable UI state. |
|
||||
| `FooEvent.kt` | Sealed interface for UI actions sent to the Presenter. |
|
||||
| `FooStateProvider.kt` | Provides sample states for Previews and Screenshot tests. |
|
||||
| `FooPresenterTest.kt` | Unit tests for the Presenter logic using Turbine. |
|
||||
|
||||
---
|
||||
|
||||
## Dependency Injection (Metro)
|
||||
|
||||
- We use [Metro](https://zacsweers.github.io/metro/) for DI.
|
||||
- Inject via constructor parameters using `@Inject`.
|
||||
- Use `@AssistedInject` and `@AssistedFactory` for components requiring runtime arguments (like Navigators or IDs).
|
||||
- Use `@ContributesBinding(AppScope::class)` for singleton-like services.
|
||||
- Use `@ContributesNode(RoomScope::class)` for Appyx Nodes.
|
||||
|
||||
---
|
||||
|
||||
## Compound Design System
|
||||
|
||||
Always prefer Compound components and tokens from `libraries/compound/` module.
|
||||
|
||||
- **Colours**: `ElementTheme.colors.textPrimary`, `ElementTheme.colors.bgCanvasDefault`.
|
||||
- **Typography**: `ElementTheme.typography.fontBodyMdRegular`.
|
||||
- **Icons**: Use `CompoundIcons.IconName()` (e.g., `CompoundIcons.UserProfileSolid()`).
|
||||
|
||||
---
|
||||
|
||||
## The Rust SDK Layer
|
||||
|
||||
We wrap the `matrix-rust-sdk` to isolate the UI from the underlying SDK.
|
||||
- Naming: SDK `Room` → `JoinedRoom` or `RoomInfo`.
|
||||
- Type Mapping: Map Rust SDK types to Kotlin data classes in the `api` module to avoid leaking `MatrixRustSDK` into the UI.
|
||||
- Always follow Kotlin naming conventions (e.g., `userId` instead of `userID`).
|
||||
154
CHANGES.md
154
CHANGES.md
|
|
@ -1,3 +1,157 @@
|
|||
Changes in Element X v26.03.4
|
||||
=============================
|
||||
|
||||
<!-- Release notes generated using configuration in .github/release.yml at v26.03.4 -->
|
||||
|
||||
## What's Changed
|
||||
### ✨ Features
|
||||
* Add a foreground service with a wakelock for fetching push notifications by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6321
|
||||
### 🙌 Improvements
|
||||
* Iterate on send button colors by @bmarty in https://github.com/element-hq/element-x-android/pull/6314
|
||||
### 🐛 Bugfixes
|
||||
* Fix key storage if it's broken by @andybalaam in https://github.com/element-hq/element-x-android/pull/6290
|
||||
* Improve error displayed when .well-known file is malformed by @bmarty in https://github.com/element-hq/element-x-android/pull/6370
|
||||
* Fix crash when starting a DM by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6419
|
||||
* Fix media seeking flicker by @bxdxnn in https://github.com/element-hq/element-x-android/pull/6434
|
||||
* Fix `TransactionTooLargeExceptions` caused by Appyx by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6410
|
||||
* Fix wakelock not stopping early when notifications are disabled by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6424
|
||||
* Fix long messages not being clickable by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6356
|
||||
* Fix: "Reset identity" flow leaves backup disabled #5075 by @andybalaam in https://github.com/element-hq/element-x-android/pull/6420
|
||||
* Restore custom user certificate provider by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6451
|
||||
### 🗣 Translations
|
||||
* Sync Strings - iterate on wording about crypto identity by @ElementBot in https://github.com/element-hq/element-x-android/pull/6352
|
||||
* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/6435
|
||||
### 🧱 Build
|
||||
* Limit number of created PR to upgrade Posthog dependency by @bmarty in https://github.com/element-hq/element-x-android/pull/6318
|
||||
* Renovate: add a cooldown of 7 days for dependencies that we do not manage by @bmarty in https://github.com/element-hq/element-x-android/pull/6323
|
||||
* Improve Kover setup by using only convention plugins by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6213
|
||||
* Fix permissions issue. by @bmarty in https://github.com/element-hq/element-x-android/pull/6355
|
||||
* Fix permissions issue. by @bmarty in https://github.com/element-hq/element-x-android/pull/6366
|
||||
### 📄 Documentation
|
||||
* Add warning about new features to pull request template by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6425
|
||||
### Dependency upgrades
|
||||
* fix(deps): update dependency com.posthog:posthog-android to v3.36.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6311
|
||||
* fix(deps): update dependency com.posthog:posthog-android to v3.36.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6316
|
||||
* chore(deps): update reactivecircus/android-emulator-runner action to v2.36.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6320
|
||||
* fix(deps): update dependency com.posthog:posthog-android to v3.37.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6317
|
||||
* chore(deps): update actions/download-artifact action to v8.0.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6324
|
||||
* fix(deps): update dependency com.github.matrix-org:matrix-analytics-events to v0.33.2 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6313
|
||||
* chore(deps): update plugin ktlint to v14.2.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6332
|
||||
* fix(deps): update dependency androidx.compose:compose-bom to v2026.03.00 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6329
|
||||
* fix(deps): update datastore to v1.2.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6326
|
||||
* chore(deps): update webfactory/ssh-agent action to v0.10.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6325
|
||||
* fix(deps): update activity to v1.13.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6327
|
||||
* fix(deps): update dependency io.sentry:sentry-android to v8.35.0 and enable ANR profiling by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6331
|
||||
* fix(deps): update dependency org.matrix.rustcomponents:sdk-android to v26.03.19 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6411
|
||||
* chore(deps): update reactivecircus/android-emulator-runner action to v2.37.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6430
|
||||
* fix(deps): update media3 to v1.9.3 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6445
|
||||
* fix(deps): update dependency org.matrix.rustcomponents:sdk-android to v26.03.23 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6444
|
||||
* fix(deps): update dependency androidx.compose.material3:material3 to v1.5.0-alpha15 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6306
|
||||
* fix(deps): update dependency org.matrix.rustcomponents:sdk-android to v26.03.24 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6455
|
||||
### Others
|
||||
* fix(deps): update sqldelight to v2.3.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6343
|
||||
* Remove matrix.to intent filter from the AndroidManifest. by @bmarty in https://github.com/element-hq/element-x-android/pull/6345
|
||||
* Update wording of button "Enter recovery key" to "Use recovery key" by @bmarty in https://github.com/element-hq/element-x-android/pull/6357
|
||||
* Fix room member not tappable in a Thread by @bxdxnn in https://github.com/element-hq/element-x-android/pull/6416
|
||||
* Fix keyboard not auto-opening when editing a message by @kalix127 in https://github.com/element-hq/element-x-android/pull/6412
|
||||
* Design iteration on file attachment in the timeline by @bmarty in https://github.com/element-hq/element-x-android/pull/6322
|
||||
* fix(deps): update dependency org.maplibre.gl:android-sdk to v13.0.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6428
|
||||
* Iterate on microphone icon by @bmarty in https://github.com/element-hq/element-x-android/pull/6452
|
||||
* Increase icon size of audio and files in the timeline by @bmarty in https://github.com/element-hq/element-x-android/pull/6453
|
||||
* Fix voice recording being interrupted by notifications sounds by @kalix127 in https://github.com/element-hq/element-x-android/pull/6438
|
||||
|
||||
## New Contributors
|
||||
* @bxdxnn made their first contribution in https://github.com/element-hq/element-x-android/pull/6416
|
||||
* @kalix127 made their first contribution in https://github.com/element-hq/element-x-android/pull/6412
|
||||
|
||||
**Full Changelog**: https://github.com/element-hq/element-x-android/compare/v26.03.3...v26.03.4
|
||||
|
||||
Changes in Element X v26.03.3
|
||||
=============================
|
||||
|
||||
<!-- Release notes generated using configuration in .github/release.yml at v26.03.3 -->
|
||||
|
||||
## What's Changed
|
||||
### ✨ Features
|
||||
* Support for Voice Call only (no video), parity with web by @BillCarsonFr in https://github.com/element-hq/element-x-android/pull/5995
|
||||
### 🐛 Bugfixes
|
||||
* Fix read receipts not appearing in threaded timelines by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6297
|
||||
* Try fixing index OOB issues in `Editable.checkSuggestionNeeded` by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6303
|
||||
### 🗣 Translations
|
||||
* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/6302
|
||||
### 🧱 Build
|
||||
* Add zizmorcore/zizmor-action by @bmarty in https://github.com/element-hq/element-x-android/pull/6286
|
||||
* Add use existing branch confirmation and progress for file download by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6294
|
||||
* Replace `knit` with `generate_toc.py` script by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6279
|
||||
### Dependency upgrades
|
||||
* Update plugin sonarqube to v7.2.3.7755 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6283
|
||||
* Update dependency io.sentry:sentry-android to v8.34.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6289
|
||||
* Update dependency org.matrix.rustcomponents:sdk-android to v26.03.6 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6292
|
||||
* Update dependency com.posthog:posthog-android to v3.35.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6293
|
||||
* Update zizmorcore/zizmor-action action to v0.5.2 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6299
|
||||
* fix(deps): update dependency org.maplibre.gl:android-sdk to v13 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6277
|
||||
* fix(deps): update dependency org.matrix.rustcomponents:sdk-android to v26.03.09 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6307
|
||||
* fix(deps): update dependency org.matrix.rustcomponents:sdk-android to v26.03.11 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/6310
|
||||
### Others
|
||||
* Add code to help debugging the saved nav state graph by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6295
|
||||
* Add network constraints for fetching notifications with `WorkManager` by @jmartinesp in https://github.com/element-hq/element-x-android/pull/6305
|
||||
|
||||
|
||||
**Full Changelog**: https://github.com/element-hq/element-x-android/compare/v26.03.2...v26.03.3
|
||||
|
||||
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
|
||||
=============================
|
||||
|
||||
|
|
|
|||
1
CLAUDE.md
Normal file
1
CLAUDE.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
@AGENTS.md
|
||||
|
|
@ -10,13 +10,14 @@
|
|||
* [I want to add new strings to the project](#i-want-to-add-new-strings-to-the-project)
|
||||
* [I want to help translating Element](#i-want-to-help-translating-element)
|
||||
* [Element X Android Gallery](#element-x-android-gallery)
|
||||
* [I want to add a new feature to Element X Android](#i-want-to-add-a-new-feature-to-element-x-android)
|
||||
* [I want to submit a PR to fix an issue](#i-want-to-submit-a-pr-to-fix-an-issue)
|
||||
* [Kotlin](#kotlin)
|
||||
* [Changelog](#changelog)
|
||||
* [Code quality](#code-quality)
|
||||
* [detekt](#detekt)
|
||||
* [ktlint](#ktlint)
|
||||
* [knit](#knit)
|
||||
* [checkDocs](#checkdocs)
|
||||
* [lint](#lint)
|
||||
* [Unit tests](#unit-tests)
|
||||
* [konsist](#konsist)
|
||||
|
|
@ -76,6 +77,14 @@ Once added to Localazy, translations can be checked screen per screen using our
|
|||
|
||||
Localazy syncs occur every Monday and the screenshots on this page are generated every Tuesday, so you'll have to wait to see your change appearing on Element X Android Gallery.
|
||||
|
||||
## I want to add a new feature to Element X Android
|
||||
|
||||
Thank you for contributing to the project! Please have a look in the [dedicated documentation](./docs/pull_request.md) about pull request.
|
||||
|
||||
Also, please keep in mind that any feature added to Element X Android needs to be added to [the iOS client](https://github.com/element-hq/element-x-ios) too, unless it's related to an Android OS only behaviour.
|
||||
|
||||
**IMPORTANT:** if you are adding new screens or modifying existing ones, this needs acceptance from the product and design teams before being merged. For this, it's better to start with a [feature request issue](https://github.com/element-hq/element-x-android/issues/new?template=enhancement.yml) describing the change you want to make and the motivation behind it instead of directly creating a pull request. This will allow the product and design teams to give feedback on the change before you start working on it, and avoid you doing work that might end up being rejected.
|
||||
|
||||
## I want to submit a PR to fix an issue
|
||||
|
||||
Please have a look in the [dedicated documentation](./docs/pull_request.md) about pull request.
|
||||
|
|
@ -123,13 +132,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 +146,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
|
||||
|
|
@ -184,7 +193,7 @@ internal fun PinIconPreview() = ElementPreview {
|
|||
}
|
||||
```
|
||||
|
||||
This will allow to preview the composable in both light and dark mode in Android Studio. This will also automatically add UI tests. The GitHub action [Record screenshots](https://github.com/element-hq/element-x-android/actions/workflows/recordScreenshots.yml) has to be run to record the new screenshots. The PR reviewer can trigger this for you if you're not part of the core team.
|
||||
This will allow to preview the composable in both light and dark mode in Android Studio. This will also automatically add UI tests. The GitHub action [Record screenshots](https://github.com/element-hq/element-x-android/actions/workflows/recordScreenshots.yml) has to be run to record the new screenshots. The PR reviewer can trigger this for you if you're not part of the core team.
|
||||
|
||||
### Authors
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,5 @@
|
|||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
plugins {
|
||||
alias(libs.plugins.kotlin.jvm)
|
||||
id("com.android.lint")
|
||||
id("io.element.jvm-library")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,10 +21,8 @@ import extension.allFeaturesImpl
|
|||
import extension.allLibrariesImpl
|
||||
import extension.allServicesImpl
|
||||
import extension.buildConfigFieldStr
|
||||
import extension.koverDependencies
|
||||
import extension.locales
|
||||
import extension.setupDependencyInjection
|
||||
import extension.setupKover
|
||||
import extension.testCommonDependencies
|
||||
import java.util.Locale
|
||||
|
||||
|
|
@ -33,7 +31,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)
|
||||
|
|
@ -41,8 +38,6 @@ plugins {
|
|||
// alias(libs.plugins.gms.google.services)
|
||||
}
|
||||
|
||||
setupKover()
|
||||
|
||||
android {
|
||||
namespace = "io.element.android.x"
|
||||
|
||||
|
|
@ -250,26 +245,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 {
|
||||
|
|
@ -316,8 +291,6 @@ dependencies {
|
|||
testCommonDependencies(libs)
|
||||
testImplementation(projects.libraries.matrix.test)
|
||||
testImplementation(projects.services.toolbox.test)
|
||||
|
||||
koverDependencies()
|
||||
}
|
||||
|
||||
tasks.withType<GenerateBuildConfig>().configureEach {
|
||||
|
|
@ -334,6 +307,7 @@ licensee {
|
|||
allow("BSD-2-Clause")
|
||||
allow("BSD-3-Clause")
|
||||
allow("EPL-1.0")
|
||||
allowUrl("https://opensource.org/license/bsd-3-clause")
|
||||
allowUrl("https://opensource.org/licenses/MIT")
|
||||
allowUrl("https://developer.android.com/studio/terms.html")
|
||||
allowUrl("https://www.zetetic.net/sqlcipher/license/")
|
||||
|
|
|
|||
|
|
@ -74,3 +74,6 @@
|
|||
|
||||
# Keep Metro classes
|
||||
-keep,allowoptimization,allowshrinking class dev.zacsweers.metro.** { *; }
|
||||
|
||||
# Rustls Platform Verifier
|
||||
-keep, includedescriptorclasses class org.rustls.platformverifier.** { *; }
|
||||
|
|
|
|||
|
|
@ -118,20 +118,6 @@
|
|||
<data android:host="mobile.element.io" />
|
||||
<data android:path="/element/" />
|
||||
</intent-filter>
|
||||
<!--
|
||||
matrix.to links
|
||||
Note: On Android 12 and higher clicking a web link (that is not an Android App Link) always shows content in a web browser
|
||||
https://developer.android.com/training/app-links#web-links
|
||||
-->
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<data android:scheme="https" />
|
||||
<data android:host="matrix.to" />
|
||||
</intent-filter>
|
||||
<!--
|
||||
matrix: links
|
||||
-->
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@
|
|||
package io.element.android.x
|
||||
|
||||
import android.app.Application
|
||||
import androidx.compose.material3.ComposeMaterial3Flags.isAnchoredDraggableComponentsStrictOffsetCheckEnabled
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.startup.AppInitializer
|
||||
import androidx.work.Configuration
|
||||
import dev.zacsweers.metro.createGraphFactory
|
||||
|
|
@ -27,6 +29,7 @@ class ElementXApplication : Application(), DependencyInjectionGraphOwner, Config
|
|||
.setWorkerFactory(MetroWorkerFactory(graph.workerProviders))
|
||||
.build()
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
AppInitializer.getInstance(this).apply {
|
||||
|
|
@ -36,5 +39,9 @@ class ElementXApplication : Application(), DependencyInjectionGraphOwner, Config
|
|||
}
|
||||
|
||||
logApplicationInfo(this)
|
||||
|
||||
// Disable the strict offset check for anchored draggable components, as it can cause issues with bottom sheets.
|
||||
// Remove once https://issuetracker.google.com/issues/477038695 is fixed.
|
||||
isAnchoredDraggableComponentsStrictOffsetCheckEnabled = false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ dependencies {
|
|||
|
||||
testCommonDependencies(libs)
|
||||
testImplementation(projects.features.login.test)
|
||||
testImplementation(projects.features.share.test)
|
||||
testImplementation(projects.libraries.matrix.test)
|
||||
testImplementation(projects.libraries.oidc.test)
|
||||
testImplementation(projects.libraries.preferences.test)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
package io.element.android.appnav
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Parcelable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.runtime.Composable
|
||||
|
|
@ -63,6 +62,7 @@ import io.element.android.features.roomdirectory.api.RoomDescription
|
|||
import io.element.android.features.roomdirectory.api.RoomDirectoryEntryPoint
|
||||
import io.element.android.features.securebackup.api.SecureBackupEntryPoint
|
||||
import io.element.android.features.share.api.ShareEntryPoint
|
||||
import io.element.android.features.share.api.ShareIntentData
|
||||
import io.element.android.features.startchat.api.StartChatEntryPoint
|
||||
import io.element.android.features.userprofile.api.UserProfileEntryPoint
|
||||
import io.element.android.features.verifysession.api.IncomingVerificationEntryPoint
|
||||
|
|
@ -307,7 +307,7 @@ class LoggedInFlowNode(
|
|||
data object RoomDirectory : NavTarget
|
||||
|
||||
@Parcelize
|
||||
data class IncomingShare(val intent: Intent) : NavTarget
|
||||
data class IncomingShare(val shareIntentData: ShareIntentData) : NavTarget
|
||||
|
||||
@Parcelize
|
||||
data class IncomingVerificationRequest(val data: VerificationRequest.Incoming) : NavTarget
|
||||
|
|
@ -570,7 +570,7 @@ class LoggedInFlowNode(
|
|||
shareEntryPoint.createNode(
|
||||
parentNode = this,
|
||||
buildContext = buildContext,
|
||||
params = ShareEntryPoint.Params(intent = navTarget.intent),
|
||||
params = ShareEntryPoint.Params(shareIntentData = navTarget.shareIntentData),
|
||||
callback = object : ShareEntryPoint.Callback {
|
||||
override fun onDone(roomIds: List<RoomId>) {
|
||||
// Remove the incoming share screen
|
||||
|
|
@ -649,13 +649,13 @@ class LoggedInFlowNode(
|
|||
}
|
||||
}
|
||||
|
||||
internal suspend fun attachIncomingShare(intent: Intent) {
|
||||
internal suspend fun attachIncomingShare(shareIntentData: ShareIntentData) {
|
||||
waitForNavTargetAttached { navTarget ->
|
||||
navTarget is NavTarget.Home
|
||||
}
|
||||
attachChild<Node> {
|
||||
backstack.push(
|
||||
NavTarget.IncomingShare(intent)
|
||||
NavTarget.IncomingShare(shareIntentData)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ import io.element.android.features.announcement.api.AnnouncementService
|
|||
import io.element.android.features.login.api.LoginParams
|
||||
import io.element.android.features.login.api.accesscontrol.AccountProviderAccessControl
|
||||
import io.element.android.features.rageshake.api.bugreport.BugReportEntryPoint
|
||||
import io.element.android.features.share.api.ShareIntentData
|
||||
import io.element.android.features.signedout.api.SignedOutEntryPoint
|
||||
import io.element.android.libraries.accountselect.api.AccountSelectEntryPoint
|
||||
import io.element.android.libraries.architecture.BackstackView
|
||||
|
|
@ -265,7 +266,7 @@ class RootFlowNode(
|
|||
|
||||
@Parcelize data class AccountSelect(
|
||||
val currentSessionId: SessionId,
|
||||
val intent: Intent?,
|
||||
val shareIntentData: ShareIntentData?,
|
||||
val permalinkData: PermalinkData?,
|
||||
) : NavTarget
|
||||
|
||||
|
|
@ -357,8 +358,8 @@ class RootFlowNode(
|
|||
backstack.pop()
|
||||
}
|
||||
attachSession(sessionId).apply {
|
||||
if (navTarget.intent != null) {
|
||||
attachIncomingShare(navTarget.intent)
|
||||
if (navTarget.shareIntentData != null) {
|
||||
attachIncomingShare(navTarget.shareIntentData)
|
||||
} else if (navTarget.permalinkData != null) {
|
||||
attachPermalinkData(navTarget.permalinkData)
|
||||
}
|
||||
|
|
@ -392,7 +393,7 @@ class RootFlowNode(
|
|||
is ResolvedIntent.Login -> onLoginLink(resolvedIntent.params)
|
||||
is ResolvedIntent.Oidc -> onOidcAction(resolvedIntent.oidcAction)
|
||||
is ResolvedIntent.Permalink -> navigateTo(resolvedIntent.permalinkData)
|
||||
is ResolvedIntent.IncomingShare -> onIncomingShare(resolvedIntent.intent)
|
||||
is ResolvedIntent.IncomingShare -> onIncomingShare(resolvedIntent.shareIntentData)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -423,7 +424,7 @@ class RootFlowNode(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun onIncomingShare(intent: Intent) {
|
||||
private suspend fun onIncomingShare(shareIntentData: ShareIntentData) {
|
||||
// Is there a session already?
|
||||
val latestSessionId = sessionStore.getLatestSessionId()
|
||||
if (latestSessionId == null) {
|
||||
|
|
@ -437,13 +438,13 @@ class RootFlowNode(
|
|||
backstack.push(
|
||||
NavTarget.AccountSelect(
|
||||
currentSessionId = latestSessionId,
|
||||
intent = intent,
|
||||
shareIntentData = shareIntentData,
|
||||
permalinkData = null,
|
||||
)
|
||||
)
|
||||
} else {
|
||||
// Only one account, directly attach the incoming share node.
|
||||
loggedInFlowNode.attachIncomingShare(intent)
|
||||
loggedInFlowNode.attachIncomingShare(shareIntentData)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -467,7 +468,7 @@ class RootFlowNode(
|
|||
backstack.push(
|
||||
NavTarget.AccountSelect(
|
||||
currentSessionId = latestSessionId,
|
||||
intent = null,
|
||||
shareIntentData = null,
|
||||
permalinkData = permalinkData,
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ import android.content.Intent
|
|||
import dev.zacsweers.metro.Inject
|
||||
import io.element.android.features.login.api.LoginIntentResolver
|
||||
import io.element.android.features.login.api.LoginParams
|
||||
import io.element.android.features.share.api.ShareIntentData
|
||||
import io.element.android.features.share.api.ShareIntentHandler
|
||||
import io.element.android.libraries.deeplink.api.DeeplinkData
|
||||
import io.element.android.libraries.deeplink.api.DeeplinkParser
|
||||
import io.element.android.libraries.matrix.api.permalink.PermalinkData
|
||||
|
|
@ -25,7 +27,7 @@ sealed interface ResolvedIntent {
|
|||
data class Oidc(val oidcAction: OidcAction) : ResolvedIntent
|
||||
data class Permalink(val permalinkData: PermalinkData) : ResolvedIntent
|
||||
data class Login(val params: LoginParams) : ResolvedIntent
|
||||
data class IncomingShare(val intent: Intent) : ResolvedIntent
|
||||
data class IncomingShare(val shareIntentData: ShareIntentData) : ResolvedIntent
|
||||
}
|
||||
|
||||
@Inject
|
||||
|
|
@ -34,6 +36,7 @@ class IntentResolver(
|
|||
private val loginIntentResolver: LoginIntentResolver,
|
||||
private val oidcIntentResolver: OidcIntentResolver,
|
||||
private val permalinkParser: PermalinkParser,
|
||||
private val shareIntentHandler: ShareIntentHandler,
|
||||
) {
|
||||
fun resolve(intent: Intent): ResolvedIntent? {
|
||||
if (intent.canBeIgnored()) return null
|
||||
|
|
@ -62,7 +65,8 @@ class IntentResolver(
|
|||
if (permalinkData != null) return ResolvedIntent.Permalink(permalinkData)
|
||||
|
||||
if (intent.action == Intent.ACTION_SEND || intent.action == Intent.ACTION_SEND_MULTIPLE) {
|
||||
return ResolvedIntent.IncomingShare(intent)
|
||||
val data = shareIntentHandler.handleIncomingShareIntent(intent) ?: return null
|
||||
return ResolvedIntent.IncomingShare(data)
|
||||
}
|
||||
|
||||
// Unknown intent
|
||||
|
|
|
|||
|
|
@ -167,6 +167,6 @@ class LoggedInPresenter(
|
|||
|
||||
private fun CoroutineScope.preloadAccountManagementUrl() = launch {
|
||||
matrixClient.getAccountManagementUrl(AccountManagementAction.Profile)
|
||||
matrixClient.getAccountManagementUrl(AccountManagementAction.SessionsList)
|
||||
matrixClient.getAccountManagementUrl(AccountManagementAction.DevicesList)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,9 @@ import androidx.core.net.toUri
|
|||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.login.api.LoginParams
|
||||
import io.element.android.features.login.test.FakeLoginIntentResolver
|
||||
import io.element.android.features.share.api.ShareIntentData
|
||||
import io.element.android.features.share.api.UriToShare
|
||||
import io.element.android.features.share.test.FakeShareIntentHandler
|
||||
import io.element.android.libraries.deeplink.api.DeeplinkData
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.permalink.PermalinkData
|
||||
|
|
@ -239,26 +242,34 @@ class IntentResolverTest {
|
|||
|
||||
@Test
|
||||
fun `test incoming share simple`() {
|
||||
val shareIntentData = ShareIntentData.PlainText("Hello")
|
||||
val sut = createIntentResolver(
|
||||
oidcIntentResolverResult = { null },
|
||||
onIncomingShareIntent = { shareIntentData },
|
||||
)
|
||||
val intent = Intent(RuntimeEnvironment.getApplication(), Activity::class.java).apply {
|
||||
action = Intent.ACTION_SEND
|
||||
putExtra(Intent.EXTRA_TEXT, "Hello")
|
||||
}
|
||||
val result = sut.resolve(intent)
|
||||
assertThat(result).isEqualTo(ResolvedIntent.IncomingShare(intent = intent))
|
||||
assertThat(result).isEqualTo(ResolvedIntent.IncomingShare(shareIntentData))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test incoming share multiple`() {
|
||||
val fileUri = "content://com.example.app/file1.jpg".toUri()
|
||||
val shareIntentData = ShareIntentData.Uris(text = "Hello", uris = listOf(UriToShare(fileUri, "image/jpg")))
|
||||
val sut = createIntentResolver(
|
||||
oidcIntentResolverResult = { null },
|
||||
onIncomingShareIntent = { shareIntentData },
|
||||
)
|
||||
val intent = Intent(RuntimeEnvironment.getApplication(), Activity::class.java).apply {
|
||||
action = Intent.ACTION_SEND_MULTIPLE
|
||||
putExtra(Intent.EXTRA_TEXT, "Hello")
|
||||
data = fileUri
|
||||
}
|
||||
val result = sut.resolve(intent)
|
||||
assertThat(result).isEqualTo(ResolvedIntent.IncomingShare(intent = intent))
|
||||
assertThat(result).isEqualTo(ResolvedIntent.IncomingShare(shareIntentData))
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -296,6 +307,7 @@ class IntentResolverTest {
|
|||
permalinkParserResult: (String) -> PermalinkData = { lambdaError() },
|
||||
loginIntentResolverResult: (String) -> LoginParams? = { lambdaError() },
|
||||
oidcIntentResolverResult: (Intent) -> OidcAction? = { lambdaError() },
|
||||
onIncomingShareIntent: (Intent) -> ShareIntentData? = { null },
|
||||
): IntentResolver {
|
||||
return IntentResolver(
|
||||
deeplinkParser = { deeplinkParserResult },
|
||||
|
|
@ -308,6 +320,9 @@ class IntentResolverTest {
|
|||
permalinkParser = FakePermalinkParser(
|
||||
result = permalinkParserResult
|
||||
),
|
||||
shareIntentHandler = FakeShareIntentHandler(
|
||||
onIncomingShareIntent = onIncomingShareIntent,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ class LoggedInPresenterTest {
|
|||
accountManagementUrlResult.assertions().isCalledExactly(2)
|
||||
.withSequence(
|
||||
listOf(value(AccountManagementAction.Profile)),
|
||||
listOf(value(AccountManagementAction.SessionsList)),
|
||||
listOf(value(AccountManagementAction.DevicesList)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,6 +76,9 @@ allprojects {
|
|||
filter {
|
||||
exclude { element -> element.file.path.contains(generatedPath) }
|
||||
exclude("io/element/android/tests/konsist/failures/**")
|
||||
|
||||
// This file comes from another project and we want to keep it as close to the original as possible
|
||||
exclude("**/SafeChildrenTransitionScope.kt")
|
||||
}
|
||||
}
|
||||
// Dependency check
|
||||
|
|
@ -175,12 +178,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")
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
* [Rust SDK](#rust-sdk)
|
||||
* [Matrix Rust Component Kotlin](#matrix-rust-component-kotlin)
|
||||
* [Building the SDK locally](#building-the-sdk-locally)
|
||||
* [rustls and platform verifier](#rustls-and-platform-verifier)
|
||||
* [The Android project](#the-android-project)
|
||||
* [Application](#application)
|
||||
* [Jetpack Compose](#jetpack-compose)
|
||||
|
|
@ -144,8 +145,8 @@ Prerequisites:
|
|||
```
|
||||
|
||||
You can then build the Rust SDK by running the script
|
||||
[`tools/sdk/build_rust_sdk.sh`](../tools/sdk/build_rust_sdk.sh) and just answering
|
||||
the questions.
|
||||
[`tools/sdk/build-rust-sdk`](../tools/sdk/build-rust-sdk). Type
|
||||
`./tools/sdk/build-rust-sdk --help` for help.
|
||||
|
||||
This will prompt you for the path to the Rust SDK, then build it and
|
||||
`matrix-rust-components-kotlin`, eventually producing an aar file at
|
||||
|
|
@ -160,6 +161,19 @@ Troubleshooting:
|
|||
|
||||
You can switch back to using the published version of the SDK by deleting `libraries/rustsdk/matrix-rust-sdk.aar`.
|
||||
|
||||
#### rustls and platform verifier
|
||||
|
||||
The SDK uses [rustls](https://github.com/rustls/rustls) for TLS, which is a pure Rust implementation of TLS. In turn, this means we have to add the
|
||||
`rustls-platform-verifier` library to our project, which provides platform-specific TLS certificate verification for rustls. This library uses the Android NDK's
|
||||
`TrustManager` to verify TLS certificates on Android.
|
||||
|
||||
Though it's meant to be used through convoluted way of downloading the dependency, locating it in the
|
||||
cargo folder and using that path as a local maven repo as described [here](https://github.com/rustls/rustls-platform-verifier#android), we have
|
||||
added a script (`tools/sdk/update-rustls`) to download, unpack and add this AAR file locally to the `:libraries:matrix:impl` module instead.
|
||||
|
||||
When should we run this script? Whenever we update the `rustls` dependency in the Rust SDK, we should check if the version of `rustls-platform-verifier`
|
||||
has changed as well, and if so, run this script to update the AAR file in our project. The SDK team should ping us when this happens.
|
||||
|
||||
### The Android project
|
||||
|
||||
The project should compile out of the box.
|
||||
|
|
|
|||
|
|
@ -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/202603020.txt
Normal file
2
fastlane/metadata/android/en-US/changelogs/202603020.txt
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
Main changes in this version: fixed an issue that could cause a crash when instantiating the cryptographic database.
|
||||
Full changelog: https://github.com/element-hq/element-x-android/releases
|
||||
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
|
||||
2
fastlane/metadata/android/en-US/changelogs/202603040.txt
Normal file
2
fastlane/metadata/android/en-US/changelogs/202603040.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
|
||||
|
|
@ -3,5 +3,5 @@
|
|||
<string name="screen_analytics_settings_help_us_improve">"Предоставьте разработчикам анонимные данные об использовании, чтобы помочь им выявлять проблемы эффективнее."</string>
|
||||
<string name="screen_analytics_settings_read_terms">"Вы можете ознакомиться со всеми нашими условиями %1$s."</string>
|
||||
<string name="screen_analytics_settings_read_terms_content_link">"здесь"</string>
|
||||
<string name="screen_analytics_settings_share_data">"Отправлять аналитические данные"</string>
|
||||
<string name="screen_analytics_settings_share_data">"Отправлять аналитику"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -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_analytics_settings_help_us_improve">"Muammolarni aniqlashda yordam berish uchun anonim foydalanish maʼlumotlarini baham koʻring."</string>
|
||||
<string name="screen_analytics_settings_read_terms">"Siz bizning barcha shartlarimizni o\'qishingiz mumkin%1$s."</string>
|
||||
<string name="screen_analytics_settings_read_terms_content_link">"Bu yerga"</string>
|
||||
<string name="screen_analytics_settings_read_terms">"Barcha shartlar bilan %1$s tanishib chiqishingiz mumkin."</string>
|
||||
<string name="screen_analytics_settings_read_terms_content_link">"bu yerda"</string>
|
||||
<string name="screen_analytics_settings_share_data">"Analitik ma\'lumotlarni ulashish"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_analytics_prompt_data_usage">"Biz hech qanday shaxsiy ma\'lumotlarni yozmaymiz yoki profilga kiritmaymiz"</string>
|
||||
<string name="screen_analytics_prompt_help_us_improve">"Muammolarni aniqlashda yordam berish uchun anonim foydalanish maʼlumotlarini baham koʻring."</string>
|
||||
<string name="screen_analytics_prompt_read_terms">"Siz bizning barcha shartlarimizni o\'qishingiz mumkin%1$s."</string>
|
||||
<string name="screen_analytics_prompt_read_terms_content_link">"Bu yerga"</string>
|
||||
<string name="screen_analytics_prompt_read_terms">"Barcha shartlar bilan %1$s tanishib chiqishingiz mumkin."</string>
|
||||
<string name="screen_analytics_prompt_read_terms_content_link">"bu yerda"</string>
|
||||
<string name="screen_analytics_prompt_settings">"Buni istalgan vaqtda oʻchirib qoʻyishingiz mumkin"</string>
|
||||
<string name="screen_analytics_prompt_third_party_sharing">"Biz sizning ma\'lumotlaringizni uchinchi tomonlar bilan baham ko\'rmaymiz"</string>
|
||||
<string name="screen_analytics_prompt_title">"Yaxshilashga yordam bering%1$s"</string>
|
||||
<string name="screen_analytics_prompt_title">"%1$sʼni yaxshilashga yordam bering"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_space_announcement_item1">"Se grupper, du har oprettet eller tilmeldt dig"</string>
|
||||
<string name="screen_space_announcement_item2">"Acceptere eller afvise invitationer til grupper"</string>
|
||||
<string name="screen_space_announcement_item3">"Finde alle rum, du kan deltage i, i dine grupper"</string>
|
||||
<string name="screen_space_announcement_item4">"Deltage i offentlige grupper"</string>
|
||||
<string name="screen_space_announcement_item5">"Forlade de grupper, du har tilsluttet dig"</string>
|
||||
<string name="screen_space_announcement_notice">"Filtrering, oprettelse og administration af grupper kommer snart."</string>
|
||||
<string name="screen_space_announcement_subtitle">"Velkommen til betaversionen af Grupper! Med denne første version kan du:"</string>
|
||||
<string name="screen_space_announcement_title">"Introduktion til Grupper"</string>
|
||||
<string name="screen_space_announcement_item1">"Se klynger, du har oprettet eller tilmeldt dig"</string>
|
||||
<string name="screen_space_announcement_item2">"Acceptere eller afvise invitationer til klynger"</string>
|
||||
<string name="screen_space_announcement_item3">"Finde alle rum, du kan deltage i, i dine klynger"</string>
|
||||
<string name="screen_space_announcement_item4">"Deltage i offentlige klynger"</string>
|
||||
<string name="screen_space_announcement_item5">"Forlade de klynger, du har tilsluttet dig"</string>
|
||||
<string name="screen_space_announcement_notice">"Filtrering, oprettelse og administration af klynger kommer snart."</string>
|
||||
<string name="screen_space_announcement_subtitle">"Velkommen til betaversionen af Klynger! Med denne første version kan du:"</string>
|
||||
<string name="screen_space_announcement_title">"Introduktion til Klynger"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_space_announcement_item1">"직접 만들거나 참여 중인 스페이스 보기"</string>
|
||||
<string name="screen_space_announcement_item2">"스페이스 초대 수락 또는 거절"</string>
|
||||
<string name="screen_space_announcement_item3">"참여 가능한 스페이스 내 모든 방 탐색"</string>
|
||||
<string name="screen_space_announcement_item4">"공개 스페이스 참여"</string>
|
||||
<string name="screen_space_announcement_item5">"참여 중인 스페이스 나가기"</string>
|
||||
<string name="screen_space_announcement_notice">"스페이스 필터링, 생성 및 관리 기능이 곧 추가될 예정입니다."</string>
|
||||
<string name="screen_space_announcement_subtitle">"스페이스 베타 버전에 오신 것을 환영합니다! 이번 첫 번째 버전에서는 다음과 같은 기능을 이용하실 수 있습니다.:"</string>
|
||||
<string name="screen_space_announcement_title">"스페이스 소개"</string>
|
||||
</resources>
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_space_announcement_item1">"Просмотреть пространства, которые вы создали или к которым присоединились"</string>
|
||||
<string name="screen_space_announcement_item1">"Просматривать пространства, которые вы создали или к которым присоединились"</string>
|
||||
<string name="screen_space_announcement_item2">"Принимать или отклонять приглашения в пространства"</string>
|
||||
<string name="screen_space_announcement_item3">"Найти все комнаты, к которым можно присоединиться в ваших пространствах"</string>
|
||||
<string name="screen_space_announcement_item3">"Находить все комнаты, к которым можно присоединиться в ваших пространствах"</string>
|
||||
<string name="screen_space_announcement_item4">"Присоединяться к публичным пространствам"</string>
|
||||
<string name="screen_space_announcement_item5">"Покинуть все пространства, к которым вы присоединились"</string>
|
||||
<string name="screen_space_announcement_notice">"Работа с пространствами скоро станет доступна"</string>
|
||||
<string name="screen_space_announcement_subtitle">"Добро пожаловать в бета-версию пространств! В этой первой версии вы сможете:"</string>
|
||||
<string name="screen_space_announcement_item5">"Покидать все пространства, к которым вы присоединились"</string>
|
||||
<string name="screen_space_announcement_notice">"Фильтровать, создавать пространства и управлять ими можно будет позже."</string>
|
||||
<string name="screen_space_announcement_subtitle">"Добро пожаловать в бета-версию пространств! Сейчас вы сможете:"</string>
|
||||
<string name="screen_space_announcement_title">"Представляем пространства"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_space_announcement_item3">"Знаходьте у своїх просторах кімнати, до яких можна приєднатися"</string>
|
||||
<string name="screen_space_announcement_notice">"Фільтрування, створення та керування просторами стане доступним найближчим часом."</string>
|
||||
<string name="screen_space_announcement_subtitle">"Ласкаво просимо до бета-версії Просторів! У цій першій версії ви можете:"</string>
|
||||
<string name="screen_space_announcement_title">"Представляємо Простори"</string>
|
||||
</resources>
|
||||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="call_foreground_service_channel_title_android">"Текущ разговор"</string>
|
||||
<string name="call_foreground_service_message_android">"Докоснете, за да се върнете към разговора"</string>
|
||||
<string name="call_foreground_service_title_android">"☎️ Разговор в ход"</string>
|
||||
</resources>
|
||||
|
|
@ -3,6 +3,6 @@
|
|||
<string name="call_foreground_service_channel_title_android">"Текущий вызов"</string>
|
||||
<string name="call_foreground_service_message_android">"Коснитесь, чтобы вернуться к вызову"</string>
|
||||
<string name="call_foreground_service_title_android">"☎️ Идёт вызов"</string>
|
||||
<string name="call_invalid_audio_device_bluetooth_devices_disabled">"Функция Element Call не поддерживает использование аудиоустройств Bluetooth в данной версии Android. Пожалуйста, выберите другое аудиоустройство."</string>
|
||||
<string name="screen_incoming_call_subtitle_android">"Входящий вызов Element"</string>
|
||||
<string name="call_invalid_audio_device_bluetooth_devices_disabled">"Element Call не поддерживает использование устройств Bluetooth в данной версии Android. Пожалуйста, выберите другое устройство."</string>
|
||||
<string name="screen_incoming_call_subtitle_android">"Входящий вызов Element Call"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -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,10 +3,10 @@
|
|||
<string name="screen_create_room_action_create_room">"Nyt rum"</string>
|
||||
<string name="screen_create_room_add_people_title">"Invitér andre"</string>
|
||||
<string name="screen_create_room_error_creating_room">"Der opstod en fejl ved oprettelsen af rummet"</string>
|
||||
<string name="screen_create_room_error_creating_space">"Gruppen kunne ikke oprettes på grund af en ukendt fejl. Prøv igen senere."</string>
|
||||
<string name="screen_create_room_error_creating_space">"Klyngen kunne ikke oprettes på grund af en ukendt fejl. Prøv igen senere."</string>
|
||||
<string name="screen_create_room_name_placeholder">"Tilføj navn…"</string>
|
||||
<string name="screen_create_room_new_room_title">"Nyt rum"</string>
|
||||
<string name="screen_create_room_new_space_title">"Ny gruppe"</string>
|
||||
<string name="screen_create_room_new_space_title">"Ny klynge"</string>
|
||||
<string name="screen_create_room_private_option_description">"Kun inviterede personer kan deltage."</string>
|
||||
<string name="screen_create_room_private_option_title">"Privat"</string>
|
||||
<string name="screen_create_room_public_option_description">"Alle kan finde dette rum.
|
||||
|
|
@ -27,7 +27,10 @@ Du kan ændre dette når som helst i rummets indstillinger."</string>
|
|||
<string name="screen_create_room_room_address_section_footer">"Hvis dette rum skal være synligt i det offentlige register, skal du bruge en adresse."</string>
|
||||
<string name="screen_create_room_room_address_section_title">"Adresse"</string>
|
||||
<string name="screen_create_room_room_visibility_section_title">"Rummets synlighed"</string>
|
||||
<string name="screen_create_room_space_selection_no_space_description">"(ingen klynge)"</string>
|
||||
<string name="screen_create_room_space_selection_no_space_option">"Tilføj ikke til et mellemrum"</string>
|
||||
<string name="screen_create_room_space_selection_no_space_title">"Ingen klynge valgt"</string>
|
||||
<string name="screen_create_room_space_selection_sheet_title">"Tilføj til klynge"</string>
|
||||
<string name="screen_create_room_topic_label">"Emne (valgfrit)"</string>
|
||||
<string name="screen_create_room_topic_placeholder">"Tilføj beskrivelse…"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
<string name="screen_create_room_action_create_room">"Nova soba"</string>
|
||||
<string name="screen_create_room_add_people_title">"Pozovi osobe"</string>
|
||||
<string name="screen_create_room_error_creating_room">"Došlo je do pogreške prilikom stvaranja sobe"</string>
|
||||
<string name="screen_create_room_new_room_title">"Nova soba"</string>
|
||||
<string name="screen_create_room_private_option_description">"Samo pozvane osobe mogu pristupiti ovoj sobi. Sve su poruke sveobuhvatno šifrirane."</string>
|
||||
<string name="screen_create_room_public_option_description">"Svatko može pronaći ovu sobu.
|
||||
To možete u svakom trenutku promijeniti u postavkama sobe."</string>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
<string name="screen_create_room_private_option_description">"Hanya orang-orang yang diundang dapat mengakses ruangan ini. Semua pesan terenkripsi secara ujung ke ujung."</string>
|
||||
<string name="screen_create_room_public_option_description">"Siapa pun dapat mencari ruangan ini.
|
||||
Anda dapat mengubah ini kapan pun dalam pengaturan ruangan."</string>
|
||||
<string name="screen_create_room_room_access_section_knocking_option_description">"Siapa pun dapat meminta untuk bergabung dengan ruangan tetapi administrator atau moderator harus menerima permintaan tersebut"</string>
|
||||
<string name="screen_create_room_room_access_section_knocking_option_description">"Siapa pun dapat meminta untuk bergabung dengan ijin administrator atau moderator"</string>
|
||||
<string name="screen_create_room_room_access_section_knocking_option_title">"Minta untuk bergabung"</string>
|
||||
<string name="screen_create_room_room_access_section_public_option_description">"Siapa pun dapat bergabung dengan ruangan ini"</string>
|
||||
<string name="screen_create_room_room_address_section_footer">"Supaya ruangan ini terlihat di direktori ruangan publik, Anda memerlukan alamat ruangan."</string>
|
||||
|
|
|
|||
|
|
@ -3,13 +3,34 @@
|
|||
<string name="screen_create_room_action_create_room">"새 방"</string>
|
||||
<string name="screen_create_room_add_people_title">"사람 초대하기"</string>
|
||||
<string name="screen_create_room_error_creating_room">"방을 생성하던 중 오류가 발생했어요"</string>
|
||||
<string name="screen_create_room_private_option_description">"초대받은 사람만 이 방에 액세스할 수 있습니다. 모든 메시지는 종단 간 암호화됩니다."</string>
|
||||
<string name="screen_create_room_error_creating_space">"알 수 없는 오류로 인해 스페이스를 생성할 수 없습니다. 나중에 다시 시도해 주세요."</string>
|
||||
<string name="screen_create_room_name_placeholder">"이름 추가…"</string>
|
||||
<string name="screen_create_room_new_room_title">"새 방"</string>
|
||||
<string name="screen_create_room_new_space_title">"새 스페이스"</string>
|
||||
<string name="screen_create_room_private_option_description">"초대된 사람만 참여할 수 있습니다."</string>
|
||||
<string name="screen_create_room_private_option_title">"비공개"</string>
|
||||
<string name="screen_create_room_public_option_description">"누구나 이 방을 찾을 수 있습니다.
|
||||
방 설정에서 언제든지 변경할 수 있습니다."</string>
|
||||
<string name="screen_create_room_room_access_section_knocking_option_description">"누구나 방에 참여 요청을 할 수 있지만, 관리자나 운영자가 요청을 수락해야 합니다."</string>
|
||||
<string name="screen_create_room_room_access_section_knocking_option_title">"참가 요청"</string>
|
||||
<string name="screen_create_room_room_access_section_public_option_description">"누구나 이 방에 참여할 수 있습니다."</string>
|
||||
<string name="screen_create_room_room_address_section_footer">"이 방이 공개 방 디렉토리에 표시되려면 방 주소가 필요합니다."</string>
|
||||
<string name="screen_create_room_public_option_short_description">"누구나 참여할 수 있습니다."</string>
|
||||
<string name="screen_create_room_public_option_title">"공개"</string>
|
||||
<string name="screen_create_room_room_access_section_knocking_option_description">"누구나 참여를 요청할 수 있지만, 관리자나 중재자가 요청을 승인해야 합니다."</string>
|
||||
<string name="screen_create_room_room_access_section_knocking_option_title">"참여 요청 허용"</string>
|
||||
<string name="screen_create_room_room_access_section_knocking_restricted_option_description">"%1$s의 멤버는 누구나 참여할 수 있지만, 그 외의 사람은 액세스 요청을 해야 합니다."</string>
|
||||
<string name="screen_create_room_room_access_section_knocking_restricted_option_title">"참여 요청하기"</string>
|
||||
<string name="screen_create_room_room_access_section_private_option_description">"초대받은 사람만 참여할 수 있습니다."</string>
|
||||
<string name="screen_create_room_room_access_section_private_option_title">"비공개"</string>
|
||||
<string name="screen_create_room_room_access_section_public_option_description">"누구나 참여할 수 있습니다."</string>
|
||||
<string name="screen_create_room_room_access_section_public_option_title">"공개"</string>
|
||||
<string name="screen_create_room_room_access_section_restricted_option_description">"%1$s의 멤버는 누구나 참여할 수 있습니다."</string>
|
||||
<string name="screen_create_room_room_access_section_restricted_option_title">"표준"</string>
|
||||
<string name="screen_create_room_room_access_section_title">"액세스 권한이 있는 사용자"</string>
|
||||
<string name="screen_create_room_room_address_section_footer">"공개 디렉터리에 표시하려면 주소가 필요합니다."</string>
|
||||
<string name="screen_create_room_room_address_section_title">"주소"</string>
|
||||
<string name="screen_create_room_room_visibility_section_title">"방 표시 여부"</string>
|
||||
<string name="screen_create_room_space_selection_no_space_description">"(스페이스 없음)"</string>
|
||||
<string name="screen_create_room_space_selection_no_space_option">"스페이스에 추가하지 않음"</string>
|
||||
<string name="screen_create_room_space_selection_no_space_title">"홈"</string>
|
||||
<string name="screen_create_room_space_selection_sheet_title">"스페이스에 추가"</string>
|
||||
<string name="screen_create_room_topic_label">"주제 (선택)"</string>
|
||||
<string name="screen_create_room_topic_placeholder">"설명 추가…"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -4,27 +4,32 @@
|
|||
<string name="screen_create_room_add_people_title">"Пригласить в комнату"</string>
|
||||
<string name="screen_create_room_error_creating_room">"Произошла ошибка при создании комнаты"</string>
|
||||
<string name="screen_create_room_error_creating_space">"Не удалось создать это пространство из-за неизвестной ошибки. Попробуйте позже."</string>
|
||||
<string name="screen_create_room_name_placeholder">"Добавьте имя…"</string>
|
||||
<string name="screen_create_room_name_placeholder">"Добавить имя…"</string>
|
||||
<string name="screen_create_room_new_room_title">"Новая комната"</string>
|
||||
<string name="screen_create_room_new_space_title">"Новое пространство"</string>
|
||||
<string name="screen_create_room_private_option_description">"Присоединиться могут только приглашенные."</string>
|
||||
<string name="screen_create_room_private_option_title">"Приватный"</string>
|
||||
<string name="screen_create_room_public_option_description">"Любой желающий может найти эту комнату.
|
||||
Вы можете изменить это в любое время в настройках комнаты."</string>
|
||||
<string name="screen_create_room_public_option_short_description">"Присоединиться может любой."</string>
|
||||
<string name="screen_create_room_room_access_section_knocking_option_description">"Любой желающий может подать заявку на присоединение к комнате, но администратор или модератор должен будет принять запрос."</string>
|
||||
<string name="screen_create_room_room_access_section_knocking_option_title">"Разрешить запрос на присоединение"</string>
|
||||
<string name="screen_create_room_room_access_section_knocking_restricted_option_description">"Любой из %1$s может присоединиться, а всем остальным нужно запросить доступ."</string>
|
||||
<string name="screen_create_room_room_access_section_knocking_restricted_option_title">"Подать заявку на вступление"</string>
|
||||
<string name="screen_create_room_room_access_section_private_option_description">"Присоединиться могут только приглашенные."</string>
|
||||
<string name="screen_create_room_room_access_section_public_option_description">"Присоединиться может любой желающий."</string>
|
||||
<string name="screen_create_room_room_access_section_restricted_option_description">"Любой в %1$s может присоединиться."</string>
|
||||
<string name="screen_create_room_public_option_short_description">"Кто угодно может присоединиться."</string>
|
||||
<string name="screen_create_room_public_option_title">"Публичный"</string>
|
||||
<string name="screen_create_room_room_access_section_knocking_option_description">"Кто угодно может подать заявку, но администратор или модератор должен будет принять запрос."</string>
|
||||
<string name="screen_create_room_room_access_section_knocking_option_title">"Разрешить запросы на присоединение"</string>
|
||||
<string name="screen_create_room_room_access_section_knocking_restricted_option_description">"Кто угодно в %1$s может присоединиться, а всем остальным нужно запросить доступ."</string>
|
||||
<string name="screen_create_room_room_access_section_knocking_restricted_option_title">"Запросы на присоединение"</string>
|
||||
<string name="screen_create_room_room_access_section_private_option_description">"Присоединиться можно только по приглашениям."</string>
|
||||
<string name="screen_create_room_room_access_section_private_option_title">"Приватный"</string>
|
||||
<string name="screen_create_room_room_access_section_public_option_description">"Кто угодно может присоедниться."</string>
|
||||
<string name="screen_create_room_room_access_section_public_option_title">"Публичный"</string>
|
||||
<string name="screen_create_room_room_access_section_restricted_option_description">"Кто угодно в %1$s может присоединиться."</string>
|
||||
<string name="screen_create_room_room_access_section_restricted_option_title">"Стандарт"</string>
|
||||
<string name="screen_create_room_room_access_section_title">"Кто имеет доступ"</string>
|
||||
<string name="screen_create_room_room_address_section_footer">"Вам понадобится адрес комнаты, чтобы сделать ее видимой в каталоге."</string>
|
||||
<string name="screen_create_room_room_address_section_footer">"Необходимо задать адрес комнаты, чтобы опубликовать ее в каталог комнат."</string>
|
||||
<string name="screen_create_room_room_address_section_title">"Адрес"</string>
|
||||
<string name="screen_create_room_room_visibility_section_title">"Видимость комнаты"</string>
|
||||
<string name="screen_create_room_space_selection_no_space_description">"(нет пространства)"</string>
|
||||
<string name="screen_create_room_space_selection_no_space_title">"Главная"</string>
|
||||
<string name="screen_create_room_space_selection_no_space_option">"Не добавлять в пространство"</string>
|
||||
<string name="screen_create_room_space_selection_no_space_title">"Пространство не выбрано"</string>
|
||||
<string name="screen_create_room_space_selection_sheet_title">"Добавить в пространство"</string>
|
||||
<string name="screen_create_room_topic_label">"Тема (необязательно)"</string>
|
||||
<string name="screen_create_room_topic_placeholder">"Добавить описание…"</string>
|
||||
|
|
|
|||
|
|
@ -3,14 +3,24 @@
|
|||
<string name="screen_create_room_action_create_room">"Нова кімната"</string>
|
||||
<string name="screen_create_room_add_people_title">"Запросити людей"</string>
|
||||
<string name="screen_create_room_error_creating_room">"Під час створення кімнати сталася помилка"</string>
|
||||
<string name="screen_create_room_private_option_description">"Лише запрошені люди мають доступ до цієї кімнати. Усі повідомлення захищені наскрізним шифруванням."</string>
|
||||
<string name="screen_create_room_name_placeholder">"Додати назву…"</string>
|
||||
<string name="screen_create_room_new_room_title">"Нова кімната"</string>
|
||||
<string name="screen_create_room_new_space_title">"Новий простір"</string>
|
||||
<string name="screen_create_room_private_option_description">"Можуть приєднатися лише запрошені люди."</string>
|
||||
<string name="screen_create_room_public_option_description">"Будь-хто може знайти цю кімнату.
|
||||
Ви можете змінити це в будь-який час у налаштуваннях кімнати."</string>
|
||||
<string name="screen_create_room_room_access_section_knocking_option_description">"Будь-хто може попросити приєднатися до кімнати, але адміністратор або модератор повинен буде прийняти запит"</string>
|
||||
<string name="screen_create_room_room_access_section_knocking_option_title">"Запросити приєднатися"</string>
|
||||
<string name="screen_create_room_room_access_section_public_option_description">"Будь-хто може приєднатися до цієї кімнати"</string>
|
||||
<string name="screen_create_room_room_address_section_footer">"Щоб цю кімнату було видно в каталозі загальнодоступних кімнат, вам знадобиться її адреса."</string>
|
||||
<string name="screen_create_room_room_address_section_title">"Адреса кімнати"</string>
|
||||
<string name="screen_create_room_public_option_short_description">"Приєднатися може будь-хто."</string>
|
||||
<string name="screen_create_room_room_access_section_knocking_option_description">"Будь-хто може подати запит на приєднання, але адміністратор або модератор повинен схвалити запит."</string>
|
||||
<string name="screen_create_room_room_access_section_knocking_option_title">"Дозволити запит на приєднання"</string>
|
||||
<string name="screen_create_room_room_access_section_private_option_description">"Приєднатися можуть лише запрошені особи."</string>
|
||||
<string name="screen_create_room_room_access_section_public_option_description">"Приєднатися може будь-хто."</string>
|
||||
<string name="screen_create_room_room_access_section_restricted_option_description">"Приєднатися може будь-хто з %1$s."</string>
|
||||
<string name="screen_create_room_room_access_section_title">"Хто має доступ"</string>
|
||||
<string name="screen_create_room_room_address_section_footer">"Вам знадобиться адреса, щоб зробити її видимою в загальнодоступному каталозі."</string>
|
||||
<string name="screen_create_room_room_address_section_title">"Адреса"</string>
|
||||
<string name="screen_create_room_room_visibility_section_title">"Видимість кімнати"</string>
|
||||
<string name="screen_create_room_space_selection_no_space_title">"Головна"</string>
|
||||
<string name="screen_create_room_space_selection_sheet_title">"Додати до простору"</string>
|
||||
<string name="screen_create_room_topic_label">"Тема (необов\'язково)"</string>
|
||||
<string name="screen_create_room_topic_placeholder">"Додати опис…"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="screen_deactivate_account_confirmation_dialog_content">"Вы уверены, что хотите отключить свою учётную запись? Данное действие не может быть отменено."</string>
|
||||
<string name="screen_deactivate_account_confirmation_dialog_content">"Вы уверены, что хотите отключить свою учётную запись? Данное действие необратимо."</string>
|
||||
<string name="screen_deactivate_account_delete_all_messages">"Удалить все мои сообщения"</string>
|
||||
<string name="screen_deactivate_account_delete_all_messages_notice">"Предупреждение: будущие пользователи могут увидеть незавершенные разговоры."</string>
|
||||
<string name="screen_deactivate_account_description">"Отключение вашей учетной записи %1$s и означает следующее:"</string>
|
||||
<string name="screen_deactivate_account_delete_all_messages_notice">"Внимание: в будущем пользователи могут видеть неполные переписки."</string>
|
||||
<string name="screen_deactivate_account_description">"Деактивация вашего аккаунта %1$s и означает следующее:"</string>
|
||||
<string name="screen_deactivate_account_description_bold_part">"необратимо"</string>
|
||||
<string name="screen_deactivate_account_list_item_1">"Ваша учётная запись будет %1$s (вы не сможете войти в неё снова, и ваш ID не может быть использован повторно)."</string>
|
||||
<string name="screen_deactivate_account_list_item_1_bold_part">"отключена навсегда"</string>
|
||||
<string name="screen_deactivate_account_list_item_1">"Ваша учётная запись будет %1$s (вы не сможете войти в неё снова, и другие пользователи не смогут использовать ваше имя пользователя)."</string>
|
||||
<string name="screen_deactivate_account_list_item_1_bold_part">"Отключить навсегда"</string>
|
||||
<string name="screen_deactivate_account_list_item_2">"Вы будете удалены из всех чатов."</string>
|
||||
<string name="screen_deactivate_account_list_item_3">"Данные вашей учётной записи будут удалены с нашего сервера идентификации."</string>
|
||||
<string name="screen_deactivate_account_list_item_3">"Данные Вашего аккаунта будут удалены с нашего сервера идентификации."</string>
|
||||
<string name="screen_deactivate_account_list_item_4">"Ваши сообщения по-прежнему будут видны зарегистрированным пользователям, но не будут доступны новым или незарегистрированным пользователям, если вы решите удалить их."</string>
|
||||
<string name="screen_deactivate_account_title">"Отключить учётную запись"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.produceState
|
||||
import androidx.compose.runtime.remember
|
||||
import dev.zacsweers.metro.Inject
|
||||
import io.element.android.features.logout.api.direct.DirectLogoutEvents
|
||||
|
|
@ -30,28 +31,32 @@ class ChooseSelfVerificationModePresenter(
|
|||
@Composable
|
||||
override fun present(): ChooseSelfVerificationModeState {
|
||||
val hasDevicesToVerifyAgainst by encryptionService.hasDevicesToVerifyAgainst.collectAsState()
|
||||
val canEnterRecoveryKey by encryptionService.recoveryStateStateFlow
|
||||
.mapState { recoveryState ->
|
||||
when (recoveryState) {
|
||||
RecoveryState.WAITING_FOR_SYNC,
|
||||
RecoveryState.UNKNOWN -> AsyncData.Loading()
|
||||
RecoveryState.INCOMPLETE -> AsyncData.Success(true)
|
||||
RecoveryState.ENABLED,
|
||||
RecoveryState.DISABLED -> AsyncData.Success(false)
|
||||
val canUseRecoveryKey by produceState<AsyncData<Boolean>>(AsyncData.Uninitialized) {
|
||||
encryptionService.recoveryStateStateFlow
|
||||
.mapState { recoveryState ->
|
||||
when (recoveryState) {
|
||||
RecoveryState.WAITING_FOR_SYNC,
|
||||
RecoveryState.UNKNOWN -> AsyncData.Loading()
|
||||
RecoveryState.INCOMPLETE -> AsyncData.Success(true)
|
||||
RecoveryState.ENABLED,
|
||||
RecoveryState.DISABLED -> AsyncData.Success(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
.collectAsState()
|
||||
.collect {
|
||||
value = it
|
||||
}
|
||||
}
|
||||
val buttonsState by remember {
|
||||
derivedStateOf {
|
||||
val canUseAnotherDevice = hasDevicesToVerifyAgainst.dataOrNull()
|
||||
val canEnterRecoveryKey = canEnterRecoveryKey.dataOrNull()
|
||||
if (canUseAnotherDevice == null || canEnterRecoveryKey == null) {
|
||||
val canUseRecoveryKey = canUseRecoveryKey.dataOrNull()
|
||||
if (canUseAnotherDevice == null || canUseRecoveryKey == null) {
|
||||
AsyncData.Loading()
|
||||
} else {
|
||||
AsyncData.Success(
|
||||
ChooseSelfVerificationModeState.ButtonsState(
|
||||
canUseAnotherDevice = canUseAnotherDevice,
|
||||
canEnterRecoveryKey = canEnterRecoveryKey,
|
||||
canUseRecoveryKey = canUseRecoveryKey,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,6 @@ data class ChooseSelfVerificationModeState(
|
|||
) {
|
||||
data class ButtonsState(
|
||||
val canUseAnotherDevice: Boolean,
|
||||
val canEnterRecoveryKey: Boolean,
|
||||
val canUseRecoveryKey: Boolean,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,22 +17,22 @@ class ChooseSelfVerificationModeStateProvider :
|
|||
override val values = sequenceOf(
|
||||
aChooseSelfVerificationModeState(
|
||||
buttonsState = AsyncData.Success(
|
||||
aButtonsState(canUseAnotherDevice = false, canEnterRecoveryKey = true),
|
||||
aButtonsState(canUseAnotherDevice = false, canUseRecoveryKey = true),
|
||||
),
|
||||
),
|
||||
aChooseSelfVerificationModeState(
|
||||
buttonsState = AsyncData.Success(
|
||||
aButtonsState(canUseAnotherDevice = false, canEnterRecoveryKey = false),
|
||||
aButtonsState(canUseAnotherDevice = false, canUseRecoveryKey = false),
|
||||
),
|
||||
),
|
||||
aChooseSelfVerificationModeState(
|
||||
buttonsState = AsyncData.Success(
|
||||
aButtonsState(canUseAnotherDevice = true, canEnterRecoveryKey = true),
|
||||
aButtonsState(canUseAnotherDevice = true, canUseRecoveryKey = true),
|
||||
),
|
||||
),
|
||||
aChooseSelfVerificationModeState(
|
||||
buttonsState = AsyncData.Success(
|
||||
aButtonsState(canUseAnotherDevice = true, canEnterRecoveryKey = false),
|
||||
aButtonsState(canUseAnotherDevice = true, canUseRecoveryKey = false),
|
||||
),
|
||||
),
|
||||
aChooseSelfVerificationModeState(
|
||||
|
|
@ -51,8 +51,8 @@ fun aChooseSelfVerificationModeState(
|
|||
|
||||
fun aButtonsState(
|
||||
canUseAnotherDevice: Boolean = true,
|
||||
canEnterRecoveryKey: Boolean = true,
|
||||
canUseRecoveryKey: Boolean = true,
|
||||
) = ChooseSelfVerificationModeState.ButtonsState(
|
||||
canUseAnotherDevice = canUseAnotherDevice,
|
||||
canEnterRecoveryKey = canEnterRecoveryKey,
|
||||
canUseRecoveryKey = canUseRecoveryKey,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -122,10 +122,10 @@ private fun ChooseSelfVerificationModeButtons(
|
|||
onClick = onUseAnotherDevice,
|
||||
)
|
||||
}
|
||||
if (state.buttonsState.data.canEnterRecoveryKey) {
|
||||
if (state.buttonsState.data.canUseRecoveryKey) {
|
||||
Button(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResource(R.string.screen_session_verification_enter_recovery_key),
|
||||
text = stringResource(R.string.screen_identity_confirmation_use_recovery_key),
|
||||
onClick = onUseRecoveryKey,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,5 +12,4 @@
|
|||
<string name="screen_identity_waiting_on_other_device">"Чаканне на іншай прыладзе…"</string>
|
||||
<string name="screen_notification_optin_subtitle">"Вы можаце змяніць налады пазней."</string>
|
||||
<string name="screen_notification_optin_title">"Дазвольце апавяшчэнні і ніколі не прапускайце іх"</string>
|
||||
<string name="screen_session_verification_enter_recovery_key">"Увядзіце ключ аднаўлення"</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -9,5 +9,4 @@
|
|||
<string name="screen_identity_use_another_device">"Използване на друго устройство"</string>
|
||||
<string name="screen_notification_optin_subtitle">"Можете да промените настройките си по-късно."</string>
|
||||
<string name="screen_notification_optin_title">"Разрешете известията и никога не пропускайте съобщение"</string>
|
||||
<string name="screen_session_verification_enter_recovery_key">"Въвеждане на ключ за възстановяване"</string>
|
||||
</resources>
|
||||
|
|
|
|||
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