Merge branch 'develop' into feat/variable-playback-speed

This commit is contained in:
Florian
2025-12-30 21:29:18 +01:00
committed by GitHub
8020 changed files with 56825 additions and 29988 deletions

View File

@@ -14,7 +14,7 @@ ij_smart_tabs = false
ij_visual_guides = none
ij_wrap_on_typing = false
# Ktlint rule, for more information see https://pinterest.github.io/ktlint/1.1.1/faq/#how-do-i-enable-or-disable-a-rule
# Ktlint rule, for more information see https://pinterest.github.io/ktlint/latest/faq/#how-do-i-enable-or-disable-a-rule
ktlint_standard_wrapping = disabled
ktlint_standard_trailing-comma-on-call-site = disabled
ktlint_standard_trailing-comma-on-declaration-site = disabled
@@ -26,7 +26,16 @@ ktlint_standard_annotation = disabled
ktlint_standard_parameter-list-wrapping = disabled
ktlint_standard_indent = disabled
ktlint_standard_blank-line-before-declaration = disabled
ktlint_function_naming_ignore_when_annotated_with=Composable
ktlint_function_naming_ignore_when_annotated_with = Composable
# Added when upgrading to 1.7.1
ktlint_standard_function-expression-body = disabled
ktlint_standard_chain-method-continuation = disabled
ktlint_standard_class-signature = disabled
# Added when upgrading to 1.8.0
ktlint_standard_when-entry-bracing = disabled
ktlint_standard_blank-line-between-when-conditions = disabled
ktlint_standard_mixed-condition-operators = disabled
ktlint_standard_no-unused-imports = enabled
[*.java]
ij_java_align_consecutive_assignments = false

View File

@@ -25,7 +25,7 @@ jobs:
group: ${{ github.ref == 'refs/heads/develop' && format('build-develop-{0}-{1}', matrix.variant, github.sha) || format('build-{0}-{1}', matrix.variant, github.ref) }}
cancel-in-progress: true
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
# Ensure we are building the branch and not the branch after being merged on develop
# https://github.com/actions/checkout/issues/881
@@ -46,6 +46,7 @@ jobs:
ELEMENT_ANDROID_MAPTILER_LIGHT_MAP_ID: ${{ secrets.MAPTILER_LIGHT_MAP_ID }}
ELEMENT_ANDROID_MAPTILER_DARK_MAP_ID: ${{ secrets.MAPTILER_DARK_MAP_ID }}
ELEMENT_ANDROID_SENTRY_DSN: ${{ secrets.ELEMENT_ANDROID_SENTRY_DSN }}
ELEMENT_SDK_SENTRY_DSN: ${{ secrets.ELEMENT_SDK_SENTRY_DSN }}
ELEMENT_CALL_SENTRY_DSN: ${{ secrets.ELEMENT_CALL_SENTRY_DSN }}
ELEMENT_CALL_POSTHOG_API_HOST: ${{ secrets.ELEMENT_CALL_POSTHOG_API_HOST }}
ELEMENT_CALL_POSTHOG_API_KEY: ${{ secrets.ELEMENT_CALL_POSTHOG_API_KEY }}
@@ -53,7 +54,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@v4
uses: actions/upload-artifact@v6
with:
name: elementx-debug
path: |
@@ -61,7 +62,7 @@ jobs:
app/build/outputs/apk/fdroid/debug/*-universal-debug.apk
- name: Upload x86_64 APK for Maestro
if: ${{ matrix.variant == 'debug' }}
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: elementx-apk-maestro
path: |
@@ -69,7 +70,7 @@ jobs:
retention-days: 5
overwrite: true
if-no-files-found: error
- uses: rnkdsh/action-upload-diawi@26292a7b424bdc9f4ab4ccea6202fc513f571370 # v1.5.11
- uses: rnkdsh/action-upload-diawi@4e1421305be7cfc510d05f47850262eeaf345108 # v1.5.12
id: diawi
# Do not fail the whole build if Diawi upload fails
continue-on-error: true

View File

@@ -27,7 +27,7 @@ jobs:
group: ${{ github.ref == 'refs/heads/develop' && format('build-develop-enterprise-{0}-{1}', matrix.variant, github.sha) || format('build-enterprise-{0}-{1}', matrix.variant, github.ref) }}
cancel-in-progress: true
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
# Ensure we are building the branch and not the branch after being merged on develop
# https://github.com/actions/checkout/issues/881
@@ -54,6 +54,7 @@ jobs:
ELEMENT_ANDROID_MAPTILER_LIGHT_MAP_ID: ${{ secrets.MAPTILER_LIGHT_MAP_ID }}
ELEMENT_ANDROID_MAPTILER_DARK_MAP_ID: ${{ secrets.MAPTILER_DARK_MAP_ID }}
ELEMENT_ANDROID_SENTRY_DSN: ${{ secrets.ELEMENT_ANDROID_SENTRY_DSN }}
ELEMENT_SDK_SENTRY_DSN: ${{ secrets.ELEMENT_SDK_SENTRY_DSN }}
ELEMENT_CALL_SENTRY_DSN: ${{ secrets.ELEMENT_CALL_SENTRY_DSN }}
ELEMENT_CALL_POSTHOG_API_HOST: ${{ secrets.ELEMENT_CALL_POSTHOG_API_HOST }}
ELEMENT_CALL_POSTHOG_API_KEY: ${{ secrets.ELEMENT_CALL_POSTHOG_API_KEY }}
@@ -61,7 +62,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@v4
uses: actions/upload-artifact@v6
with:
name: elementx-enterprise-debug
path: |

View File

@@ -9,7 +9,7 @@ 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@v5
- uses: actions/checkout@v6
- name: Add SSH private keys for submodule repositories
uses: webfactory/ssh-agent@a6f90b1f127823b31d4d4a8d96047790581349bd # v0.9.1
with:
@@ -20,7 +20,7 @@ jobs:
- run: |
npm install --save-dev @babel/plugin-transform-flow-strip-types
- name: Danger
uses: danger/danger-js@bdccecb77e0144055fbaea9224f10cf8b1229b68 # 13.0.4
uses: danger/danger-js@67ed2c1f42fd2fc198cc3c14b43c8f83351f4fe9 # 13.0.5
with:
args: "--dangerfile ./tools/danger/dangerfile.js"
env:

View File

@@ -25,7 +25,7 @@ jobs:
- name: Set up Python 3.12
uses: actions/setup-python@v6
with:
python-version: 3.13
python-version: 3.14
- name: Run World screenshots generation script
run: |
./tools/test/generateWorldScreenshots.py

View File

@@ -11,7 +11,7 @@ jobs:
# 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@v5
- uses: actions/checkout@v6
- uses: actions/setup-java@v5
name: Use JDK 21
if: (github.event_name == 'pull_request' && github.event.pull_request.fork == null) || github.event_name == 'workflow_dispatch'

View File

@@ -23,7 +23,7 @@ jobs:
group: ${{ format('maestro-{0}', github.ref) }}
cancel-in-progress: true
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
# Ensure we are building the branch and not the branch after being merged on develop
# https://github.com/actions/checkout/issues/881
@@ -44,7 +44,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@v4
uses: actions/upload-artifact@v6
with:
name: elementx-apk-maestro
path: |
@@ -62,14 +62,14 @@ jobs:
group: ${{ format('maestro-{0}', github.ref) }}
cancel-in-progress: true
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
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 }}
- name: Download APK artifact from previous job
uses: actions/download-artifact@v5
uses: actions/download-artifact@v7
with:
name: elementx-apk-maestro
- name: Enable KVM group perms
@@ -102,7 +102,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@v4
uses: actions/upload-artifact@v6
with:
name: test-results
path: |

View File

@@ -16,7 +16,7 @@ jobs:
runs-on: ubuntu-latest
if: ${{ github.repository == 'element-hq/element-x-android' }}
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Use JDK 21
uses: actions/setup-java@v5
with:
@@ -30,6 +30,7 @@ jobs:
ELEMENT_ANDROID_MAPTILER_LIGHT_MAP_ID: ${{ secrets.MAPTILER_LIGHT_MAP_ID }}
ELEMENT_ANDROID_MAPTILER_DARK_MAP_ID: ${{ secrets.MAPTILER_DARK_MAP_ID }}
ELEMENT_ANDROID_SENTRY_DSN: ${{ secrets.ELEMENT_ANDROID_SENTRY_DSN }}
ELEMENT_SDK_SENTRY_DSN: ${{ secrets.ELEMENT_SDK_SENTRY_DSN }}
ELEMENT_CALL_SENTRY_DSN: ${{ secrets.ELEMENT_CALL_SENTRY_DSN }}
ELEMENT_CALL_POSTHOG_API_HOST: ${{ secrets.ELEMENT_CALL_POSTHOG_API_HOST }}
ELEMENT_CALL_POSTHOG_API_KEY: ${{ secrets.ELEMENT_CALL_POSTHOG_API_KEY }}

View File

@@ -42,7 +42,7 @@ jobs:
- name: ✅ Upload kover report
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: kover-results
path: |
@@ -60,7 +60,7 @@ jobs:
name: Dependency analysis
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Use JDK 21
uses: actions/setup-java@v5
with:
@@ -74,7 +74,7 @@ jobs:
run: ./gradlew dependencyCheckAnalyze $CI_GRADLE_ARG_PROPERTIES
- name: Upload dependency analysis
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: dependency-analysis
path: build/reports/dependency-check-report.html

View File

@@ -17,7 +17,7 @@ jobs:
name: Search for forbidden patterns
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Add SSH private keys for submodule repositories
uses: webfactory/ssh-agent@a6f90b1f127823b31d4d4a8d96047790581349bd # v0.9.1
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
@@ -33,11 +33,11 @@ jobs:
name: Search for invalid screenshot files
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Set up Python 3.12
uses: actions/setup-python@v6
with:
python-version: 3.13
python-version: 3.14
- name: Search for invalid screenshot files
run: ./tools/test/checkInvalidScreenshots.py
@@ -45,7 +45,7 @@ jobs:
name: Search for invalid dependencies
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Use JDK 21
uses: actions/setup-java@v5
with:
@@ -58,7 +58,7 @@ jobs:
- name: Set up Python 3.12
uses: actions/setup-python@v6
with:
python-version: 3.13
python-version: 3.14
- name: Search for invalid dependencies
run: ./tools/dependencies/checkDependencies.py
@@ -71,7 +71,7 @@ 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@v5
- uses: actions/checkout@v6
with:
# Ensure we are building the branch and not the branch after being merged on develop
# https://github.com/actions/checkout/issues/881
@@ -97,12 +97,45 @@ jobs:
run: ./gradlew :tests:konsist:testDebugUnitTest $CI_GRADLE_ARG_PROPERTIES --no-daemon
- name: Upload reports
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: konsist-report
path: |
**/build/reports/**/*.*
compose:
name: Compose tests
runs-on: ubuntu-latest
# Allow all jobs on main and develop. Just one per PR.
concurrency:
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
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
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 compose tests
run: ./tools/compose/check_stability.sh
lint:
name: Android lint check
runs-on: ubuntu-latest
@@ -111,7 +144,7 @@ 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@v5
- uses: actions/checkout@v6
with:
# Ensure we are building the branch and not the branch after being merged on develop
# https://github.com/actions/checkout/issues/881
@@ -141,7 +174,7 @@ jobs:
run: ./gradlew :app:lintGplayDebug :app:lintFdroidDebug lintDebug $CI_GRADLE_ARG_PROPERTIES --continue
- name: Upload reports
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: linting-report
path: |
@@ -155,7 +188,7 @@ 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@v5
- uses: actions/checkout@v6
with:
# Ensure we are building the branch and not the branch after being merged on develop
# https://github.com/actions/checkout/issues/881
@@ -181,7 +214,7 @@ jobs:
run: ./gradlew detekt $CI_GRADLE_ARG_PROPERTIES --no-daemon
- name: Upload reports
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: detekt-report
path: |
@@ -195,7 +228,7 @@ 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@v5
- uses: actions/checkout@v6
with:
# Ensure we are building the branch and not the branch after being merged on develop
# https://github.com/actions/checkout/issues/881
@@ -221,7 +254,7 @@ jobs:
run: ./gradlew ktlintCheck $CI_GRADLE_ARG_PROPERTIES
- name: Upload reports
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: ktlint-report
path: |
@@ -235,7 +268,7 @@ jobs:
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) }}
cancel-in-progress: true
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
# Ensure we are building the branch and not the branch after being merged on develop
# https://github.com/actions/checkout/issues/881
@@ -266,7 +299,7 @@ jobs:
name: Check shell scripts
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Run shellcheck
uses: ludeeus/action-shellcheck@2.0.0
with:
@@ -278,13 +311,13 @@ jobs:
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@v5
- uses: actions/checkout@v6
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: Download reports from previous jobs
uses: actions/download-artifact@v5
uses: actions/download-artifact@v7
- name: Prepare Danger
if: always()
run: |
@@ -293,7 +326,7 @@ jobs:
yarn add danger-plugin-lint-report --dev
- name: Danger lint
if: always()
uses: danger/danger-js@bdccecb77e0144055fbaea9224f10cf8b1229b68 # 13.0.4
uses: danger/danger-js@67ed2c1f42fd2fc198cc3c14b43c8f83351f4fe9 # 13.0.5
with:
args: "--dangerfile ./tools/danger/dangerfile-lint.js"
env:

View File

@@ -18,7 +18,7 @@ jobs:
group: ${{ format('build-release-main-gplay-{0}', github.sha) }}
cancel-in-progress: true
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Use JDK 21
uses: actions/setup-java@v5
with:
@@ -32,13 +32,14 @@ jobs:
ELEMENT_ANDROID_MAPTILER_LIGHT_MAP_ID: ${{ secrets.MAPTILER_LIGHT_MAP_ID }}
ELEMENT_ANDROID_MAPTILER_DARK_MAP_ID: ${{ secrets.MAPTILER_DARK_MAP_ID }}
ELEMENT_ANDROID_SENTRY_DSN: ${{ secrets.ELEMENT_ANDROID_SENTRY_DSN }}
ELEMENT_SDK_SENTRY_DSN: ${{ secrets.ELEMENT_SDK_SENTRY_DSN }}
ELEMENT_CALL_SENTRY_DSN: ${{ secrets.ELEMENT_CALL_SENTRY_DSN }}
ELEMENT_CALL_POSTHOG_API_HOST: ${{ secrets.ELEMENT_CALL_POSTHOG_API_HOST }}
ELEMENT_CALL_POSTHOG_API_KEY: ${{ secrets.ELEMENT_CALL_POSTHOG_API_KEY }}
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@v4
uses: actions/upload-artifact@v6
with:
name: elementx-app-gplay-bundle-unsigned
path: |
@@ -52,7 +53,7 @@ jobs:
group: ${{ format('build-release-main-enterprise-{0}', github.sha) }}
cancel-in-progress: true
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Add SSH private keys for submodule repositories
uses: webfactory/ssh-agent@a6f90b1f127823b31d4d4a8d96047790581349bd # v0.9.1
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
@@ -74,7 +75,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@v4
uses: actions/upload-artifact@v6
with:
name: elementx-enterprise-app-gplay-bundle-unsigned
path: |
@@ -87,7 +88,7 @@ jobs:
group: ${{ format('build-release-main-fdroid-{0}', github.sha) }}
cancel-in-progress: true
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Use JDK 21
uses: actions/setup-java@v5
with:
@@ -102,7 +103,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@v4
uses: actions/upload-artifact@v6
with:
name: elementx-app-fdroid-apks-unsigned
path: |

View File

@@ -1,9 +1,10 @@
#!/bin/sh
#
# Copyright (c) 2025 Element Creations Ltd.
# Copyright 2024 New Vector Ltd.
#
# SPDX-License-Identifier: AGPL-3.0-only
# SPDX-License-Identifier: AGPL-3.0-only.
# Please see LICENSE in the repository root for full details.
#

View File

@@ -1,9 +1,10 @@
#!/bin/sh
#
# Copyright (c) 2025 Element Creations Ltd.
# Copyright 2024 New Vector Ltd.
#
# SPDX-License-Identifier: AGPL-3.0-only
# SPDX-License-Identifier: AGPL-3.0-only.
# Please see LICENSE in the repository root for full details.
#

View File

@@ -1,8 +1,9 @@
#!/bin/bash
# Copyright (c) 2025 Element Creations Ltd.
# Copyright 2023-2024 New Vector Ltd.
#
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
# Please see LICENSE files in the repository root for full details.
set -e

View File

@@ -22,7 +22,7 @@ jobs:
group: ${{ format('sonar-{0}', github.ref) }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/develop' }}
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
# Ensure we are building the branch and not the branch after being merged on develop
# https://github.com/actions/checkout/issues/881

View File

@@ -11,7 +11,7 @@ jobs:
# 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@v5
- uses: actions/checkout@v6
- name: Use JDK 21
uses: actions/setup-java@v5
with:
@@ -24,7 +24,7 @@ jobs:
- name: Set up Python 3.12
uses: actions/setup-python@v6
with:
python-version: 3.13
python-version: 3.14
- name: Setup Localazy
run: |
curl -sS https://dist.localazy.com/debian/pubkey.gpg | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/localazy.gpg
@@ -36,7 +36,7 @@ jobs:
./tools/localazy/importSupportedLocalesFromLocalazy.py
./tools/test/generateAllScreenshots.py
- name: Create Pull Request for Strings
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725 # v8.0.0
with:
token: ${{ secrets.DANGER_GITHUB_API_TOKEN }}
commit-message: Sync Strings from Localazy

View File

@@ -12,18 +12,18 @@ 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@v5
- uses: actions/checkout@v6
- name: Set up Python 3.12
uses: actions/setup-python@v6
with:
python-version: 3.13
python-version: 3.14
- name: Install Prerequisite dependencies
run: |
pip install requests
- name: Run SAS String script
run: ./tools/sas/import_sas_strings.py
- name: Create Pull Request for SAS Strings
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725 # v8.0.0
with:
commit-message: Sync SAS Strings
title: Sync SAS Strings

View File

@@ -61,7 +61,7 @@ jobs:
- name: 🚫 Upload kover failed coverage reports
if: failure()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: kover-error-report
path: |
@@ -73,7 +73,7 @@ jobs:
- name: 🚫 Upload test results on error
if: failure()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: tests-and-screenshot-tests-results
path: |
@@ -83,7 +83,7 @@ jobs:
# https://github.com/codecov/codecov-action
- name: ☂️ Upload coverage reports to codecov
uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1
uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2
with:
fail_ci_if_error: true
token: ${{ secrets.CODECOV_TOKEN }}

6
.gitignore vendored
View File

@@ -1,3 +1,8 @@
# Copyright (c) 2025 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.
# Built application files
*.apk
*.ap_
@@ -50,6 +55,7 @@ captures/
.idea/deviceManager.xml
.idea/gradle.xml
.idea/jarRepositories.xml
.idea/markdown.xml
.idea/misc.xml
.idea/modules.xml
# Comment next line if keeping position of elements in Navigation Editor is relevant for you

View File

@@ -1,6 +1,6 @@
<component name="CopyrightManager">
<copyright>
<option name="notice" value="© &amp;#36;today.year New Vector Limited, Element Software SARL, Element Software Inc.,&#10;and Element Software GmbH (the &quot;Element Group&quot;) only make this file available&#10;under a proprietary license model.&#10;&#10;Without a proprietary license with us, you cannot use this file. The terms of&#10;the proprietary license agreement between you and any member of the Element Group&#10;shall always apply to your use of this file. Unauthorised use, copying, distribution,&#10;or modification of this file, via any medium, is strictly prohibited.&#10;&#10;For details about the licensing terms, you must either visit our website or contact&#10;a member of our sales team." />
<option name="notice" value="© &amp;#36;today.year Element Creations Ltd.&#10;&#10;Element Creations Ltd, Element Software SARL, Element Software Inc.,&#10;and Element Software GmbH (the &quot;Element Group&quot;) only make this file available&#10;under a proprietary license model.&#10;&#10;Without a proprietary license with us, you cannot use this file. The terms of&#10;the proprietary license agreement between you and any member of the Element Group&#10;shall always apply to your use of this file. Unauthorised use, copying, distribution,&#10;or modification of this file, via any medium, is strictly prohibited.&#10;&#10;For details about the licensing terms, you must either visit our website or contact&#10;a member of our sales team." />
<option name="myName" value="Element Enterprise" />
</copyright>
</component>

View File

@@ -1,6 +1,6 @@
<component name="CopyrightManager">
<copyright>
<option name="notice" value="Copyright &amp;#36;today.year New Vector Ltd.&#10;&#10;SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial&#10;Please see LICENSE files in the repository root for full details." />
<option name="notice" value="Copyright (c) &amp;#36;today.year Element Creations Ltd.&#10;&#10;SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.&#10;Please see LICENSE files in the repository root for full details." />
<option name="myName" value="Element FOSS" />
</copyright>
</component>

4
.idea/kotlinc.xml generated
View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KotlinJpsPluginSettings">
<option name="version" value="2.2.20" />
<option name="version" value="2.3.0" />
</component>
</project>
</project>

View File

@@ -1,3 +1,290 @@
Changes in Element X v25.12.0
=============================
<!-- Release notes generated using configuration in .github/release.yml at v25.12.0 -->
## What's Changed
### ✨ Features
* Room list: enable latest event sorter. by @bmarty in https://github.com/element-hq/element-x-android/pull/5825
* Add room list indicators about last message by @bmarty in https://github.com/element-hq/element-x-android/pull/5824
### 🙌 Improvements
* Change : improve room and space member list by @ganfra in https://github.com/element-hq/element-x-android/pull/5806
* Change : security and privacy rework by @ganfra in https://github.com/element-hq/element-x-android/pull/5816
### 🐛 Bugfixes
* Ensure confirmation dialog is displayed when an admin add other admin to a room by @bmarty in https://github.com/element-hq/element-x-android/pull/5786
* Edit user profile cancel confirmation by @bmarty in https://github.com/element-hq/element-x-android/pull/5788
* Fix editing owner by @bmarty in https://github.com/element-hq/element-x-android/pull/5807
* Uris should take precedence in plain text intents by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5785
* Fix long voice recording by @bmarty in https://github.com/element-hq/element-x-android/pull/5821
### 🗣 Translations
* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/5792
* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/5830
### 🧱 Build
* Use regex to check forbidden terms by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5784
* Update Gradle Wrapper from 8.14.3 to 9.2.1 by @ElementBot in https://github.com/element-hq/element-x-android/pull/5751
### Dependency upgrades
* fix(deps): update dependency androidx.sqlite:sqlite-ktx to v2.6.2 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5769
* fix(deps): update datastore to v1.2.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5789
* chore(deps): update peter-evans/create-pull-request action to v7.0.9 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5793
* fix(deps): update dependency io.nlopez.compose.rules:detekt to v0.4.28 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5795
* fix(deps): update metro to v0.7.7 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5771
* chore(deps): update plugin sonarqube to v7.1.0.6387 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5783
* fix(deps): update dependency io.github.sergio-sastre.composablepreviewscanner:android to v0.7.2 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5799
* fix(deps): update dependency org.matrix.rustcomponents:sdk-android to v25.11.24 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5796
* fix(deps): update dependency io.sentry:sentry-android to v8.27.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5803
* fix(deps): update dependency io.element.android:emojibase-bindings to v1.5.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5801
* fix(deps): update roborazzi to v1.52.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5804
* fix(deps): update dependency org.maplibre.gl:android-sdk to v12.2.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5814
* chore(deps): update actions/checkout action to v6 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5805
* fix(deps): update dependency com.google.testparameterinjector:test-parameter-injector to v1.20 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5800
* fix(deps): update android.gradle.plugin to v8.13.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5260
* fix(deps): update dependency org.matrix.rustcomponents:sdk-android to v25.11.26 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5818
* fix(deps): update dependencyanalysis to v3.5.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5819
* fix(deps): update dependency com.posthog:posthog-android to v3.27.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5834
* fix(deps): update dependency io.element.android:element-call-embedded to v0.16.3 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5839
* Upgrade the Rust SDK to `v25.12.2` by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5838
### Others
* misc : use newLatestEvent api from sdk by @ganfra in https://github.com/element-hq/element-x-android/pull/5809
* Inject RoomMemberListDataSource in the presenter constructor. by @bmarty in https://github.com/element-hq/element-x-android/pull/5822
* Add more performance checks by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5767
* Load `JoinedRoom` in home screen, pass it to the room flow by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5817
* Revert "fix(deps): update dependency com.posthog:posthog-android to v3.27.0" by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5836
**Full Changelog**: https://github.com/element-hq/element-x-android/compare/v25.11.3...v25.12.0
Changes in Element X v25.11.3
=============================
<!-- Release notes generated using configuration in .github/release.yml at v25.11.3 -->
## What's Changed
### 🙌 Improvements
* Improve rendering notification for multi account by @bmarty in https://github.com/element-hq/element-x-android/pull/5645
* Change : roles and permissions by @ganfra in https://github.com/element-hq/element-x-android/pull/5685
* Improve account provider selection during the login flow by @bmarty in https://github.com/element-hq/element-x-android/pull/5692
* Let notifications use avatar fallback. by @bmarty in https://github.com/element-hq/element-x-android/pull/5721
* Changes : member list improvements by @ganfra in https://github.com/element-hq/element-x-android/pull/5728
### 🐛 Bugfixes
* Do not use the bestDescription but the caption for images, when available by @bmarty in https://github.com/element-hq/element-x-android/pull/5684
* Add the user certificate if any when creating Matrix Client. by @bmarty in https://github.com/element-hq/element-x-android/pull/5686
* Ensure the form data are not lost when opening the log viewer. by @bmarty in https://github.com/element-hq/element-x-android/pull/5695
* Fix password flow when using a login link by @bmarty in https://github.com/element-hq/element-x-android/pull/5693
* Fix layout issue in text composer by @bmarty in https://github.com/element-hq/element-x-android/pull/5710
* Fix navigation stack overflow when sharing media by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5724
* Notification robustness by @bmarty in https://github.com/element-hq/element-x-android/pull/5726
* Send read receipts using the current timeline, not the live timeline by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5731
* Render Owner in the horizontal list when editing Admins. by @bmarty in https://github.com/element-hq/element-x-android/pull/5736
* Stop overriding the homeserver when restoring a `Client` by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5753
* Revert "Stop overriding the homeserver when restoring a `Client`" by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5754
* Try fixing forced dark mode issues on MIUI on Android 10 by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5708
* Fix crash at startup by @bmarty in https://github.com/element-hq/element-x-android/pull/5761
* Fix null pointer exception on room notification settings. by @bmarty in https://github.com/element-hq/element-x-android/pull/5758
* Fix crash when viewing Pinned events by @bmarty in https://github.com/element-hq/element-x-android/pull/5764
* Fix crash when pressing back from the showkase Activity by @bmarty in https://github.com/element-hq/element-x-android/pull/5772
* Fix navigation issue once incoming share is handled by @bmarty in https://github.com/element-hq/element-x-android/pull/5773
* Fix crash in work manager by @bmarty in https://github.com/element-hq/element-x-android/pull/5768
### 🗣 Translations
* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/5704
* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/5747
* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/5782
### 🧱 Build
* Module cleanup by @bmarty in https://github.com/element-hq/element-x-android/pull/5722
* Add `NIGHTLY` env for Sentry by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5779
### 🚧 In development 🚧
* Space : prepare Space Settings screen by @ganfra in https://github.com/element-hq/element-x-android/pull/5668
### Dependency upgrades
* fix(deps): update dependency androidx.core:core-splashscreen to v1.2.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5687
* fix(deps): update dependency com.posthog:posthog-android to v3.26.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5696
* fix(deps): update metro to v0.7.5 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5697
* Update dependency org.matrix.rustcomponents:sdk-android to v25.11.11 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5716
* Update plugin ktlint to v14 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5713
* Update plugin dependencycheck to v12.1.9 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5717
* Update dependency org.maplibre.gl:android-sdk to v12.1.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5714
* Update dependency io.sentry:sentry-android to v8.26.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5720
* Update sqldelight to v2.2.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5730
* fix(deps): update dependency com.squareup.okhttp3:okhttp-bom to v5.3.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5746
* fix(deps): update dependency com.google.firebase:firebase-bom to v34.6.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5737
* fix(deps): update metro to v0.7.6 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5752
* fix(deps): update dependency org.maplibre.gl:android-sdk to v12.1.3 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5743
* Update dependency com.squareup.okhttp3:okhttp-bom to v5.3.2 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5757
* fix(deps): update dependency com.pinterest.ktlint:ktlint-cli to v1.8.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5738
* fix(deps): update dependency org.matrix.rustcomponents:sdk-android to v25.11.19 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5762
* fix(deps): update dependencyanalysis to v3.5.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5776
### Others
* Extract save change dialog by @bmarty in https://github.com/element-hq/element-x-android/pull/5679
* Use the dedicated subdomain for the bug report URL by default by @benbz in https://github.com/element-hq/element-x-android/pull/5689
* Convert `ComposerAlertMolecule` to use alert levels. by @kaylendog in https://github.com/element-hq/element-x-android/pull/5691
* Improve composer alert molecule by @bmarty in https://github.com/element-hq/element-x-android/pull/5701
* Code consistency around view event handling by @bmarty in https://github.com/element-hq/element-x-android/pull/5698
* Update copyright holders by @bmarty in https://github.com/element-hq/element-x-android/pull/5706
* Fix rendering notifications after receiving redundant push by @SpiritCroc in https://github.com/element-hq/element-x-android/pull/5711
* Fix push gateway with some push provider (Sunup/autopush) by @p1gp1g in https://github.com/element-hq/element-x-android/pull/5741
* Use new notification sound in release. by @bmarty in https://github.com/element-hq/element-x-android/pull/5748
* Fix issue on brand color override by @bmarty in https://github.com/element-hq/element-x-android/pull/5626
* Add media retention policy by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5749
* Enable logging OkHttp traffic based on the current log level by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5750
* Remove unused `slidingSyncProxy` from DB. by @bmarty in https://github.com/element-hq/element-x-android/pull/5755
* Add some performance metrics for Sentry by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5760
## New Contributors
* @benbz made their first contribution in https://github.com/element-hq/element-x-android/pull/5689
* @kaylendog made their first contribution in https://github.com/element-hq/element-x-android/pull/5691
**Full Changelog**: https://github.com/element-hq/element-x-android/compare/v25.11.2...v25.11.3
Changes in Element X v25.11.2
=============================
<!-- Release notes generated using configuration in .github/release.yml at v25.11.2 -->
## What's Changed
### ✨ Features
* Enable access to security and privacy by @bmarty in https://github.com/element-hq/element-x-android/pull/5566
* Add ability to forward a media from the media viewer and the gallery by @bmarty in https://github.com/element-hq/element-x-android/pull/5622
* Split notifications for messages in threads by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5595
### 🙌 Improvements
* Enable `SyncNotificationsWithWorkManager` in nightly and debug builds by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5573
* Confirm exit without saving change in room details edit screen by @bmarty in https://github.com/element-hq/element-x-android/pull/5618
* Space : add view members entry by @ganfra in https://github.com/element-hq/element-x-android/pull/5619
* Update notification sound by @bmarty in https://github.com/element-hq/element-x-android/pull/5667
* Use the new notification sound only on debug and nightly build by @bmarty in https://github.com/element-hq/element-x-android/pull/5673
* Make sure we know the session verification state before showing the options to verify the session by @bmarty in https://github.com/element-hq/element-x-android/pull/5677
### 🐛 Bugfixes
* Improve how brand color is applied. by @bmarty in https://github.com/element-hq/element-x-android/pull/5584
* Improve wellknown retrieval API by @bmarty in https://github.com/element-hq/element-x-android/pull/5587
* Clearing the room list search clears the search term too by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5603
* Delete pin code only when the last session is deleted by @bmarty in https://github.com/element-hq/element-x-android/pull/5600
* Fix issues with WorkManager on Android 12 and below by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5606
* Fix marking a room as read re-instantiates its timeline by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5628
* Display only valid emojis in recent emoji list by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5612
* Fix navigation issue. by @bmarty in https://github.com/element-hq/element-x-android/pull/5666
* Fix forward events from media viewer from pinned media timeline by @bmarty in https://github.com/element-hq/element-x-android/pull/5669
* Try fixing 'Timeline Event object has already been destroyed' by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5675
* Use the SDK Client to check whether a homeserver is compatible by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5664
### 🗣 Translations
* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/5610
* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/5662
### 🧱 Build
* Remove `@Inject`, not necessary anymore when class is annotated with `@ContributesBinding` by @bmarty in https://github.com/element-hq/element-x-android/pull/5589
* Upgrade ktlint to 1.7.1 and ensure Renovate will upgrade the version by @bmarty in https://github.com/element-hq/element-x-android/pull/5638
* Improve architecture around Nodes by @bmarty in https://github.com/element-hq/element-x-android/pull/5641
* Move dependencies block out of the android block. by @bmarty in https://github.com/element-hq/element-x-android/pull/5674
* Always use the handleEvent(s) function the same way. by @bmarty in https://github.com/element-hq/element-x-android/pull/5672
### Dependency upgrades
* fix(deps): update metro to v0.7.2 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5576
* fix(deps): update dependencyanalysis to v3.2.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5577
* fix(deps): update dependency io.sentry:sentry-android to v8.24.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5586
* fix(deps): update dependency androidx.work:work-runtime-ktx to v2.11.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5590
* fix(deps): update dependency com.posthog:posthog-android to v3.25.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5594
* fix(deps): update dependency com.google.crypto.tink:tink-android to v1.19.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5572
* Update plugin sonarqube to v7.0.1.6134 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5605
* fix(deps): update dependency org.matrix.rustcomponents:sdk-android to v25.10.28 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5620
* fix(deps): update dependencyanalysis to v3.3.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5602
* fix(deps): update dependency com.github.matrix-org:matrix-analytics-events to v0.29.2 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5621
* fix(deps): update dependencyanalysis to v3.4.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5624
* fix(deps): update dependency org.matrix.rustcomponents:sdk-android to v25.10.29 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5625
* fix(deps): update dependency io.sentry:sentry-android to v8.25.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5629
* fix(deps): update dependencyanalysis to v3.4.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5642
* fix(deps): update dependency com.squareup.okhttp3:okhttp-bom to v5.3.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5644
* chore(deps): update danger/danger-js action to v13.0.5 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5652
* fix(deps): update dependency com.google.firebase:firebase-bom to v34.5.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5643
* fix(deps): update firebaseappdistribution to v5.2.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5640
* fix(deps): update metro to v0.7.3 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5663
* fix(deps): update dependency org.matrix.rustcomponents:sdk-android to v25.10.31 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5657
* Update GitHub Artifact Actions (major) by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5609
* Update dependency io.element.android:element-call-embedded to v0.16.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5598
* Update roborazzi to v1.51.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5676
* fix(deps): update dependency org.matrix.rustcomponents:sdk-android to v25.11.4 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5681
* fix(deps): update metro to v0.7.4 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5683
### Others
* Improve code around Element .well-known configuration by @bmarty in https://github.com/element-hq/element-x-android/pull/5565
* misc: display offline banner for all LoggedIn screens by @ganfra in https://github.com/element-hq/element-x-android/pull/5574
* Remove icon preview duplicate by @bmarty in https://github.com/element-hq/element-x-android/pull/5588
* Remove application navigation state usage in the push module by @bmarty in https://github.com/element-hq/element-x-android/pull/5596
* Design : update Home TopBar and RoomList Filters by @ganfra in https://github.com/element-hq/element-x-android/pull/5599
* Add missing tests on the analytic modules by @bmarty in https://github.com/element-hq/element-x-android/pull/5604
* design(space): let SpaceRoomItemView divider be full width by @ganfra in https://github.com/element-hq/element-x-android/pull/5597
* Update notification style by @bmarty in https://github.com/element-hq/element-x-android/pull/5607
* Improve how data is handled for the WorkManager. by @bmarty in https://github.com/element-hq/element-x-android/pull/5592
* Revert "Make sure declining a call stops observing the ringing call state" by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5615
* Misc : space flow inject room by @ganfra in https://github.com/element-hq/element-x-android/pull/5614
* Enable `SyncNotificationsWithWorkManager` by default in release mode apps too by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5646
* Revert "Update notification sound" by @bmarty in https://github.com/element-hq/element-x-android/pull/5671
* Introduce new query to count accounts by @bmarty in https://github.com/element-hq/element-x-android/pull/5678
**Full Changelog**: https://github.com/element-hq/element-x-android/compare/v25.11.0...v25.11.2
Changes in Element X v25.11.0
=============================
Hotfix release.
Includes https://github.com/element-hq/element-x-android/pull/5615, which fixes an issue that prevented Element Call notifications from being displayed sometimes.
**Full Changelog**: https://github.com/element-hq/element-x-android/compare/v25.10.1...v25.11.0
Changes in Element X v25.10.1
=============================
<!-- Release notes generated using configuration in .github/release.yml at v25.10.1 -->
## What's Changed
### ✨ Features
* Sync notifications using WorkManager by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5545
### 🙌 Improvements
* Sort feature flags by @bmarty in https://github.com/element-hq/element-x-android/pull/5557
### 🐛 Bugfixes
* Makes sure images are loaded when cancelling multiaccount flow by @ganfra in https://github.com/element-hq/element-x-android/pull/5502
* Fix 'test push loop back' notification check by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5541
* Display 'join anyway' button on room preview when the state can't be loaded by @ShadowRZ in https://github.com/element-hq/element-x-android/pull/5514
* Fix media viewer not being dismissed with reduced motion enabled by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5555
* Keep the cursor position in room list search when going back by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5570
* Make sure declining a call stops observing the ringing call state by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5563
### 🗣 Translations
* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/5515
* Sync Strings by @ElementBot in https://github.com/element-hq/element-x-android/pull/5562
### 🧱 Build
* Do some cleanup on our immutable annotation usage by @bmarty in https://github.com/element-hq/element-x-android/pull/5503
* `interface TestParameterValuesProvider` is deprecated. by @bmarty in https://github.com/element-hq/element-x-android/pull/5568
### Dependency upgrades
* fix(deps): update metro to v0.6.9 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5480
* fix(deps): update dependency org.unifiedpush.android:connector to v3.1.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5443
* fix(deps): update wysiwyg to v2.40.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5400
* fix(deps): update dependency io.github.sergio-sastre.composablepreviewscanner:android to v0.7.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5510
* fix(deps): update camera to v1.5.1 - autoclosed by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5509
* chore(deps): update plugin dependencycheck to v12.1.7 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5518
* chore(deps): update plugin licensee to v1.14.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5477
* chore(deps): update dependency python to 3.14 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5475
* fix(deps): update metro to v0.6.10 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5520
* fix(deps): update dependency org.unifiedpush.android:connector to v3.1.2 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5519
* chore(deps): update plugin gms_google_services to v4.4.4 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5507
* fix(deps): update dependency com.google.firebase:firebase-bom to v34.4.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5522
* fix(deps): update dependency com.squareup.okhttp3:okhttp-bom to v5.2.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5524
* fix(deps): update dependency net.zetetic:sqlcipher-android to v4.11.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5525
* fix(deps): update dependencyanalysis to v3.1.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5523
* fix(deps): update dependency org.matrix.rustcomponents:sdk-android to v25.10.13 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5527
* chore(deps): update plugin dependencycheck to v12.1.8 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5531
* chore(deps): update rnkdsh/action-upload-diawi action to v1.5.12 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5533
* fix(deps): update dependency org.maplibre.gl:android-sdk to v12.0.1 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5548
* fix(deps): update metro to v0.7.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5554
* fix(deps): update dependency com.posthog:posthog-android to v3.24.0 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5564
* chore(deps): update plugin sonarqube to v7 by @renovate[bot] in https://github.com/element-hq/element-x-android/pull/5535
### Others
* Import Compound tokens - fixed icons by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5506
* Replace Uri by String in States that are used in Composable function. by @bmarty in https://github.com/element-hq/element-x-android/pull/5508
* Let room filters follow the design. by @bmarty in https://github.com/element-hq/element-x-android/pull/5526
* Allow uploading notification push rules in bug reports by @jmartinesp in https://github.com/element-hq/element-x-android/pull/5538
* Add number of accounts info in the rageshake data. by @bmarty in https://github.com/element-hq/element-x-android/pull/5532
* design(space): match figma for Space views by @ganfra in https://github.com/element-hq/element-x-android/pull/5540
* Extract console message logger and mutualize instance of Json by @bmarty in https://github.com/element-hq/element-x-android/pull/5552
* Improve colors customization by @bmarty in https://github.com/element-hq/element-x-android/pull/5542
* Fix test warning by @bmarty in https://github.com/element-hq/element-x-android/pull/5558
**Full Changelog**: https://github.com/element-hq/element-x-android/compare/v25.10.0...v25.10.1
Changes in Element X v25.10.0
=============================

View File

@@ -2,7 +2,7 @@
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=element-x-android&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=element-x-android)
[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=element-x-android&metric=vulnerabilities)](https://sonarcloud.io/summary/new_code?id=element-x-android)
[![Bugs](https://sonarcloud.io/api/project_badges/measure?project=element-x-android&metric=bugs)](https://sonarcloud.io/summary/new_code?id=element-x-android)
[![codecov](https://codecov.io/github/element-hq/element-x-android/branch/develop/graph/badge.svg?token=ecwvia7amV)](https://codecov.io/github/vector-im/element-x-android)
[![codecov](https://codecov.io/github/element-hq/element-x-android/branch/develop/graph/badge.svg?token=ecwvia7amV)](https://codecov.io/github/element-hq/element-x-android)
[![Element X Android Matrix room #element-x-android:matrix.org](https://img.shields.io/matrix/element-x-android:matrix.org.svg?label=%23element-x-android:matrix.org&logo=matrix&server_fqdn=matrix.org)](https://matrix.to/#/#element-x-android:matrix.org)
[![Localazy](https://img.shields.io/endpoint?url=https%3A%2F%2Fconnect.localazy.com%2Fstatus%2Felement%2Fdata%3Fcontent%3Dall%26title%3Dlocalazy%26logo%3Dtrue)](https://localazy.com/p/element)
@@ -102,9 +102,10 @@ If after your research you still have a question, ask at [#element-x-android:mat
## Copyright and License
Copyright (c) 2022 - 2025 New Vector Ltd
Copyright (c) 2025 Element Creations Ltd.
Copyright (c) 2022 - 2025 New Vector Ltd.
This software is dual licensed by New Vector Ltd (Element). It can be used either:
This software is dual licensed by Element Creations Ltd (Element). It can be used either:
(1) for free under the terms of the GNU Affero General Public License (as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version); OR

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2022-2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2022-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
plugins {

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2022-2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2022-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
@@ -128,12 +129,12 @@ android {
)
signingConfig = signingConfigs.getByName("debug")
postprocessing {
isRemoveUnusedCode = true
isObfuscate = false
isOptimizeCode = true
isRemoveUnusedResources = true
proguardFiles("proguard-rules.pro")
optimization {
enable = true
keepRules {
files.add(File(projectDir, "proguard-rules.pro"))
files.add(getDefaultProguardFile("proguard-android-optimize.txt"))
}
}
}
@@ -151,10 +152,6 @@ android {
matchingFallbacks += listOf("release")
signingConfig = signingConfigs.getByName("nightly")
postprocessing {
initWith(release.postprocessing)
}
firebaseAppDistribution {
artifactType = "APK"
// We upload the universal APK to fix this error:
@@ -197,6 +194,16 @@ android {
buildConfigFieldStr("FLAVOR_DESCRIPTION", "FDroid")
}
}
packaging {
resources.pickFirsts += setOf(
"META-INF/versions/9/OSGI-INF/MANIFEST.MF",
)
jniLibs {
useLegacyPackaging = project.findProperty("useLegacyPackaging")?.toString()?.toBoolean()
}
}
}
androidComponents {
@@ -318,6 +325,7 @@ licensee {
allowUrl("https://jsoup.org/license")
allowUrl("https://asm.ow2.io/license.html")
allowUrl("https://www.gnu.org/licenses/agpl-3.0.txt")
allowUrl("https://github.com/mhssn95/compose-color-picker/blob/main/LICENSE")
ignoreDependencies("com.github.matrix-org", "matrix-analytics-events")
// Ignore dependency that are not third-party licenses to us.
ignoreDependencies(groupId = "io.element.android")
@@ -333,7 +341,7 @@ fun Project.configureLicensesTasks(reportingExtension: ReportingExtension) {
it.toString()
}
}
val artifactsFile = reportingExtension.file("licensee/android$capitalizedVariantName/artifacts.json")
val artifactsFile = reportingExtension.baseDirectory.file("licensee/android$capitalizedVariantName/artifacts.json")
val copyArtifactsTask =
project.tasks.register<AssetCopyTask>("copy${capitalizedVariantName}LicenseeReportToAssets") {

View File

@@ -41,8 +41,6 @@
static int windowAttachCount(android.view.View);
}
-keep class io.element.android.x.di.** { *; }
# Keep LogSessionId class and related classes (https://github.com/androidx/media/issues/2535)
-keep class android.media.metrics.LogSessionId { *; }
@@ -51,3 +49,27 @@
# Keep Media3 classes that use reflection (https://github.com/androidx/media/issues/2535)
-keep class androidx.media3.** { *; }
-dontwarn android.media.metrics.**
# New rules after AGP 8.13.1 upgrade
-dontwarn androidx.window.extensions.WindowExtensions
-dontwarn androidx.window.extensions.WindowExtensionsProvider
-dontwarn androidx.window.extensions.area.ExtensionWindowAreaPresentation
-dontwarn androidx.window.extensions.layout.DisplayFeature
-dontwarn androidx.window.extensions.layout.FoldingFeature
-dontwarn androidx.window.extensions.layout.WindowLayoutComponent
-dontwarn androidx.window.extensions.layout.WindowLayoutInfo
-dontwarn androidx.window.sidecar.SidecarDeviceState
-dontwarn androidx.window.sidecar.SidecarDisplayFeature
-dontwarn androidx.window.sidecar.SidecarInterface$SidecarCallback
-dontwarn androidx.window.sidecar.SidecarInterface
-dontwarn androidx.window.sidecar.SidecarProvider
-dontwarn androidx.window.sidecar.SidecarWindowLayoutInfo
# Also needed after AGP 8.13.1 upgrade, it seems like proguard is now more aggressive on removing unused code
-keep class org.matrix.rustcomponents.sdk.** { *;}
-keep class uniffi.** { *;}
-keep class io.element.android.x.di.** { *; }
-keepclasseswithmembernames,allowoptimization,allowshrinking class io.element.android.** { *; }
# Keep Metro classes
-keep class dev.zacsweers.metro.** { *; }

View File

@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2025 Element Creations Ltd.
~ Copyright 2022 New Vector Ltd.
~
~ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
~ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
~ Please see LICENSE files in the repository root for full details.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
@@ -34,10 +35,17 @@
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false">
android:exported="false"
tools:node="merge">
<meta-data
android:name='androidx.lifecycle.ProcessLifecycleInitializer'
android:value='androidx.startup' />
<!-- Remove to use Application workManagerConfiguration -->
<meta-data
android:name="androidx.work.WorkManagerInitializer"
android:value="androidx.startup"
tools:node="remove" />
</provider>
<!--
@@ -175,7 +183,6 @@
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_providers" />
</provider>
</application>
</manifest>

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2022-2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2022-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
@@ -9,17 +10,23 @@ package io.element.android.x
import android.app.Application
import androidx.startup.AppInitializer
import androidx.work.Configuration
import dev.zacsweers.metro.createGraphFactory
import io.element.android.features.cachecleaner.api.CacheCleanerInitializer
import io.element.android.libraries.di.DependencyInjectionGraphOwner
import io.element.android.libraries.workmanager.api.di.MetroWorkerFactory
import io.element.android.x.di.AppGraph
import io.element.android.x.info.logApplicationInfo
import io.element.android.x.initializer.CacheCleanerInitializer
import io.element.android.x.initializer.CrashInitializer
import io.element.android.x.initializer.PlatformInitializer
class ElementXApplication : Application(), DependencyInjectionGraphOwner {
class ElementXApplication : Application(), DependencyInjectionGraphOwner, Configuration.Provider {
override val graph: AppGraph = createGraphFactory<AppGraph.Factory>().create(this)
override val workManagerConfiguration: Configuration = Configuration.Builder()
.setWorkerFactory(MetroWorkerFactory(graph.workerProviders))
.build()
override fun onCreate() {
super.onCreate()
AppInitializer.getInstance(this).apply {
@@ -27,6 +34,7 @@ class ElementXApplication : Application(), DependencyInjectionGraphOwner {
initializeComponent(PlatformInitializer::class.java)
initializeComponent(CacheCleanerInitializer::class.java)
}
logApplicationInfo(this)
}
}

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2022-2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2022-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
@@ -16,6 +17,9 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalUriHandler
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
@@ -25,6 +29,7 @@ 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
import io.element.android.compound.theme.ElementTheme
import io.element.android.features.lockscreen.api.LockScreenEntryPoint
import io.element.android.features.lockscreen.api.LockScreenLockState
@@ -61,9 +66,13 @@ class MainActivity : NodeActivity() {
@Composable
private fun MainContent(appBindings: AppBindings) {
val migrationState = appBindings.migrationEntryPoint().present()
val colors by remember {
appBindings.enterpriseService().semanticColorsFlow(sessionId = null)
}.collectAsState(SemanticColorsLightDark.default)
ElementThemeApp(
appPreferencesStore = appBindings.preferencesStore(),
enterpriseService = appBindings.enterpriseService(),
compoundLight = colors.light,
compoundDark = colors.dark,
buildMeta = appBindings.buildMeta()
) {
CompositionLocalProvider(

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
@@ -16,6 +17,7 @@ import io.element.android.features.lockscreen.api.LockScreenService
import io.element.android.features.rageshake.api.reporter.BugReporter
import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.di.identifiers.SentrySdkDsn
import io.element.android.libraries.featureflag.api.FeatureFlagService
import io.element.android.libraries.matrix.api.platform.InitPlatformService
import io.element.android.libraries.matrix.api.tracing.TracingService
@@ -47,4 +49,6 @@ interface AppBindings {
fun featureFlagService(): FeatureFlagService
fun buildMeta(): BuildMeta
fun sentrySdkDsn(): SentrySdkDsn?
}

View File

@@ -1,23 +1,32 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* 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.x.di
import android.content.Context
import androidx.work.ListenableWorker
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.DependencyGraph
import dev.zacsweers.metro.Multibinds
import dev.zacsweers.metro.Provides
import io.element.android.libraries.architecture.NodeFactoriesBindings
import io.element.android.libraries.di.annotations.ApplicationContext
import io.element.android.libraries.workmanager.api.di.MetroWorkerFactory
import kotlin.reflect.KClass
@DependencyGraph(AppScope::class)
interface AppGraph : NodeFactoriesBindings {
val sessionGraphFactory: SessionGraph.Factory
@Multibinds
val workerProviders:
Map<KClass<out ListenableWorker>, MetroWorkerFactory.WorkerInstanceFactory<*>>
@DependencyGraph.Factory
interface Factory {
fun create(

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
@@ -18,8 +19,6 @@ import dev.zacsweers.metro.Provides
import dev.zacsweers.metro.SingleIn
import io.element.android.appconfig.ApplicationConfig
import io.element.android.features.enterprise.api.EnterpriseService
import io.element.android.features.messages.impl.timeline.components.customreaction.DefaultEmojibaseProvider
import io.element.android.features.messages.impl.timeline.components.customreaction.EmojibaseProvider
import io.element.android.libraries.androidutils.system.getVersionCodeFromManifest
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.core.meta.BuildMeta
@@ -29,6 +28,8 @@ import io.element.android.libraries.di.BaseDirectory
import io.element.android.libraries.di.CacheDirectory
import io.element.android.libraries.di.annotations.AppCoroutineScope
import io.element.android.libraries.di.annotations.ApplicationContext
import io.element.android.libraries.recentemojis.api.EmojibaseProvider
import io.element.android.libraries.recentemojis.impl.DefaultEmojibaseProvider
import io.element.android.x.BuildConfig
import io.element.android.x.R
import kotlinx.coroutines.CoroutineName

View File

@@ -1,20 +1,19 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* 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.x.di
import dev.zacsweers.metro.ContributesBinding
import dev.zacsweers.metro.Inject
import io.element.android.appnav.di.RoomGraphFactory
import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.matrix.api.room.JoinedRoom
@ContributesBinding(SessionScope::class)
@Inject
class DefaultRoomGraphFactory(
private val sessionGraph: SessionGraph,
) : RoomGraphFactory {

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
@@ -9,12 +10,10 @@ package io.element.android.x.di
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesBinding
import dev.zacsweers.metro.Inject
import io.element.android.appnav.di.SessionGraphFactory
import io.element.android.libraries.matrix.api.MatrixClient
@ContributesBinding(AppScope::class)
@Inject
class DefaultSessionGraphFactory(
private val appGraph: AppGraph
) : SessionGraphFactory {

View File

@@ -1,7 +1,8 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
@@ -9,13 +10,14 @@ package io.element.android.x.di
import dev.zacsweers.metro.GraphExtension
import dev.zacsweers.metro.Provides
import io.element.android.appnav.di.TimelineBindings
import io.element.android.libraries.architecture.NodeFactoriesBindings
import io.element.android.libraries.di.RoomScope
import io.element.android.libraries.matrix.api.room.BaseRoom
import io.element.android.libraries.matrix.api.room.JoinedRoom
@GraphExtension(RoomScope::class)
interface RoomGraph : NodeFactoriesBindings {
interface RoomGraph : NodeFactoriesBindings, TimelineBindings {
@GraphExtension.Factory
interface Factory {
fun create(

View File

@@ -1,7 +1,8 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/

View File

@@ -1,14 +1,16 @@
/*
* Copyright 2022-2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2022-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* 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.cachecleaner.api
package io.element.android.x.initializer
import android.content.Context
import androidx.startup.Initializer
import io.element.android.features.cachecleaner.impl.CacheCleanerBindings
import io.element.android.libraries.architecture.bindings
class CacheCleanerInitializer : Initializer<Unit> {

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2022-2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2022-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2022-2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2022-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
@@ -37,6 +38,7 @@ class PlatformInitializer : Initializer<Unit> {
logLevel = logLevel,
extraTargets = listOf(ELEMENT_X_TARGET),
traceLogPacks = runBlocking { preferencesStore.getTracingLogPacksFlow().first() },
sdkSentryDsn = appBindings.sentrySdkDsn()?.value?.takeIf { it.isNotBlank() },
)
bugReporter.setCurrentTracingLogLevel(logLevel.name)
platformService.init(tracingConfiguration)

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
@@ -9,12 +10,13 @@ package io.element.android.x.intent
import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.core.net.toUri
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesBinding
import dev.zacsweers.metro.Inject
import io.element.android.libraries.deeplink.api.DeepLinkCreator
import io.element.android.libraries.di.annotations.ApplicationContext
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.ThreadId
@@ -22,7 +24,6 @@ import io.element.android.libraries.push.impl.intent.IntentProvider
import io.element.android.x.MainActivity
@ContributesBinding(AppScope::class)
@Inject
class DefaultIntentProvider(
@ApplicationContext private val context: Context,
private val deepLinkCreator: DeepLinkCreator,
@@ -31,10 +32,13 @@ class DefaultIntentProvider(
sessionId: SessionId,
roomId: RoomId?,
threadId: ThreadId?,
eventId: EventId?,
extras: Bundle?,
): Intent {
return Intent(context, MainActivity::class.java).apply {
action = Intent.ACTION_VIEW
data = deepLinkCreator.create(sessionId, roomId, threadId).toUri()
data = deepLinkCreator.create(sessionId, roomId, threadId, eventId).toUri()
extras?.let(::putExtras)
}
}
}

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/

View File

@@ -1,7 +1,8 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
@@ -9,13 +10,11 @@ package io.element.android.x.oidc
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesBinding
import dev.zacsweers.metro.Inject
import io.element.android.libraries.matrix.api.auth.OidcRedirectUrlProvider
import io.element.android.services.toolbox.api.strings.StringProvider
import io.element.android.x.R
@ContributesBinding(AppScope::class)
@Inject
class DefaultOidcRedirectUrlProvider(
private val stringProvider: StringProvider,
) : OidcRedirectUrlProvider {

View File

@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2025 Element Creations Ltd.
~ Copyright 2023 New Vector Ltd.
~
~ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
~ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
~ Please see LICENSE files in the repository root for full details.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android">

View File

@@ -1,6 +1,7 @@
# Copyright (c) 2025 Element Creations Ltd.
# Copyright 2024 New Vector Ltd.
#
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
# Please see LICENSE files in the repository root for full details.
unqualifiedResLocale=en

View File

@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2025 Element Creations Ltd.
~ Copyright 2023 New Vector Ltd.
~
~ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
~ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
~ Please see LICENSE files in the repository root for full details.
-->
<resources>

View File

@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2025 Element Creations Ltd.
~ Copyright 2022 New Vector Ltd.
~
~ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
~ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
~ Please see LICENSE files in the repository root for full details.
-->
<resources>

View File

@@ -1,14 +1,17 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2025 Element Creations Ltd.
~ Copyright 2022 New Vector Ltd.
~
~ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
~ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
~ Please see LICENSE files in the repository root for full details.
-->
<resources>
<resources xmlns:tools="http://schemas.android.com/tools">
<style name="Theme.ElementX.Splash" parent="Theme.SplashScreen">
<item name="windowSplashScreenBackground">@color/splashscreen_bg_light</item>
<item name="windowSplashScreenAnimatedIcon">@drawable/transparent</item>
<item name="postSplashScreenTheme">@style/Theme.ElementX</item>
</style>
<style name="Theme.ElementX" parent="Theme.Material3.Light.NoActionBar" />
<style name="Theme.ElementX" parent="Theme.Material3.Light.NoActionBar">
<item name="android:forceDarkAllowed" tools:targetApi="q">false</item>
</style>
</resources>

View File

@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2025 Element Creations Ltd.
~ Copyright 2025 New Vector Ltd.
~
~ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
~ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
~ Please see LICENSE files in the repository root for full details.
-->
<automotiveApp>

View File

@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2025 Element Creations Ltd.
~ Copyright 2022 New Vector Ltd.
~
~ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
~ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
~ Please see LICENSE files in the repository root for full details.
-->
<!--

View File

@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2025 Element Creations Ltd.
~ Copyright 2023 New Vector Ltd.
~
~ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
~ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
~ Please see LICENSE files in the repository root for full details.
-->
<!--
@@ -23,4 +24,4 @@
<exclude domain="sharedpref" path="."/>
<exclude domain="external" path="."/>
</device-transfer>
</data-extraction-rules>
</data-extraction-rules>

View File

@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2025 Element Creations Ltd.
~ Copyright 2023 New Vector Ltd.
~
~ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
~ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
~ Please see LICENSE files in the repository root for full details.
-->
<paths>

View File

@@ -9,13 +9,13 @@
<locale android:name="el"/>
<locale android:name="en"/>
<locale android:name="en_US"/>
<locale android:name="eo"/>
<locale android:name="es"/>
<locale android:name="et"/>
<locale android:name="eu"/>
<locale android:name="fa"/>
<locale android:name="fi"/>
<locale android:name="fr"/>
<locale android:name="hr"/>
<locale android:name="hu"/>
<locale android:name="in"/>
<locale android:name="it"/>

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
@@ -13,9 +14,11 @@ import android.content.Context
import android.content.Intent
import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.deeplink.api.DeepLinkCreator
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.ThreadId
import io.element.android.libraries.matrix.test.AN_EVENT_ID
import io.element.android.libraries.matrix.test.A_ROOM_ID
import io.element.android.libraries.matrix.test.A_SESSION_ID
import io.element.android.libraries.matrix.test.A_THREAD_ID
@@ -31,14 +34,15 @@ import org.robolectric.RuntimeEnvironment
class DefaultIntentProviderTest {
@Test
fun `test getViewRoomIntent with data`() {
val deepLinkCreator = lambdaRecorder<SessionId, RoomId?, ThreadId?, String> { _, _, _ -> "deepLinkCreatorResult" }
val deepLinkCreator = lambdaRecorder<SessionId, RoomId?, ThreadId?, EventId?, String> { _, _, _, _ -> "deepLinkCreatorResult" }
val sut = createDefaultIntentProvider(
deepLinkCreator = { sessionId, roomId, threadId -> deepLinkCreator.invoke(sessionId, roomId, threadId) },
deepLinkCreator = { sessionId, roomId, threadId, eventId -> deepLinkCreator.invoke(sessionId, roomId, threadId, eventId) },
)
val result = sut.getViewRoomIntent(
sessionId = A_SESSION_ID,
roomId = A_ROOM_ID,
threadId = A_THREAD_ID,
eventId = AN_EVENT_ID,
)
result.commonAssertions()
assertThat(result.data.toString()).isEqualTo("deepLinkCreatorResult")
@@ -46,11 +50,12 @@ class DefaultIntentProviderTest {
value(A_SESSION_ID),
value(A_ROOM_ID),
value(A_THREAD_ID),
value(AN_EVENT_ID),
)
}
private fun createDefaultIntentProvider(
deepLinkCreator: DeepLinkCreator = DeepLinkCreator { _, _, _ -> "" },
deepLinkCreator: DeepLinkCreator = DeepLinkCreator { _, _, _, _ -> "" },
): DefaultIntentProvider {
return DefaultIntentProvider(
context = RuntimeEnvironment.getApplication() as Context,

View File

@@ -1,7 +1,8 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/

View File

@@ -2,9 +2,10 @@ import config.BuildTimeConfig
import extension.buildConfigFieldStr
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2022-2024 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
plugins {
@@ -32,7 +33,7 @@ android {
value = if (isEnterpriseBuild) {
BuildTimeConfig.BUG_REPORT_URL ?: ""
} else {
"https://riot.im/bugreports/submit"
"https://rageshakes.element.io/api/submit"
},
)
buildConfigFieldStr(

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
@@ -12,4 +13,5 @@ object LearnMoreConfig {
const val DEVICE_VERIFICATION_URL: String = "https://element.io/help#encryption-device-verification"
const val SECURE_BACKUP_URL: String = "https://element.io/help#encryption5"
const val IDENTITY_CHANGE_URL: String = "https://element.io/help#encryption18"
const val HISTORY_VISIBLE_URL: String = "https://element.io/en/help#e2ee-history-sharing"
}

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2024, 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/

View File

@@ -1,14 +1,15 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* 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.appconfig
import android.graphics.Color
import androidx.annotation.ColorInt
import androidx.core.graphics.toColorInt
object NotificationConfig {
/**
@@ -27,5 +28,5 @@ object NotificationConfig {
const val SHOW_QUICK_REPLY_ACTION = true
@ColorInt
val NOTIFICATION_ACCENT_COLOR: Int = Color.parseColor("#FF0DBD8B")
val NOTIFICATION_ACCENT_COLOR: Int = "#FF0DBD8B".toColorInt()
}

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2024, 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2024, 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
@@ -16,19 +17,19 @@ object TimelineConfig {
* Event types that will be filtered out from the timeline (i.e. not displayed).
*/
val excludedEvents = listOf(
StateEventType.CALL_MEMBER,
StateEventType.ROOM_ALIASES,
StateEventType.ROOM_CANONICAL_ALIAS,
StateEventType.ROOM_GUEST_ACCESS,
StateEventType.ROOM_HISTORY_VISIBILITY,
StateEventType.ROOM_JOIN_RULES,
StateEventType.ROOM_POWER_LEVELS,
StateEventType.ROOM_SERVER_ACL,
StateEventType.ROOM_TOMBSTONE,
StateEventType.SPACE_CHILD,
StateEventType.SPACE_PARENT,
StateEventType.POLICY_RULE_ROOM,
StateEventType.POLICY_RULE_SERVER,
StateEventType.POLICY_RULE_USER,
StateEventType.CallMember,
StateEventType.RoomAliases,
StateEventType.RoomCanonicalAlias,
StateEventType.RoomGuestAccess,
StateEventType.RoomHistoryVisibility,
StateEventType.RoomJoinRules,
StateEventType.RoomPowerLevels,
StateEventType.RoomServerAcl,
StateEventType.RoomTombstone,
StateEventType.SpaceChild,
StateEventType.SpaceParent,
StateEventType.PolicyRuleRoom,
StateEventType.PolicyRuleServer,
StateEventType.PolicyRuleUser,
)
}

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2024, 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
plugins {

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/

View File

@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2025 Element Creations Ltd.
~ Copyright 2023 New Vector Ltd.
~
~ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
~ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
~ Please see LICENSE files in the repository root for full details.
-->
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">

View File

@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2025 Element Creations Ltd.
~ Copyright 2023 New Vector Ltd.
~
~ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
~ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
~ Please see LICENSE files in the repository root for full details.
-->
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2024, 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
plugins {

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2024, 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/

View File

@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2025 Element Creations Ltd.
~ Copyright 2024 New Vector Ltd.
~
~ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
~ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
~ Please see LICENSE files in the repository root for full details.
-->
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">

View File

@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2025 Element Creations Ltd.
~ Copyright 2024 New Vector Ltd.
~
~ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
~ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
~ Please see LICENSE files in the repository root for full details.
-->
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2022-2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2022-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
@@ -38,6 +39,7 @@ dependencies {
implementation(projects.libraries.pushproviders.api)
implementation(projects.libraries.designsystem)
implementation(projects.libraries.matrixui)
implementation(projects.libraries.matrixmedia.api)
implementation(projects.libraries.uiCommon)
implementation(projects.libraries.uiStrings)
implementation(projects.features.login.api)
@@ -46,6 +48,7 @@ dependencies {
implementation(projects.features.announcement.api)
implementation(projects.features.ftue.api)
implementation(projects.features.linknewdevice.api)
implementation(projects.features.share.api)
implementation(projects.services.apperror.impl)
@@ -59,8 +62,10 @@ dependencies {
testImplementation(projects.libraries.preferences.test)
testImplementation(projects.libraries.push.test)
testImplementation(projects.libraries.pushproviders.test)
testImplementation(projects.features.forward.test)
testImplementation(projects.features.networkmonitor.test)
testImplementation(projects.features.rageshake.test)
testImplementation(projects.services.appnavstate.impl)
testImplementation(projects.services.appnavstate.test)
testImplementation(projects.services.analytics.test)
testImplementation(projects.services.toolbox.test)

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
@@ -21,13 +22,13 @@ import com.bumble.appyx.core.navigation.model.permanent.PermanentNavModel
import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.node.ParentNode
import com.bumble.appyx.core.plugin.Plugin
import com.bumble.appyx.core.plugin.plugins
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode
import io.element.android.appnav.di.SessionGraphFactory
import io.element.android.libraries.architecture.NodeInputs
import io.element.android.libraries.architecture.callback
import io.element.android.libraries.architecture.createNode
import io.element.android.libraries.architecture.inputs
import io.element.android.libraries.di.DependencyInjectionGraphOwner
@@ -56,10 +57,12 @@ class LoggedInAppScopeFlowNode(
plugins = plugins
), DependencyInjectionGraphOwner {
interface Callback : Plugin {
fun onOpenBugReport()
fun onAddAccount()
fun navigateToBugReport()
fun navigateToAddAccount()
}
private val callback: Callback = callback()
@Parcelize
object NavTarget : Parcelable
@@ -81,12 +84,12 @@ class LoggedInAppScopeFlowNode(
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
val callback = object : LoggedInFlowNode.Callback {
override fun onOpenBugReport() {
plugins<Callback>().forEach { it.onOpenBugReport() }
override fun navigateToBugReport() {
callback.navigateToBugReport()
}
override fun onAddAccount() {
plugins<Callback>().forEach { it.onAddAccount() }
override fun navigateToAddAccount() {
callback.navigateToAddAccount()
}
}
return createNode<LoggedInFlowNode>(buildContext, listOf(callback))

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
@@ -13,6 +14,7 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.lifecycle.lifecycleScope
import com.bumble.appyx.core.composable.PermanentChild
@@ -23,7 +25,6 @@ import com.bumble.appyx.core.navigation.NavKey
import com.bumble.appyx.core.navigation.model.permanent.PermanentNavModel
import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.plugin.Plugin
import com.bumble.appyx.core.plugin.plugins
import com.bumble.appyx.navmodel.backstack.BackStack
import com.bumble.appyx.navmodel.backstack.BackStack.State.ACTIVE
import com.bumble.appyx.navmodel.backstack.BackStack.State.CREATED
@@ -38,7 +39,6 @@ import com.bumble.appyx.navmodel.backstack.operation.replace
import com.bumble.appyx.navmodel.backstack.operation.singleTop
import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject
import im.vector.app.features.analytics.plan.JoinedRoom
import io.element.android.annotations.ContributesNode
import io.element.android.appnav.loggedin.LoggedInNode
import io.element.android.appnav.loggedin.MediaPreviewConfigMigration
@@ -46,26 +46,33 @@ import io.element.android.appnav.loggedin.SendQueues
import io.element.android.appnav.room.RoomFlowNode
import io.element.android.appnav.room.RoomNavigationTarget
import io.element.android.appnav.room.joined.JoinedRoomLoadedFlowNode
import io.element.android.compound.colors.SemanticColorsLightDark
import io.element.android.features.enterprise.api.EnterpriseService
import io.element.android.features.enterprise.api.SessionEnterpriseService
import io.element.android.features.ftue.api.FtueEntryPoint
import io.element.android.features.ftue.api.state.FtueService
import io.element.android.features.ftue.api.state.FtueState
import io.element.android.features.home.api.HomeEntryPoint
import io.element.android.features.linknewdevice.api.LinkNewDeviceEntryPoint
import io.element.android.features.networkmonitor.api.NetworkMonitor
import io.element.android.features.networkmonitor.api.NetworkStatus
import io.element.android.features.networkmonitor.api.ui.ConnectivityIndicatorContainer
import io.element.android.features.preferences.api.PreferencesEntryPoint
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.space.api.SpaceEntryPoint
import io.element.android.features.startchat.api.StartChatEntryPoint
import io.element.android.features.userprofile.api.UserProfileEntryPoint
import io.element.android.features.verifysession.api.IncomingVerificationEntryPoint
import io.element.android.libraries.architecture.BackstackView
import io.element.android.libraries.architecture.BaseFlowNode
import io.element.android.libraries.architecture.callback
import io.element.android.libraries.architecture.createNode
import io.element.android.libraries.architecture.waitForChildAttached
import io.element.android.libraries.architecture.waitForNavTargetAttached
import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.designsystem.theme.ElementThemeApp
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.di.annotations.SessionCoroutineScope
@@ -77,10 +84,16 @@ import io.element.android.libraries.matrix.api.core.RoomIdOrAlias
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias
import io.element.android.libraries.matrix.api.permalink.PermalinkData
import io.element.android.libraries.matrix.api.room.JoinedRoom
import io.element.android.libraries.matrix.api.sync.SyncService
import io.element.android.libraries.matrix.api.verification.SessionVerificationServiceListener
import io.element.android.libraries.matrix.api.verification.VerificationRequest
import io.element.android.libraries.preferences.api.store.AppPreferencesStore
import io.element.android.libraries.push.api.notifications.conversations.NotificationConversationService
import io.element.android.libraries.ui.common.nodes.emptyNode
import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction
import io.element.android.services.analytics.api.AnalyticsService
import io.element.android.services.analytics.api.watchers.AnalyticsRoomListStateWatcher
import io.element.android.services.appnavstate.api.AppNavigationStateService
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.first
@@ -97,6 +110,7 @@ import java.util.UUID
import kotlin.time.Duration.Companion.minutes
import kotlin.time.Duration.Companion.seconds
import kotlin.time.toKotlinDuration
import im.vector.app.features.analytics.plan.JoinedRoom as JoinedRoomAnalyticsEvent
@ContributesNode(SessionScope::class)
@AssistedInject
@@ -110,6 +124,7 @@ class LoggedInFlowNode(
private val secureBackupEntryPoint: SecureBackupEntryPoint,
private val userProfileEntryPoint: UserProfileEntryPoint,
private val ftueEntryPoint: FtueEntryPoint,
private val linkNewDeviceEntryPoint: LinkNewDeviceEntryPoint,
@SessionCoroutineScope
private val sessionCoroutineScope: CoroutineScope,
private val ftueService: FtueService,
@@ -122,7 +137,13 @@ class LoggedInFlowNode(
private val sessionEnterpriseService: SessionEnterpriseService,
private val networkMonitor: NetworkMonitor,
private val notificationConversationService: NotificationConversationService,
private val syncService: SyncService,
private val enterpriseService: EnterpriseService,
private val appPreferencesStore: AppPreferencesStore,
private val buildMeta: BuildMeta,
snackbarDispatcher: SnackbarDispatcher,
private val analyticsService: AnalyticsService,
private val analyticsRoomListStateWatcher: AnalyticsRoomListStateWatcher,
) : BaseFlowNode<LoggedInFlowNode.NavTarget>(
backstack = BackStack(
initialElement = NavTarget.Placeholder,
@@ -136,10 +157,11 @@ class LoggedInFlowNode(
plugins = plugins
) {
interface Callback : Plugin {
fun onOpenBugReport()
fun onAddAccount()
fun navigateToBugReport()
fun navigateToAddAccount()
}
private val callback: Callback = callback()
private val loggedInFlowProcessor = LoggedInEventProcessor(
snackbarDispatcher = snackbarDispatcher,
roomMembershipObserver = matrixClient.roomMembershipObserver,
@@ -185,6 +207,7 @@ class LoggedInFlowNode(
}
lifecycle.subscribe(
onCreate = {
analyticsRoomListStateWatcher.start()
appNavigationStateService.onNavigateToSession(id, matrixClient.sessionId)
// TODO We do not support Space yet, so directly navigate to main space
appNavigationStateService.onNavigateToSpace(id, MAIN_SPACE)
@@ -198,6 +221,8 @@ class LoggedInFlowNode(
matrixClient.getMaxFileUploadSize()
}
analyticsService.startLongRunningTransaction(AnalyticsLongRunningTransaction.FirstRoomsDisplayed)
ftueService.state
.onEach { ftueState ->
when (ftueState) {
@@ -219,6 +244,7 @@ class LoggedInFlowNode(
appNavigationStateService.onLeavingSession(id)
loggedInFlowProcessor.stopObserving()
matrixClient.sessionVerificationService.setListener(null)
analyticsRoomListStateWatcher.stop()
}
)
setupSendingQueue()
@@ -242,9 +268,9 @@ class LoggedInFlowNode(
data class Room(
val roomIdOrAlias: RoomIdOrAlias,
val serverNames: List<String> = emptyList(),
val trigger: JoinedRoom.Trigger? = null,
val trigger: JoinedRoomAnalyticsEvent.Trigger? = null,
val roomDescription: RoomDescription? = null,
val initialElement: RoomNavigationTarget = RoomNavigationTarget.Messages(),
val initialElement: RoomNavigationTarget = RoomNavigationTarget.Root(),
val targetId: UUID = UUID.randomUUID(),
) : NavTarget
@@ -270,7 +296,10 @@ class LoggedInFlowNode(
data object Ftue : NavTarget
@Parcelize
data object RoomDirectorySearch : NavTarget
data object LinkNewDevice : NavTarget
@Parcelize
data object RoomDirectory : NavTarget
@Parcelize
data class IncomingShare(val intent: Intent) : NavTarget
@@ -292,50 +321,52 @@ class LoggedInFlowNode(
}
NavTarget.Home -> {
val callback = object : HomeEntryPoint.Callback {
override fun onRoomClick(roomId: RoomId) {
backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias()))
override fun navigateToRoom(roomId: RoomId, joinedRoom: JoinedRoom?) {
backstack.push(
NavTarget.Room(
roomIdOrAlias = roomId.toRoomIdOrAlias(),
initialElement = RoomNavigationTarget.Root(joinedRoom = joinedRoom)
)
)
}
override fun onSettingsClick() {
override fun navigateToSettings() {
backstack.push(NavTarget.Settings())
}
override fun onStartChatClick() {
override fun navigateToCreateRoom() {
backstack.push(NavTarget.CreateRoom)
}
override fun onSetUpRecoveryClick() {
override fun navigateToSetUpRecovery() {
backstack.push(NavTarget.SecureBackup(initialElement = SecureBackupEntryPoint.InitialTarget.Root))
}
override fun onSessionConfirmRecoveryKeyClick() {
override fun navigateToEnterRecoveryKey() {
backstack.push(NavTarget.SecureBackup(initialElement = SecureBackupEntryPoint.InitialTarget.EnterRecoveryKey))
}
override fun onRoomSettingsClick(roomId: RoomId) {
override fun navigateToRoomSettings(roomId: RoomId) {
backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias(), initialElement = RoomNavigationTarget.Details))
}
override fun onReportBugClick() {
plugins<Callback>().forEach { it.onOpenBugReport() }
override fun navigateToBugReport() {
callback.navigateToBugReport()
}
}
homeEntryPoint
.nodeBuilder(this, buildContext)
.callback(callback)
.build()
homeEntryPoint.createNode(
parentNode = this,
buildContext = buildContext,
callback = callback,
)
}
is NavTarget.Room -> {
val joinedRoomCallback = object : JoinedRoomLoadedFlowNode.Callback {
override fun onOpenRoom(roomId: RoomId, serverNames: List<String>) {
override fun navigateToRoom(roomId: RoomId, serverNames: List<String>) {
backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias(), serverNames))
}
override fun onForwardedToSingleRoom(roomId: RoomId) {
sessionCoroutineScope.launch { attachRoom(roomId.toRoomIdOrAlias(), clearBackstack = false) }
}
override fun onPermalinkClick(data: PermalinkData, pushToBackstack: Boolean) {
override fun handlePermalinkClick(data: PermalinkData, pushToBackstack: Boolean) {
when (data) {
is PermalinkData.UserLink -> {
// Should not happen (handled by MessagesNode)
@@ -345,8 +376,8 @@ class LoggedInFlowNode(
val target = NavTarget.Room(
roomIdOrAlias = data.roomIdOrAlias,
serverNames = data.viaParameters,
trigger = JoinedRoom.Trigger.Timeline,
initialElement = RoomNavigationTarget.Messages(data.eventId),
trigger = JoinedRoomAnalyticsEvent.Trigger.Timeline,
initialElement = RoomNavigationTarget.Root(data.eventId),
)
if (pushToBackstack) {
backstack.push(target)
@@ -361,15 +392,10 @@ class LoggedInFlowNode(
}
}
override fun onOpenGlobalNotificationSettings() {
override fun navigateToGlobalNotificationSettings() {
backstack.push(NavTarget.Settings(PreferencesEntryPoint.InitialTarget.NotificationSettings))
}
}
val spaceCallback = object : SpaceEntryPoint.Callback {
override fun onOpenRoom(roomId: RoomId, viaParameters: List<String>) {
backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias(), serverNames = viaParameters))
}
}
val inputs = RoomFlowNode.Inputs(
roomIdOrAlias = navTarget.roomIdOrAlias,
roomDescription = Optional.ofNullable(navTarget.roomDescription),
@@ -377,114 +403,138 @@ class LoggedInFlowNode(
trigger = Optional.ofNullable(navTarget.trigger),
initialElement = navTarget.initialElement
)
createNode<RoomFlowNode>(buildContext, plugins = listOf(inputs, joinedRoomCallback, spaceCallback))
createNode<RoomFlowNode>(buildContext, plugins = listOf(inputs, joinedRoomCallback))
}
is NavTarget.UserProfile -> {
val callback = object : UserProfileEntryPoint.Callback {
override fun onOpenRoom(roomId: RoomId) {
override fun navigateToRoom(roomId: RoomId) {
backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias()))
}
}
userProfileEntryPoint.nodeBuilder(this, buildContext)
.params(UserProfileEntryPoint.Params(userId = navTarget.userId))
.callback(callback)
.build()
userProfileEntryPoint.createNode(
parentNode = this,
buildContext = buildContext,
params = UserProfileEntryPoint.Params(userId = navTarget.userId),
callback = callback,
)
}
is NavTarget.Settings -> {
val callback = object : PreferencesEntryPoint.Callback {
override fun onAddAccount() {
plugins<Callback>().forEach { it.onAddAccount() }
override fun navigateToAddAccount() {
callback.navigateToAddAccount()
}
override fun onOpenBugReport() {
plugins<Callback>().forEach { it.onOpenBugReport() }
override fun navigateToLinkNewDevice() {
backstack.push(NavTarget.LinkNewDevice)
}
override fun onSecureBackupClick() {
override fun navigateToBugReport() {
callback.navigateToBugReport()
}
override fun navigateToSecureBackup() {
backstack.push(NavTarget.SecureBackup())
}
override fun onOpenRoomNotificationSettings(roomId: RoomId) {
override fun navigateToRoomNotificationSettings(roomId: RoomId) {
backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias(), initialElement = RoomNavigationTarget.NotificationSettings))
}
override fun navigateTo(roomId: RoomId, eventId: EventId) {
backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias(), initialElement = RoomNavigationTarget.Messages(eventId)))
override fun navigateToEvent(roomId: RoomId, eventId: EventId) {
backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias(), initialElement = RoomNavigationTarget.Root(eventId)))
}
}
val inputs = PreferencesEntryPoint.Params(navTarget.initialElement)
preferencesEntryPoint.nodeBuilder(this, buildContext)
.params(inputs)
.callback(callback)
.build()
preferencesEntryPoint.createNode(
parentNode = this,
buildContext = buildContext,
params = inputs,
callback = callback,
)
}
NavTarget.CreateRoom -> {
val callback = object : StartChatEntryPoint.Callback {
override fun onOpenRoom(roomIdOrAlias: RoomIdOrAlias, serverNames: List<String>) {
override fun onRoomCreated(roomIdOrAlias: RoomIdOrAlias, serverNames: List<String>) {
backstack.replace(NavTarget.Room(roomIdOrAlias = roomIdOrAlias, serverNames = serverNames))
}
override fun onOpenRoomDirectory() {
backstack.push(NavTarget.RoomDirectorySearch)
override fun navigateToRoomDirectory() {
backstack.push(NavTarget.RoomDirectory)
}
}
startChatEntryPoint
.nodeBuilder(this, buildContext)
.callback(callback)
.build()
startChatEntryPoint.createNode(
parentNode = this,
buildContext = buildContext,
callback = callback,
)
}
is NavTarget.SecureBackup -> {
secureBackupEntryPoint.nodeBuilder(this, buildContext)
.params(SecureBackupEntryPoint.Params(initialElement = navTarget.initialElement))
.callback(object : SecureBackupEntryPoint.Callback {
secureBackupEntryPoint.createNode(
parentNode = this,
buildContext = buildContext,
params = SecureBackupEntryPoint.Params(initialElement = navTarget.initialElement),
callback = object : SecureBackupEntryPoint.Callback {
override fun onDone() {
backstack.pop()
}
})
.build()
},
)
}
NavTarget.Ftue -> {
ftueEntryPoint.createNode(this, buildContext)
}
NavTarget.RoomDirectorySearch -> {
roomDirectoryEntryPoint.nodeBuilder(this, buildContext)
.callback(object : RoomDirectoryEntryPoint.Callback {
override fun onResultClick(roomDescription: RoomDescription) {
NavTarget.LinkNewDevice -> {
val callback = object : LinkNewDeviceEntryPoint.Callback {
override fun onDone() {
backstack.pop()
}
}
linkNewDeviceEntryPoint.createNode(this, buildContext, callback)
}
NavTarget.RoomDirectory -> {
roomDirectoryEntryPoint.createNode(
parentNode = this,
buildContext = buildContext,
callback = object : RoomDirectoryEntryPoint.Callback {
override fun navigateToRoom(roomDescription: RoomDescription) {
backstack.push(
NavTarget.Room(
roomIdOrAlias = roomDescription.roomId.toRoomIdOrAlias(),
roomDescription = roomDescription,
trigger = JoinedRoom.Trigger.RoomDirectory,
trigger = JoinedRoomAnalyticsEvent.Trigger.RoomDirectory,
)
)
}
})
.build()
},
)
}
is NavTarget.IncomingShare -> {
shareEntryPoint.nodeBuilder(this, buildContext)
.callback(object : ShareEntryPoint.Callback {
shareEntryPoint.createNode(
parentNode = this,
buildContext = buildContext,
params = ShareEntryPoint.Params(intent = navTarget.intent),
callback = object : ShareEntryPoint.Callback {
override fun onDone(roomIds: List<RoomId>) {
navigateUp()
if (roomIds.size == 1) {
val targetRoomId = roomIds.first()
backstack.push(NavTarget.Room(targetRoomId.toRoomIdOrAlias()))
backstack.pop()
roomIds.singleOrNull()?.let { roomId ->
backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias()))
}
}
})
.params(ShareEntryPoint.Params(intent = navTarget.intent))
.build()
},
)
}
is NavTarget.IncomingVerificationRequest -> {
incomingVerificationEntryPoint.nodeBuilder(this, buildContext)
.params(IncomingVerificationEntryPoint.Params(navTarget.data))
.callback(object : IncomingVerificationEntryPoint.Callback {
incomingVerificationEntryPoint.createNode(
parentNode = this,
buildContext = buildContext,
params = IncomingVerificationEntryPoint.Params(navTarget.data),
callback = object : IncomingVerificationEntryPoint.Callback {
override fun onDone() {
backstack.pop()
}
})
.build()
},
)
}
}
}
@@ -492,10 +542,10 @@ class LoggedInFlowNode(
suspend fun attachRoom(
roomIdOrAlias: RoomIdOrAlias,
serverNames: List<String> = emptyList(),
trigger: JoinedRoom.Trigger? = null,
trigger: JoinedRoomAnalyticsEvent.Trigger? = null,
eventId: EventId? = null,
clearBackstack: Boolean,
) {
): RoomFlowNode {
waitForNavTargetAttached { navTarget ->
navTarget is NavTarget.Home
}
@@ -504,12 +554,19 @@ class LoggedInFlowNode(
roomIdOrAlias = roomIdOrAlias,
serverNames = serverNames,
trigger = trigger,
initialElement = RoomNavigationTarget.Messages(
focusedEventId = eventId
)
initialElement = RoomNavigationTarget.Root(eventId = eventId)
)
backstack.accept(AttachRoomOperation(roomNavTarget, clearBackstack))
}
// If we don't do this check, we might be returning while a previous node with the same type is still displayed
// This means we may attach some new nodes to that one, which will be quickly replaced by the one instantiated above
return waitForChildAttached<RoomFlowNode, NavTarget> {
it is NavTarget.Room &&
it.roomIdOrAlias == roomIdOrAlias &&
it.initialElement is RoomNavigationTarget.Root &&
it.initialElement.eventId == eventId
}
}
suspend fun attachUser(userId: UserId) {
@@ -538,11 +595,27 @@ class LoggedInFlowNode(
@Composable
override fun View(modifier: Modifier) {
Box(modifier = modifier) {
val ftueState by ftueService.state.collectAsState()
BackstackView()
if (ftueState is FtueState.Complete) {
PermanentChild(permanentNavModel = permanentNavModel, navTarget = NavTarget.LoggedInPermanent)
val colors by remember {
enterpriseService.semanticColorsFlow(sessionId = matrixClient.sessionId)
}.collectAsState(SemanticColorsLightDark.default)
ElementThemeApp(
appPreferencesStore = appPreferencesStore,
compoundLight = colors.light,
compoundDark = colors.dark,
buildMeta = buildMeta,
) {
val isOnline by syncService.isOnline.collectAsState()
ConnectivityIndicatorContainer(
isOnline = isOnline,
modifier = modifier,
) { contentModifier ->
Box(modifier = contentModifier) {
val ftueState by ftueService.state.collectAsState()
BackstackView()
if (ftueState is FtueState.Complete) {
PermanentChild(permanentNavModel = permanentNavModel, navTarget = NavTarget.LoggedInPermanent)
}
}
}
}
}

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
@@ -18,7 +19,6 @@ import com.bumble.appyx.core.lifecycle.subscribe
import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.plugin.Plugin
import com.bumble.appyx.core.plugin.plugins
import com.bumble.appyx.navmodel.backstack.BackStack
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.Assisted
@@ -29,10 +29,12 @@ import io.element.android.features.login.api.LoginParams
import io.element.android.libraries.architecture.BackstackView
import io.element.android.libraries.architecture.BaseFlowNode
import io.element.android.libraries.architecture.NodeInputs
import io.element.android.libraries.architecture.callback
import io.element.android.libraries.architecture.inputs
import io.element.android.libraries.designsystem.utils.ForceOrientationInMobileDevices
import io.element.android.libraries.designsystem.utils.ScreenOrientation
import io.element.android.libraries.matrix.ui.media.NotLoggedInImageLoaderFactory
import io.element.android.libraries.matrix.ui.media.ImageLoaderHolder
import io.element.android.services.analytics.api.watchers.AnalyticsColdStartWatcher
import kotlinx.parcelize.Parcelize
@ContributesNode(AppScope::class)
@@ -41,7 +43,8 @@ class NotLoggedInFlowNode(
@Assisted buildContext: BuildContext,
@Assisted plugins: List<Plugin>,
private val loginEntryPoint: LoginEntryPoint,
private val notLoggedInImageLoaderFactory: NotLoggedInImageLoaderFactory,
private val imageLoaderHolder: ImageLoaderHolder,
private val analyticsColdStartWatcher: AnalyticsColdStartWatcher,
) : BaseFlowNode<NotLoggedInFlowNode.NavTarget>(
backstack = BackStack(
initialElement = NavTarget.Root,
@@ -55,16 +58,19 @@ class NotLoggedInFlowNode(
) : NodeInputs
interface Callback : Plugin {
fun onOpenBugReport()
fun navigateToBugReport()
fun onDone()
}
private val callback: Callback = callback()
private val inputs = inputs<Params>()
override fun onBuilt() {
super.onBuilt()
analyticsColdStartWatcher.whenLoggingIn()
lifecycle.subscribe(
onResume = {
SingletonImageLoader.setUnsafe(notLoggedInImageLoaderFactory.newImageLoader())
SingletonImageLoader.setUnsafe(imageLoaderHolder.get())
},
)
}
@@ -78,20 +84,23 @@ class NotLoggedInFlowNode(
return when (navTarget) {
NavTarget.Root -> {
val callback = object : LoginEntryPoint.Callback {
override fun onReportProblem() {
plugins<Callback>().forEach { it.onOpenBugReport() }
override fun navigateToBugReport() {
callback.navigateToBugReport()
}
override fun onDone() {
callback.onDone()
}
}
loginEntryPoint
.nodeBuilder(this, buildContext)
.params(
LoginEntryPoint.Params(
accountProvider = inputs.loginParams?.accountProvider,
loginHint = inputs.loginParams?.loginHint,
)
)
.callback(callback)
.build()
loginEntryPoint.createNode(
parentNode = this,
buildContext = buildContext,
params = LoginEntryPoint.Params(
accountProvider = inputs.loginParams?.accountProvider,
loginHint = inputs.loginParams?.loginHint,
),
callback = callback,
)
}
}
}

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
@@ -31,6 +32,7 @@ import io.element.android.annotations.ContributesNode
import io.element.android.appnav.di.MatrixSessionCache
import io.element.android.appnav.intent.IntentResolver
import io.element.android.appnav.intent.ResolvedIntent
import io.element.android.appnav.room.RoomFlowNode
import io.element.android.appnav.root.RootNavStateFlowFactory
import io.element.android.appnav.root.RootPresenter
import io.element.android.appnav.root.RootView
@@ -38,7 +40,6 @@ 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.rageshake.api.reporter.BugReporter
import io.element.android.features.signedout.api.SignedOutEntryPoint
import io.element.android.libraries.accountselect.api.AccountSelectEntryPoint
import io.element.android.libraries.architecture.BackstackView
@@ -50,7 +51,10 @@ import io.element.android.libraries.core.uri.ensureProtocol
import io.element.android.libraries.deeplink.api.DeeplinkData
import io.element.android.libraries.featureflag.api.FeatureFlagService
import io.element.android.libraries.featureflag.api.FeatureFlags
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.core.ThreadId
import io.element.android.libraries.matrix.api.core.asEventId
import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias
import io.element.android.libraries.matrix.api.permalink.PermalinkData
import io.element.android.libraries.oidc.api.OidcAction
@@ -58,6 +62,10 @@ import io.element.android.libraries.oidc.api.OidcActionFlow
import io.element.android.libraries.sessionstorage.api.LoggedInState
import io.element.android.libraries.sessionstorage.api.SessionStore
import io.element.android.libraries.ui.common.nodes.emptyNode
import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction
import io.element.android.services.analytics.api.AnalyticsService
import io.element.android.services.analytics.api.watchers.AnalyticsColdStartWatcher
import io.element.android.services.appnavstate.api.ROOM_OPENED_FROM_NOTIFICATION
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@@ -80,9 +88,10 @@ class RootFlowNode(
private val accountSelectEntryPoint: AccountSelectEntryPoint,
private val intentResolver: IntentResolver,
private val oidcActionFlow: OidcActionFlow,
private val bugReporter: BugReporter,
private val featureFlagService: FeatureFlagService,
private val announcementService: AnnouncementService,
private val analyticsService: AnalyticsService,
private val analyticsColdStartWatcher: AnalyticsColdStartWatcher,
) : BaseFlowNode<RootFlowNode.NavTarget>(
backstack = BackStack(
initialElement = NavTarget.SplashScreen,
@@ -92,6 +101,7 @@ class RootFlowNode(
plugins = plugins
) {
override fun onBuilt() {
analyticsColdStartWatcher.start()
matrixSessionCache.restoreWithSavedState(buildContext.savedStateMap)
super.onBuilt()
observeNavState()
@@ -130,7 +140,6 @@ class RootFlowNode(
private fun switchToNotLoggedInFlow(params: LoginParams?) {
matrixSessionCache.removeAll()
bugReporter.setLogDirectorySubfolder(null)
backstack.safeRoot(NavTarget.NotLoggedInFlow(params))
}
@@ -226,11 +235,11 @@ class RootFlowNode(
}
val inputs = LoggedInAppScopeFlowNode.Inputs(matrixClient)
val callback = object : LoggedInAppScopeFlowNode.Callback {
override fun onOpenBugReport() {
override fun navigateToBugReport() {
backstack.push(NavTarget.BugReport)
}
override fun onAddAccount() {
override fun navigateToAddAccount() {
backstack.push(NavTarget.NotLoggedInFlow(null))
}
}
@@ -238,9 +247,13 @@ class RootFlowNode(
}
is NavTarget.NotLoggedInFlow -> {
val callback = object : NotLoggedInFlowNode.Callback {
override fun onOpenBugReport() {
override fun navigateToBugReport() {
backstack.push(NavTarget.BugReport)
}
override fun onDone() {
backstack.pop()
}
}
val params = NotLoggedInFlowNode.Params(
loginParams = navTarget.params,
@@ -248,11 +261,13 @@ class RootFlowNode(
createNode<NotLoggedInFlowNode>(buildContext, plugins = listOf(params, callback))
}
is NavTarget.SignedOutFlow -> {
signedOutEntryPoint.nodeBuilder(this, buildContext).params(
SignedOutEntryPoint.Params(
sessionId = navTarget.sessionId
)
).build()
signedOutEntryPoint.createNode(
parentNode = this,
buildContext = buildContext,
params = SignedOutEntryPoint.Params(
sessionId = navTarget.sessionId,
),
)
}
NavTarget.SplashScreen -> emptyNode(buildContext)
NavTarget.BugReport -> {
@@ -261,11 +276,15 @@ class RootFlowNode(
backstack.pop()
}
}
bugReportEntryPoint.nodeBuilder(this, buildContext).callback(callback).build()
bugReportEntryPoint.createNode(
parentNode = this,
buildContext = buildContext,
callback = callback,
)
}
is NavTarget.AccountSelect -> {
val callback: AccountSelectEntryPoint.Callback = object : AccountSelectEntryPoint.Callback {
override fun onSelectAccount(sessionId: SessionId) {
override fun onAccountSelected(sessionId: SessionId) {
lifecycleScope.launch {
if (sessionId == navTarget.currentSessionId) {
// Ensure that the account selection Node is removed from the backstack
@@ -286,7 +305,11 @@ class RootFlowNode(
backstack.pop()
}
}
accountSelectEntryPoint.nodeBuilder(this, buildContext).callback(callback).build()
accountSelectEntryPoint.createNode(
parentNode = this,
buildContext = buildContext,
callback = callback,
)
}
}
}
@@ -294,7 +317,13 @@ class RootFlowNode(
suspend fun handleIntent(intent: Intent) {
val resolvedIntent = intentResolver.resolve(intent) ?: return
when (resolvedIntent) {
is ResolvedIntent.Navigation -> navigateTo(resolvedIntent.deeplinkData)
is ResolvedIntent.Navigation -> {
val openingRoomFromNotification = intent.getBooleanExtra(ROOM_OPENED_FROM_NOTIFICATION, false)
if (openingRoomFromNotification && resolvedIntent.deeplinkData is DeeplinkData.Room) {
analyticsService.startLongRunningTransaction(AnalyticsLongRunningTransaction.NotificationTapOpensTimeline)
}
navigateTo(resolvedIntent.deeplinkData)
}
is ResolvedIntent.Login -> onLoginLink(resolvedIntent.params)
is ResolvedIntent.Oidc -> onOidcAction(resolvedIntent.oidcAction)
is ResolvedIntent.Permalink -> navigateTo(resolvedIntent.permalinkData)
@@ -338,7 +367,7 @@ class RootFlowNode(
} else {
// wait for the current session to be restored
val loggedInFlowNode = attachSession(latestSessionId)
if (sessionStore.getAllSessions().size > 1) {
if (sessionStore.numberOfSessions() > 1) {
// Several accounts, let the user choose which one to use
backstack.push(
NavTarget.AccountSelect(
@@ -368,7 +397,7 @@ class RootFlowNode(
is PermalinkData.FallbackLink -> Unit
is PermalinkData.RoomEmailInviteLink -> Unit
else -> {
if (sessionStore.getAllSessions().size > 1) {
if (sessionStore.numberOfSessions() > 1) {
// Several accounts, let the user choose which one to use
backstack.push(
NavTarget.AccountSelect(
@@ -391,13 +420,19 @@ class RootFlowNode(
is PermalinkData.FallbackLink -> Unit
is PermalinkData.RoomEmailInviteLink -> Unit
is PermalinkData.RoomLink -> {
// If there is a thread id, focus on it in the main timeline
val focusedEventId = if (permalinkData.threadId != null) {
permalinkData.threadId?.asEventId()
} else {
permalinkData.eventId
}
attachRoom(
roomIdOrAlias = permalinkData.roomIdOrAlias,
trigger = JoinedRoom.Trigger.MobilePermalink,
serverNames = permalinkData.viaParameters,
eventId = permalinkData.eventId,
eventId = focusedEventId,
clearBackstack = true
)
).maybeAttachThread(permalinkData.threadId, permalinkData.eventId)
}
is PermalinkData.UserLink -> {
attachUser(permalinkData.userId)
@@ -405,12 +440,24 @@ class RootFlowNode(
}
}
private suspend fun RoomFlowNode.maybeAttachThread(threadId: ThreadId?, focusedEventId: EventId?) {
if (threadId != null) {
attachThread(threadId, focusedEventId)
}
}
private suspend fun navigateTo(deeplinkData: DeeplinkData) {
Timber.d("Navigating to $deeplinkData")
attachSession(deeplinkData.sessionId).apply {
attachSession(deeplinkData.sessionId).let { loggedInFlowNode ->
when (deeplinkData) {
is DeeplinkData.Root -> Unit // The room list will always be shown, observing FtueState
is DeeplinkData.Room -> attachRoom(deeplinkData.roomId.toRoomIdOrAlias(), clearBackstack = true)
is DeeplinkData.Room -> {
loggedInFlowNode.attachRoom(
roomIdOrAlias = deeplinkData.roomId.toRoomIdOrAlias(),
eventId = if (deeplinkData.threadId != null) deeplinkData.threadId?.asEventId() else deeplinkData.eventId,
clearBackstack = true,
).maybeAttachThread(deeplinkData.threadId, deeplinkData.eventId)
}
}
}
}

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
@@ -12,12 +13,14 @@ import com.bumble.appyx.core.state.MutableSavedStateMap
import com.bumble.appyx.core.state.SavedStateMap
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesBinding
import dev.zacsweers.metro.Inject
import dev.zacsweers.metro.SingleIn
import io.element.android.libraries.androidutils.hash.hash
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.MatrixClientProvider
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.services.analytics.api.AnalyticsService
import io.element.android.services.analyticsproviders.api.AnalyticsUserData
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
@@ -33,10 +36,10 @@ private const val SAVE_INSTANCE_KEY = "io.element.android.x.di.MatrixClientsHold
*/
@SingleIn(AppScope::class)
@ContributesBinding(AppScope::class)
@Inject
class MatrixSessionCache(
private val authenticationService: MatrixAuthenticationService,
private val syncOrchestratorFactory: SyncOrchestrator.Factory,
private val analyticsService: AnalyticsService,
) : MatrixClientProvider {
private val sessionIdsToMatrixSession = ConcurrentHashMap<SessionId, InMemoryMatrixSession>()
private val restoreMutex = Mutex()
@@ -101,6 +104,11 @@ class MatrixSessionCache(
Timber.d("Restore matrix session: $sessionId")
return authenticationService.restoreSession(sessionId)
.onSuccess { matrixClient ->
// Add the current homeserver (hashed) to the extra info
// This may not play well with multiple sessions, but it should work for now
analyticsService.addIndexableData(AnalyticsUserData.HOMESERVER, matrixClient.userIdServerName().hash())
// Add the new client to the in-memory cache
onNewMatrixClient(matrixClient)
}
.onFailure {

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/

View File

@@ -1,7 +1,8 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
@@ -17,6 +18,9 @@ import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.core.coroutine.childScope
import io.element.android.libraries.matrix.api.sync.SyncService
import io.element.android.libraries.matrix.api.sync.SyncState
import io.element.android.services.analytics.api.AnalyticsService
import io.element.android.services.analytics.api.recordTransaction
import io.element.android.services.analyticsproviders.api.AnalyticsUserData
import io.element.android.services.appnavstate.api.AppForegroundStateService
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.FlowPreview
@@ -38,6 +42,7 @@ class SyncOrchestrator(
private val appForegroundStateService: AppForegroundStateService,
private val networkMonitor: NetworkMonitor,
dispatchers: CoroutineDispatchers,
private val analyticsService: AnalyticsService,
) {
@AssistedFactory
interface Factory {
@@ -68,10 +73,13 @@ class SyncOrchestrator(
// Perform an initial sync if the sync service is not running, to check whether the homeserver is accessible
// Otherwise, if the device is offline the sync service will never start and the SyncState will be Idle, not Offline
Timber.tag(tag).d("performing initial sync attempt")
syncService.startSync()
analyticsService.recordTransaction("First sync", "syncService.startSync()") { transaction ->
syncService.startSync()
// Wait until the sync service is not idle, either it will be running or in error/offline state
syncService.syncState.first { it != SyncState.Idle }
// Wait until the sync service is not idle, either it will be running or in error/offline state
val firstState = syncService.syncState.first { it != SyncState.Idle }
transaction.putIndexableData(AnalyticsUserData.FIRST_SYNC_STATE, firstState.name)
}
observeStates()
}

View File

@@ -0,0 +1,17 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2025 New Vector 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.appnav.di
import io.element.android.features.messages.api.pinned.PinnedEventsTimelineProvider
import io.element.android.libraries.matrix.api.timeline.TimelineProvider
interface TimelineBindings {
val timelineProvider: TimelineProvider
val pinnedEventsTimelineProvider: PinnedEventsTimelineProvider
}

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2024, 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2024, 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
@@ -12,10 +13,10 @@ import androidx.compose.ui.Modifier
import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.plugin.Plugin
import com.bumble.appyx.core.plugin.plugins
import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode
import io.element.android.libraries.architecture.callback
import io.element.android.libraries.di.SessionScope
@ContributesNode(SessionScope::class)
@@ -32,18 +33,14 @@ class LoggedInNode(
fun navigateToNotificationTroubleshoot()
}
private fun navigateToNotificationTroubleshoot() {
plugins<Callback>().forEach {
it.navigateToNotificationTroubleshoot()
}
}
private val callback: Callback = callback()
@Composable
override fun View(modifier: Modifier) {
val loggedInState = loggedInPresenter.present()
LoggedInView(
state = loggedInState,
navigateToNotificationTroubleshoot = ::navigateToNotificationTroubleshoot,
navigateToNotificationTroubleshoot = callback::navigateToNotificationTroubleshoot,
modifier = modifier
)
}

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
@@ -35,7 +36,7 @@ import io.element.android.libraries.matrix.api.sync.SyncService
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus
import io.element.android.libraries.push.api.PushService
import io.element.android.libraries.pushproviders.api.RegistrationFailure
import io.element.android.libraries.push.api.PusherRegistrationFailure
import io.element.android.services.analytics.api.AnalyticsService
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.combine
@@ -70,7 +71,17 @@ class LoggedInPresenter(
when (sessionVerifiedStatus) {
SessionVerifiedStatus.Unknown -> Unit
SessionVerifiedStatus.Verified -> {
ensurePusherIsRegistered(pusherRegistrationState)
Timber.tag(pusherTag.value).d("Ensure pusher is registered")
pushService.ensurePusherIsRegistered(matrixClient).fold(
onSuccess = {
Timber.tag(pusherTag.value).d("Pusher registered")
pusherRegistrationState.value = AsyncData.Success(Unit)
},
onFailure = {
Timber.tag(pusherTag.value).e(it, "Failed to register pusher")
pusherRegistrationState.value = AsyncData.Failure(it)
},
)
}
SessionVerifiedStatus.NotVerified -> {
pusherRegistrationState.value = AsyncData.Failure(PusherRegistrationFailure.AccountNotVerified())
@@ -122,7 +133,7 @@ class LoggedInPresenter(
ignoreRegistrationError = ignoreRegistrationError,
forceNativeSlidingSyncMigration = forceNativeSlidingSyncMigration,
appName = buildMeta.applicationName,
eventSink = ::handleEvent
eventSink = ::handleEvent,
)
}
@@ -132,59 +143,6 @@ class LoggedInPresenter(
currentSlidingSyncVersion == SlidingSyncVersion.Proxy
}
private suspend fun ensurePusherIsRegistered(pusherRegistrationState: MutableState<AsyncData<Unit>>) {
Timber.tag(pusherTag.value).d("Ensure pusher is registered")
val currentPushProvider = pushService.getCurrentPushProvider()
val result = if (currentPushProvider == null) {
Timber.tag(pusherTag.value).d("Register with the first available push provider with at least one distributor")
val pushProvider = pushService.getAvailablePushProviders()
.firstOrNull { it.getDistributors().isNotEmpty() }
// Else fallback to the first available push provider (the list should never be empty)
?: pushService.getAvailablePushProviders().firstOrNull()
?: return Unit
.also { Timber.tag(pusherTag.value).w("No push providers available") }
.also { pusherRegistrationState.value = AsyncData.Failure(PusherRegistrationFailure.NoProvidersAvailable()) }
val distributor = pushProvider.getDistributors().firstOrNull()
?: return Unit
.also { Timber.tag(pusherTag.value).w("No distributors available") }
.also {
// In this case, consider the push provider is chosen.
pushService.selectPushProvider(matrixClient.sessionId, pushProvider)
}
.also { pusherRegistrationState.value = AsyncData.Failure(PusherRegistrationFailure.NoDistributorsAvailable()) }
pushService.registerWith(matrixClient, pushProvider, distributor)
} else {
val currentPushDistributor = currentPushProvider.getCurrentDistributor(matrixClient.sessionId)
if (currentPushDistributor == null) {
Timber.tag(pusherTag.value).d("Register with the first available distributor")
val distributor = currentPushProvider.getDistributors().firstOrNull()
?: return Unit
.also { Timber.tag(pusherTag.value).w("No distributors available") }
.also { pusherRegistrationState.value = AsyncData.Failure(PusherRegistrationFailure.NoDistributorsAvailable()) }
pushService.registerWith(matrixClient, currentPushProvider, distributor)
} else {
Timber.tag(pusherTag.value).d("Re-register with the current distributor")
pushService.registerWith(matrixClient, currentPushProvider, currentPushDistributor)
}
}
result.fold(
onSuccess = {
Timber.tag(pusherTag.value).d("Pusher registered")
pusherRegistrationState.value = AsyncData.Success(Unit)
},
onFailure = {
Timber.tag(pusherTag.value).e(it, "Failed to register pusher")
if (it is RegistrationFailure) {
pusherRegistrationState.value = AsyncData.Failure(
PusherRegistrationFailure.RegistrationFailure(it.clientException, it.isRegisteringAgain)
)
} else {
pusherRegistrationState.value = AsyncData.Failure(it)
}
}
)
}
private fun reportCryptoStatusToAnalytics(verificationState: SessionVerifiedStatus, recoveryState: RecoveryState) {
// Update first the user property, to store the current status for that posthog user
val userVerificationState = verificationState.toAnalyticsUserPropertyValue()

View File

@@ -1,7 +1,8 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/

Some files were not shown because too many files have changed in this diff Show More