Merge tag 'v1.6.20' into sc

Change-Id: Ibf61668b312b1f4b1867ccc6ba4d3174e37550a5

Conflicts:
	library/attachment-viewer/build.gradle
	library/ui-styles/build.gradle
	matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/RealmSessionStoreMigration.kt
	vector/src/main/java/im/vector/app/core/glide/ImageContentRendererDataLoader.kt
	vector/src/main/java/im/vector/app/core/pushers/UnifiedPushHelper.kt
	vector/src/main/java/im/vector/app/core/ui/views/JoinConferenceView.kt
	vector/src/main/java/im/vector/app/features/crypto/verification/IncomingVerificationRequestHandler.kt
	vector/src/main/java/im/vector/app/features/crypto/verification/user/VerificationEpoxyExt.kt
	vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt
	vector/src/main/java/im/vector/app/features/home/HomeDetailViewState.kt
	vector/src/main/java/im/vector/app/features/home/RoomListDisplayMode.kt
	vector/src/main/java/im/vector/app/features/home/room/detail/TimelineFragment.kt
	vector/src/main/java/im/vector/app/features/home/room/detail/composer/PlainTextComposerLayout.kt
	vector/src/main/java/im/vector/app/features/home/room/detail/composer/voice/VoiceMessageViews.kt
	vector/src/main/java/im/vector/app/features/home/room/detail/timeline/edithistory/ViewEditHistoryEpoxyController.kt
	vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/EncryptedItemFactory.kt
	vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/DisplayableEventFormatter.kt
	vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MatrixItemColorProvider.kt
	vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/TimelineMediaSizeProvider.kt
	vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageItem.kt
	vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/BasedMergedItem.kt
	vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageAudioItem.kt
	vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageFileItem.kt
	vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageVoiceItem.kt
	vector/src/main/java/im/vector/app/features/home/room/detail/timeline/reactions/ViewReactionsEpoxyController.kt
	vector/src/main/java/im/vector/app/features/home/room/list/RoomCategoryItem.kt
	vector/src/main/java/im/vector/app/features/home/room/list/RoomListFooterController.kt
	vector/src/main/java/im/vector/app/features/home/room/list/RoomListFragment.kt
	vector/src/main/java/im/vector/app/features/home/room/list/RoomListSectionBuilder.kt
	vector/src/main/java/im/vector/app/features/home/room/list/SectionHeaderAdapter.kt
	vector/src/main/java/im/vector/app/features/home/room/list/actions/RoomListQuickActionsEpoxyController.kt
	vector/src/main/java/im/vector/app/features/home/room/threads/list/model/ThreadListItem.kt
	vector/src/main/java/im/vector/app/features/link/LinkHandlerActivity.kt
	vector/src/main/java/im/vector/app/features/login/LoginFragment.kt
	vector/src/main/java/im/vector/app/features/login/LoginServerSelectionFragment.kt
	vector/src/main/java/im/vector/app/features/login/LoginSplashFragment.kt
	vector/src/main/java/im/vector/app/features/media/ImageContentRenderer.kt
	vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt
	vector/src/main/java/im/vector/app/features/poll/create/CreatePollController.kt
	vector/src/main/java/im/vector/app/features/rageshake/BugReportActivity.kt
	vector/src/main/java/im/vector/app/features/roomdirectory/createroom/CreateRoomController.kt
	vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt
	vector/src/main/java/im/vector/app/features/roomprofile/members/RoomMemberListViewState.kt
	vector/src/main/java/im/vector/app/features/roomprofile/notifications/RoomNotificationSettingsController.kt
	vector/src/main/java/im/vector/app/features/settings/VectorPreferences.kt
	vector/src/main/java/im/vector/app/features/settings/VectorSettingsAdvancedSettingsFragment.kt
	vector/src/main/java/im/vector/app/features/settings/VectorSettingsPreferencesFragment.kt
	vector/src/main/java/im/vector/app/features/settings/labs/VectorSettingsLabsFragment.kt
	vector/src/main/java/im/vector/app/features/spaces/NewSpaceSummaryController.kt
	vector/src/main/java/im/vector/app/features/spaces/SpaceSummaryController.kt
This commit is contained in:
SpiritCroc 2024-07-30 20:56:50 +02:00
commit 3291517b8b
757 changed files with 5447 additions and 4482 deletions

View File

@ -24,12 +24,17 @@ jobs:
group: ${{ github.ref == 'refs/heads/develop' && format('integration-tests-develop-{0}-{1}', matrix.target, github.sha) || format('build-debug-{0}-{1}', matrix.target, github.ref) }} group: ${{ github.ref == 'refs/heads/develop' && format('integration-tests-develop-{0}-{1}', matrix.target, github.sha) || format('build-debug-{0}-{1}', matrix.target, github.ref) }}
cancel-in-progress: true cancel-in-progress: true
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
with: with:
# https://github.com/actions/checkout/issues/881 # https://github.com/actions/checkout/issues/881
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
- name: Use JDK 17
uses: actions/setup-java@v4
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '17'
- name: Configure gradle - name: Configure gradle
uses: gradle/gradle-build-action@v2 uses: gradle/actions/setup-gradle@v3
with: with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }} cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: Assemble ${{ matrix.target }} debug apk - name: Assemble ${{ matrix.target }} debug apk
@ -48,12 +53,17 @@ jobs:
group: ${{ github.ref == 'refs/head/main' && format('build-release-apk-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('build-release-apk-develop-{0}', github.sha) || format('build-debug-{0}', github.ref) }} group: ${{ github.ref == 'refs/head/main' && format('build-release-apk-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('build-release-apk-develop-{0}', github.sha) || format('build-debug-{0}', github.ref) }}
cancel-in-progress: ${{ github.ref != 'refs/head/main' }} cancel-in-progress: ${{ github.ref != 'refs/head/main' }}
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
with: with:
# https://github.com/actions/checkout/issues/881 # https://github.com/actions/checkout/issues/881
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }} ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.ref }}
- name: Use JDK 17
uses: actions/setup-java@v4
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '17'
- name: Configure gradle - name: Configure gradle
uses: gradle/gradle-build-action@v2 uses: gradle/actions/setup-gradle@v3
with: with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }} cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: Assemble GPlay unsigned apk - name: Assemble GPlay unsigned apk

View File

@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
name: Danger name: Danger
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- run: | - run: |
npm install --save-dev @babel/plugin-transform-flow-strip-types npm install --save-dev @babel/plugin-transform-flow-strip-types
- name: Danger - name: Danger

View File

@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v3 uses: actions/checkout@v4
- name: Build docs - name: Build docs
run: ./gradlew dokkaHtml run: ./gradlew dokkaHtml

View File

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

View File

@ -7,7 +7,7 @@ on:
- cron: "0 4 * * *" - cron: "0 4 * * *"
env: env:
GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx3072m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError -XX:MaxMetaspaceSize=1g" -Dkotlin.daemon.jvm.options="-Xmx2560m" -Dkotlin.incremental=false GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx6g -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError -XX:MaxMetaspaceSize=1g" -Dkotlin.incremental=false -XX:+UseG1GC
CI_GRADLE_ARG_PROPERTIES: --stacktrace -PpreDexEnable=false --max-workers 2 --no-daemon CI_GRADLE_ARG_PROPERTIES: --stacktrace -PpreDexEnable=false --max-workers 2 --no-daemon
jobs: jobs:
@ -15,13 +15,18 @@ jobs:
name: Build and publish nightly Gplay APK to Firebase name: Build and publish nightly Gplay APK to Firebase
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Set up Python 3.8 - name: Set up Python 3.8
uses: actions/setup-python@v4 uses: actions/setup-python@v4
with: with:
python-version: 3.8 python-version: 3.8
- name: Use JDK 17
uses: actions/setup-java@v4
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '17'
- name: Configure gradle - name: Configure gradle
uses: gradle/gradle-build-action@v2 uses: gradle/actions/setup-gradle@v3
with: with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }} cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: Install towncrier - name: Install towncrier

View File

@ -39,17 +39,18 @@ jobs:
api-level: [ 28 ] api-level: [ 28 ]
# No concurrency required, runs every time on a schedule. # No concurrency required, runs every time on a schedule.
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Set up Python 3.8 - name: Set up Python 3.8
uses: actions/setup-python@v4 uses: actions/setup-python@v4
with: with:
python-version: 3.8 python-version: 3.8
- uses: actions/setup-java@v3 - name: Use JDK 17
uses: actions/setup-java@v4
with: with:
distribution: 'adopt' distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '11' java-version: '17'
- name: Configure gradle - name: Configure gradle
uses: gradle/gradle-build-action@v2 uses: gradle/actions/setup-gradle@v3
with: with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }} cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: Start synapse server - name: Start synapse server

View File

@ -7,7 +7,7 @@ on:
# Enrich gradle.properties for CI/CD # Enrich gradle.properties for CI/CD
env: env:
GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx3072m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError" -XX:MaxPermSize=512m -Dkotlin.daemon.jvm.options="-Xmx2g" -Dkotlin.incremental=false GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx3072m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError -XX:MaxMetaspaceSize=1g" -Dkotlin.daemon.jvm.options="-Xmx2560m" -Dkotlin.incremental=false
CI_GRADLE_ARG_PROPERTIES: --stacktrace -PpreDexEnable=false --max-workers 2 --no-daemon CI_GRADLE_ARG_PROPERTIES: --stacktrace -PpreDexEnable=false --max-workers 2 --no-daemon
jobs: jobs:
@ -15,7 +15,7 @@ jobs:
name: Project Check Suite name: Project Check Suite
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Run code quality check suite - name: Run code quality check suite
run: ./tools/check/check_code_quality.sh run: ./tools/check/check_code_quality.sh
@ -24,7 +24,16 @@ jobs:
name: Knit name: Knit
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Use JDK 17
uses: actions/setup-java@v4
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '17'
- name: Configure gradle
uses: gradle/actions/setup-gradle@v3
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: Run knit - name: Run knit
run: | run: |
./gradlew knitCheck $CI_GRADLE_ARG_PROPERTIES ./gradlew knitCheck $CI_GRADLE_ARG_PROPERTIES
@ -38,7 +47,16 @@ jobs:
group: ${{ github.ref == 'refs/heads/main' && format('lint-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('lint-develop-{0}', github.sha) || format('lint-{0}', github.ref) }} group: ${{ github.ref == 'refs/heads/main' && format('lint-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('lint-develop-{0}', github.sha) || format('lint-{0}', github.ref) }}
cancel-in-progress: true cancel-in-progress: true
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Use JDK 17
uses: actions/setup-java@v4
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '17'
- name: Configure gradle
uses: gradle/actions/setup-gradle@v3
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: Run ktlint - name: Run ktlint
run: | run: |
./gradlew ktlintCheck $CI_GRADLE_ARG_PROPERTIES --continue ./gradlew ktlintCheck $CI_GRADLE_ARG_PROPERTIES --continue
@ -83,7 +101,16 @@ jobs:
group: ${{ github.ref == 'refs/heads/main' && format('dep-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('dep-develop-{0}', github.sha) || format('dep-{0}', github.ref) }} group: ${{ github.ref == 'refs/heads/main' && format('dep-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('dep-develop-{0}', github.sha) || format('dep-{0}', github.ref) }}
cancel-in-progress: true cancel-in-progress: true
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Use JDK 17
uses: actions/setup-java@v4
with:
distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '17'
- name: Configure gradle
uses: gradle/actions/setup-gradle@v3
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
- name: Dependency analysis - name: Dependency analysis
run: ./gradlew dependencyCheckAnalyze $CI_GRADLE_ARG_PROPERTIES run: ./gradlew dependencyCheckAnalyze $CI_GRADLE_ARG_PROPERTIES
- name: Upload dependency analysis - name: Upload dependency analysis

View File

@ -12,7 +12,7 @@ jobs:
if: github.repository == 'element-hq/element-android' if: github.repository == 'element-hq/element-android'
# No concurrency required, runs every time on a schedule. # No concurrency required, runs every time on a schedule.
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Set up Python 3.8 - name: Set up Python 3.8
uses: actions/setup-python@v4 uses: actions/setup-python@v4
with: with:
@ -39,7 +39,7 @@ jobs:
if: github.repository == 'element-hq/element-android' if: github.repository == 'element-hq/element-android'
# No concurrency required, runs every time on a schedule. # No concurrency required, runs every time on a schedule.
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Set up Python 3.8 - name: Set up Python 3.8
uses: actions/setup-python@v4 uses: actions/setup-python@v4
with: with:

View File

@ -25,15 +25,17 @@ jobs:
group: ${{ github.ref == 'refs/heads/main' && format('unit-tests-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('unit-tests-develop-{0}', github.sha) || format('unit-tests-{0}', github.ref) }} group: ${{ github.ref == 'refs/heads/main' && format('unit-tests-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('unit-tests-develop-{0}', github.sha) || format('unit-tests-{0}', github.ref) }}
cancel-in-progress: true cancel-in-progress: true
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
with: with:
lfs: true lfs: true
fetch-depth: 0 fetch-depth: 0
- uses: actions/setup-java@v3 - name: Use JDK 17
uses: actions/setup-java@v4
with: with:
distribution: 'adopt' distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '17' java-version: '17'
- uses: gradle/gradle-build-action@v2 - name: Configure gradle
uses: gradle/actions/setup-gradle@v3
with: with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' }} cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
gradle-home-cache-cleanup: ${{ github.ref == 'refs/heads/develop' }} gradle-home-cache-cleanup: ${{ github.ref == 'refs/heads/develop' }}
@ -136,13 +138,14 @@ jobs:
# group: ${{ github.ref == 'refs/heads/main' && format('unit-tests-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('unit-tests-develop-{0}', github.sha) || format('build-android-tests-{0}', github.ref) }} # group: ${{ github.ref == 'refs/heads/main' && format('unit-tests-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('unit-tests-develop-{0}', github.sha) || format('build-android-tests-{0}', github.ref) }}
# cancel-in-progress: true # cancel-in-progress: true
# steps: # steps:
# - uses: actions/checkout@v3 # - uses: actions/checkout@v4
# - uses: actions/setup-java@v3 # - name: Use JDK 17
# uses: actions/setup-java@v4
# with: # with:
# distribution: 'adopt' # distribution: 'temurin' # See 'Supported distributions' for available options
# java-version: 11 # java-version: '17'
# - name: Configure gradle # - name: Configure gradle
# uses: gradle/gradle-build-action@v2 # uses: gradle/actions/setup-gradle@v3
# with: # with:
# cache-read-only: ${{ github.ref != 'refs/heads/develop' }} # cache-read-only: ${{ github.ref != 'refs/heads/develop' }}
# - name: Build Android Tests # - name: Build Android Tests

View File

@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Update Gradle Wrapper - name: Update Gradle Wrapper
uses: gradle-update/update-gradle-wrapper-action@v1 uses: gradle-update/update-gradle-wrapper-action@v1

View File

@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
name: Validate name: Validate
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
with: with:
lfs: 'true' lfs: 'true'

View File

@ -1,3 +1,11 @@
Changes in Element v1.6.20 (2024-07-25)
=======================================
Other changes
-------------
- Bump compile and target SDK to 34 ([#8860](https://github.com/element-hq/element-android/pull/8860)
- Supports Authenticated media apis ([#8868](https://github.com/element-hq/element-android/pull/8868)
Changes in Element v1.6.18 (2024-06-25) Changes in Element v1.6.18 (2024-06-25)
======================================= =======================================

View File

@ -1,29 +1,32 @@
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
CFPropertyList (3.0.5) CFPropertyList (3.0.7)
base64
nkf
rexml rexml
addressable (2.8.0) addressable (2.8.7)
public_suffix (>= 2.0.2, < 5.0) public_suffix (>= 2.0.2, < 7.0)
artifactory (3.0.15) artifactory (3.0.17)
atomos (0.1.3) atomos (0.1.3)
aws-eventstream (1.2.0) aws-eventstream (1.3.0)
aws-partitions (1.619.0) aws-partitions (1.947.0)
aws-sdk-core (3.132.0) aws-sdk-core (3.199.0)
aws-eventstream (~> 1, >= 1.0.2) aws-eventstream (~> 1, >= 1.3.0)
aws-partitions (~> 1, >= 1.525.0) aws-partitions (~> 1, >= 1.651.0)
aws-sigv4 (~> 1.1) aws-sigv4 (~> 1.8)
jmespath (~> 1, >= 1.6.1) jmespath (~> 1, >= 1.6.1)
aws-sdk-kms (1.58.0) aws-sdk-kms (1.87.0)
aws-sdk-core (~> 3, >= 3.127.0) aws-sdk-core (~> 3, >= 3.199.0)
aws-sigv4 (~> 1.1) aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.114.0) aws-sdk-s3 (1.154.0)
aws-sdk-core (~> 3, >= 3.127.0) aws-sdk-core (~> 3, >= 3.199.0)
aws-sdk-kms (~> 1) aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.4) aws-sigv4 (~> 1.8)
aws-sigv4 (1.5.1) aws-sigv4 (1.8.0)
aws-eventstream (~> 1, >= 1.0.2) aws-eventstream (~> 1, >= 1.0.2)
babosa (1.0.4) babosa (1.0.4)
base64 (0.2.0)
claide (1.1.0) claide (1.1.0)
claide-plugins (0.9.2) claide-plugins (0.9.2)
cork cork
@ -49,14 +52,14 @@ GEM
octokit (~> 4.7) octokit (~> 4.7)
terminal-table (>= 1, < 4) terminal-table (>= 1, < 4)
declarative (0.0.20) declarative (0.0.20)
digest-crc (0.6.4) digest-crc (0.6.5)
rake (>= 12.0.0, < 14.0.0) rake (>= 12.0.0, < 14.0.0)
domain_name (0.5.20190701) domain_name (0.5.20190701)
unf (>= 0.0.5, < 1.0.0) unf (>= 0.0.5, < 1.0.0)
dotenv (2.8.1) dotenv (2.8.1)
emoji_regex (3.2.3) emoji_regex (3.2.3)
excon (0.92.4) excon (0.109.0)
faraday (1.10.1) faraday (1.10.3)
faraday-em_http (~> 1.0) faraday-em_http (~> 1.0)
faraday-em_synchrony (~> 1.0) faraday-em_synchrony (~> 1.0)
faraday-excon (~> 1.1) faraday-excon (~> 1.1)
@ -74,7 +77,7 @@ GEM
faraday-em_http (1.0.0) faraday-em_http (1.0.0)
faraday-em_synchrony (1.0.0) faraday-em_synchrony (1.0.0)
faraday-excon (1.1.0) faraday-excon (1.1.0)
faraday-http-cache (2.4.1) faraday-http-cache (2.5.1)
faraday (>= 0.8) faraday (>= 0.8)
faraday-httpclient (1.0.1) faraday-httpclient (1.0.1)
faraday-multipart (1.0.4) faraday-multipart (1.0.4)
@ -86,15 +89,15 @@ GEM
faraday-retry (1.0.3) faraday-retry (1.0.3)
faraday_middleware (1.2.0) faraday_middleware (1.2.0)
faraday (~> 1.0) faraday (~> 1.0)
fastimage (2.2.6) fastimage (2.3.1)
fastlane (2.209.0) fastlane (2.221.1)
CFPropertyList (>= 2.3, < 4.0.0) CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.8, < 3.0.0) addressable (>= 2.8, < 3.0.0)
artifactory (~> 3.0) artifactory (~> 3.0)
aws-sdk-s3 (~> 1.0) aws-sdk-s3 (~> 1.0)
babosa (>= 1.0.3, < 2.0.0) babosa (>= 1.0.3, < 2.0.0)
bundler (>= 1.12.0, < 3.0.0) bundler (>= 1.12.0, < 3.0.0)
colored colored (~> 1.2)
commander (~> 4.6) commander (~> 4.6)
dotenv (>= 2.1.1, < 3.0.0) dotenv (>= 2.1.1, < 3.0.0)
emoji_regex (>= 0.1, < 4.0) emoji_regex (>= 0.1, < 4.0)
@ -106,33 +109,35 @@ GEM
gh_inspector (>= 1.1.2, < 2.0.0) gh_inspector (>= 1.1.2, < 2.0.0)
google-apis-androidpublisher_v3 (~> 0.3) google-apis-androidpublisher_v3 (~> 0.3)
google-apis-playcustomapp_v1 (~> 0.1) google-apis-playcustomapp_v1 (~> 0.1)
google-cloud-env (>= 1.6.0, < 2.0.0)
google-cloud-storage (~> 1.31) google-cloud-storage (~> 1.31)
highline (~> 2.0) highline (~> 2.0)
http-cookie (~> 1.0.5)
json (< 3.0.0) json (< 3.0.0)
jwt (>= 2.1.0, < 3) jwt (>= 2.1.0, < 3)
mini_magick (>= 4.9.4, < 5.0.0) mini_magick (>= 4.9.4, < 5.0.0)
multipart-post (~> 2.0.0) multipart-post (>= 2.0.0, < 3.0.0)
naturally (~> 2.2) naturally (~> 2.2)
optparse (~> 0.1.1) optparse (>= 0.1.1, < 1.0.0)
plist (>= 3.1.0, < 4.0.0) plist (>= 3.1.0, < 4.0.0)
rubyzip (>= 2.0.0, < 3.0.0) rubyzip (>= 2.0.0, < 3.0.0)
security (= 0.1.3) security (= 0.1.5)
simctl (~> 1.6.3) simctl (~> 1.6.3)
terminal-notifier (>= 2.0.0, < 3.0.0) terminal-notifier (>= 2.0.0, < 3.0.0)
terminal-table (>= 1.4.5, < 2.0.0) terminal-table (~> 3)
tty-screen (>= 0.6.3, < 1.0.0) tty-screen (>= 0.6.3, < 1.0.0)
tty-spinner (>= 0.8.0, < 1.0.0) tty-spinner (>= 0.8.0, < 1.0.0)
word_wrap (~> 1.0.0) word_wrap (~> 1.0.0)
xcodeproj (>= 1.13.0, < 2.0.0) xcodeproj (>= 1.13.0, < 2.0.0)
xcpretty (~> 0.3.0) xcpretty (~> 0.3.0)
xcpretty-travis-formatter (>= 0.0.3) xcpretty-travis-formatter (>= 0.0.3, < 2.0.0)
gh_inspector (1.1.3) gh_inspector (1.1.3)
git (1.13.0) git (1.19.1)
addressable (~> 2.8) addressable (~> 2.8)
rchardet (~> 1.8) rchardet (~> 1.8)
google-apis-androidpublisher_v3 (0.25.0) google-apis-androidpublisher_v3 (0.54.0)
google-apis-core (>= 0.7, < 2.a) google-apis-core (>= 0.11.0, < 2.a)
google-apis-core (0.7.0) google-apis-core (0.11.3)
addressable (~> 2.5, >= 2.5.1) addressable (~> 2.5, >= 2.5.1)
googleauth (>= 0.16.2, < 2.a) googleauth (>= 0.16.2, < 2.a)
httpclient (>= 2.8.1, < 3.a) httpclient (>= 2.8.1, < 3.a)
@ -140,101 +145,101 @@ GEM
representable (~> 3.0) representable (~> 3.0)
retriable (>= 2.0, < 4.a) retriable (>= 2.0, < 4.a)
rexml rexml
webrick google-apis-iamcredentials_v1 (0.17.0)
google-apis-iamcredentials_v1 (0.13.0) google-apis-core (>= 0.11.0, < 2.a)
google-apis-core (>= 0.7, < 2.a) google-apis-playcustomapp_v1 (0.13.0)
google-apis-playcustomapp_v1 (0.10.0) google-apis-core (>= 0.11.0, < 2.a)
google-apis-core (>= 0.7, < 2.a) google-apis-storage_v1 (0.29.0)
google-apis-storage_v1 (0.17.0) google-apis-core (>= 0.11.0, < 2.a)
google-apis-core (>= 0.7, < 2.a) google-cloud-core (1.6.1)
google-cloud-core (1.6.0) google-cloud-env (>= 1.0, < 3.a)
google-cloud-env (~> 1.0)
google-cloud-errors (~> 1.0) google-cloud-errors (~> 1.0)
google-cloud-env (1.6.0) google-cloud-env (1.6.0)
faraday (>= 0.17.3, < 3.0) faraday (>= 0.17.3, < 3.0)
google-cloud-errors (1.2.0) google-cloud-errors (1.3.1)
google-cloud-storage (1.38.0) google-cloud-storage (1.45.0)
addressable (~> 2.8) addressable (~> 2.8)
digest-crc (~> 0.4) digest-crc (~> 0.4)
google-apis-iamcredentials_v1 (~> 0.1) google-apis-iamcredentials_v1 (~> 0.1)
google-apis-storage_v1 (~> 0.17.0) google-apis-storage_v1 (~> 0.29.0)
google-cloud-core (~> 1.6) google-cloud-core (~> 1.6)
googleauth (>= 0.16.2, < 2.a) googleauth (>= 0.16.2, < 2.a)
mini_mime (~> 1.0) mini_mime (~> 1.0)
googleauth (1.2.0) googleauth (1.8.1)
faraday (>= 0.17.3, < 3.a) faraday (>= 0.17.3, < 3.a)
jwt (>= 1.4, < 3.0) jwt (>= 1.4, < 3.0)
memoist (~> 0.16)
multi_json (~> 1.11) multi_json (~> 1.11)
os (>= 0.9, < 2.0) os (>= 0.9, < 2.0)
signet (>= 0.16, < 2.a) signet (>= 0.16, < 2.a)
highline (2.0.3) highline (2.0.3)
http-cookie (1.0.5) http-cookie (1.0.6)
domain_name (~> 0.5) domain_name (~> 0.5)
httpclient (2.8.3) httpclient (2.8.3)
jmespath (1.6.1) jmespath (1.6.2)
json (2.6.2) json (2.7.2)
jwt (2.4.1) jwt (2.8.2)
base64
kramdown (2.4.0) kramdown (2.4.0)
rexml rexml
kramdown-parser-gfm (1.1.0) kramdown-parser-gfm (1.1.0)
kramdown (~> 2.0) kramdown (~> 2.0)
memoist (0.16.2) mini_magick (4.13.1)
mini_magick (4.11.0) mini_mime (1.1.5)
mini_mime (1.1.2)
multi_json (1.15.0) multi_json (1.15.0)
multipart-post (2.0.0) multipart-post (2.4.1)
nanaimo (0.3.0) nanaimo (0.3.0)
nap (1.1.0) nap (1.1.0)
naturally (2.2.1) naturally (2.2.1)
nkf (0.2.0)
no_proxy_fix (0.1.2) no_proxy_fix (0.1.2)
octokit (4.25.1) octokit (4.25.1)
faraday (>= 1, < 3) faraday (>= 1, < 3)
sawyer (~> 0.9) sawyer (~> 0.9)
open4 (1.3.4) open4 (1.3.4)
optparse (0.1.1) optparse (0.5.0)
os (1.1.4) os (1.1.4)
plist (3.6.0) plist (3.7.1)
public_suffix (4.0.7) public_suffix (5.1.1)
rake (13.0.6) rake (13.2.1)
rchardet (1.8.0) rchardet (1.8.0)
representable (3.2.0) representable (3.2.0)
declarative (< 0.1.0) declarative (< 0.1.0)
trailblazer-option (>= 0.1.1, < 0.2.0) trailblazer-option (>= 0.1.1, < 0.2.0)
uber (< 0.2.0) uber (< 0.2.0)
retriable (3.1.2) retriable (3.1.2)
rexml (3.2.5) rexml (3.2.9)
strscan
rouge (2.0.7) rouge (2.0.7)
ruby2_keywords (0.0.5) ruby2_keywords (0.0.5)
rubyzip (2.3.2) rubyzip (2.3.2)
sawyer (0.9.2) sawyer (0.9.2)
addressable (>= 2.3.5) addressable (>= 2.3.5)
faraday (>= 0.17.3, < 3) faraday (>= 0.17.3, < 3)
security (0.1.3) security (0.1.5)
signet (0.17.0) signet (0.18.0)
addressable (~> 2.8) addressable (~> 2.8)
faraday (>= 0.17.5, < 3.a) faraday (>= 0.17.5, < 3.a)
jwt (>= 1.5, < 3.0) jwt (>= 1.5, < 3.0)
multi_json (~> 1.10) multi_json (~> 1.10)
simctl (1.6.8) simctl (1.6.10)
CFPropertyList CFPropertyList
naturally naturally
strscan (3.1.0)
terminal-notifier (2.0.0) terminal-notifier (2.0.0)
terminal-table (1.8.0) terminal-table (3.0.2)
unicode-display_width (~> 1.1, >= 1.1.1) unicode-display_width (>= 1.1.1, < 3)
trailblazer-option (0.1.2) trailblazer-option (0.1.2)
tty-cursor (0.7.1) tty-cursor (0.7.1)
tty-screen (0.8.1) tty-screen (0.8.2)
tty-spinner (0.9.3) tty-spinner (0.9.3)
tty-cursor (~> 0.7) tty-cursor (~> 0.7)
uber (0.1.0) uber (0.1.0)
unf (0.1.4) unf (0.1.4)
unf_ext unf_ext
unf_ext (0.0.8.2) unf_ext (0.0.9.1)
unicode-display_width (1.8.0) unicode-display_width (2.5.0)
webrick (1.7.0)
word_wrap (1.0.0) word_wrap (1.0.0)
xcodeproj (1.22.0) xcodeproj (1.24.0)
CFPropertyList (>= 2.3.3, < 4.0) CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3) atomos (~> 0.1.3)
claide (>= 1.0.2, < 2.0) claide (>= 1.0.2, < 2.0)
@ -248,6 +253,7 @@ GEM
PLATFORMS PLATFORMS
universal-darwin-21 universal-darwin-21
universal-darwin-23
x86_64-darwin-20 x86_64-darwin-20
x86_64-linux x86_64-linux

View File

@ -28,7 +28,7 @@ buildscript {
classpath 'com.google.gms:google-services:4.3.15' classpath 'com.google.gms:google-services:4.3.15'
classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:4.0.0.2929' classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:4.0.0.2929'
classpath 'com.google.android.gms:oss-licenses-plugin:0.10.6' classpath 'com.google.android.gms:oss-licenses-plugin:0.10.6'
classpath "com.likethesalad.android:stem-plugin:2.4.1" classpath "com.likethesalad.android:stem-plugin:2.9.0"
classpath 'org.owasp:dependency-check-gradle:8.2.1' classpath 'org.owasp:dependency-check-gradle:8.2.1'
classpath "org.jetbrains.dokka:dokka-gradle-plugin:1.8.10" classpath "org.jetbrains.dokka:dokka-gradle-plugin:1.8.10"
classpath "org.jetbrains.kotlinx:kotlinx-knit:0.4.0" classpath "org.jetbrains.kotlinx:kotlinx-knit:0.4.0"
@ -45,7 +45,7 @@ plugins {
// Detekt // Detekt
id "io.gitlab.arturbosch.detekt" version "1.22.0" id "io.gitlab.arturbosch.detekt" version "1.22.0"
// Ksp // Ksp
id "com.google.devtools.ksp" version "1.8.10-1.0.9" id "com.google.devtools.ksp" version "1.9.24-1.0.20"
// Dependency Analysis // Dependency Analysis
id 'com.autonomousapps.dependency-analysis' version "1.20.0" id 'com.autonomousapps.dependency-analysis' version "1.20.0"

View File

@ -33,9 +33,9 @@ def initializeReport(report, projects, classExcludes) {
) )
} }
report.reports { report.reports {
xml.enabled true xml.required = true
html.enabled true html.required = true
csv.enabled false csv.required = false
} }
gradle.projectsEvaluated { gradle.projectsEvaluated {

View File

@ -1,39 +1,39 @@
ext.versions = [ ext.versions = [
'minSdk' : 21, 'minSdk' : 21,
'compileSdk' : 33, 'compileSdk' : 34,
'targetSdk' : 33, 'targetSdk' : 34,
'sourceCompat' : JavaVersion.VERSION_11, 'sourceCompat' : JavaVersion.VERSION_17,
'targetCompat' : JavaVersion.VERSION_11, 'targetCompat' : JavaVersion.VERSION_17,
'jvmTarget' : "17",
] ]
def gradle = "7.4.2" def gradle = "8.4.2"
// Ref: https://kotlinlang.org/releases.html // Ref: https://kotlinlang.org/releases.html
def kotlin = "1.8.10" def kotlin = "1.9.24"
def kotlinCoroutines = "1.6.4" def kotlinCoroutines = "1.8.1"
def dagger = "2.45" def dagger = "2.51.1"
def firebaseBom = "32.0.0" def firebaseBom = "33.1.1"
def appDistribution = "16.0.0-beta08" def appDistribution = "16.0.0-beta08"
def retrofit = "2.9.0" def retrofit = "2.11.0"
def markwon = "4.6.2" def markwon = "4.6.2"
def moshi = "1.14.0" def moshi = "1.15.1"
def lifecycle = "2.5.1" def lifecycle = "2.8.3"
def flowBinding = "1.2.0" def flowBinding = "1.2.0"
def flipper = "0.190.0" def flipper = "0.259.0"
def epoxy = "5.0.0" def epoxy = "5.0.0"
def mavericks = "3.0.7" def mavericks = "3.0.9"
def glide = "4.15.1" def glide = "4.16.0"
def bigImageViewer = "1.8.1" def bigImageViewer = "1.8.1"
def jjwt = "0.11.5" def jjwt = "0.11.5"
def vanniktechEmoji = "0.16.0" def vanniktechEmoji = "0.16.0"
def sentry = "6.18.1" def sentry = "6.18.1"
// Use 1.6.0 alpha to fix issue with test def fragment = "1.8.1"
def fragment = "1.6.0-beta01"
// Testing // Testing
def mockk = "1.12.3" // We need to use 1.12.3 to have mocking in androidTest until a new version is released: https://github.com/mockk/mockk/issues/819 def mockk = "1.13.11"
def espresso = "3.5.1" def espresso = "3.6.1"
def androidxTest = "1.5.0" def androidxTest = "1.6.1"
def androidxOrchestrator = "1.4.2" def androidxOrchestrator = "1.5.0"
def paparazzi = "1.2.0" def paparazzi = "1.3.4"
ext.libs = [ ext.libs = [
gradle : [ gradle : [
@ -48,8 +48,8 @@ ext.libs = [
'coroutinesTest' : "org.jetbrains.kotlinx:kotlinx-coroutines-test:$kotlinCoroutines" 'coroutinesTest' : "org.jetbrains.kotlinx:kotlinx-coroutines-test:$kotlinCoroutines"
], ],
androidx : [ androidx : [
'activity' : "androidx.activity:activity-ktx:1.7.2", 'activity' : "androidx.activity:activity-ktx:1.9.0",
'appCompat' : "androidx.appcompat:appcompat:1.6.1", 'appCompat' : "androidx.appcompat:appcompat:1.7.0",
'biometric' : "androidx.biometric:biometric:1.1.0", 'biometric' : "androidx.biometric:biometric:1.1.0",
'core' : "androidx.core:core-ktx:1.10.1", 'core' : "androidx.core:core-ktx:1.10.1",
'recyclerview' : "androidx.recyclerview:recyclerview:1.3.0", 'recyclerview' : "androidx.recyclerview:recyclerview:1.3.0",
@ -77,11 +77,11 @@ ext.libs = [
'espressoCore' : "androidx.test.espresso:espresso-core:$espresso", 'espressoCore' : "androidx.test.espresso:espresso-core:$espresso",
'espressoContrib' : "androidx.test.espresso:espresso-contrib:$espresso", 'espressoContrib' : "androidx.test.espresso:espresso-contrib:$espresso",
'espressoIntents' : "androidx.test.espresso:espresso-intents:$espresso", 'espressoIntents' : "androidx.test.espresso:espresso-intents:$espresso",
'viewpager2' : "androidx.viewpager2:viewpager2:1.0.0", 'viewpager2' : "androidx.viewpager2:viewpager2:1.1.0",
'transition' : "androidx.transition:transition:1.4.1", 'transition' : "androidx.transition:transition:1.5.0",
], ],
google : [ google : [
'material' : "com.google.android.material:material:1.9.0", 'material' : "com.google.android.material:material:1.12.0",
'firebaseBom' : "com.google.firebase:firebase-bom:$firebaseBom", 'firebaseBom' : "com.google.firebase:firebase-bom:$firebaseBom",
'messaging' : "com.google.firebase:firebase-messaging", 'messaging' : "com.google.firebase:firebase-messaging",
//'appdistributionApi' : "com.google.firebase:firebase-appdistribution-api-ktx:$appDistribution", //'appdistributionApi' : "com.google.firebase:firebase-appdistribution-api-ktx:$appDistribution",
@ -102,7 +102,7 @@ ext.libs = [
], ],
element : [ element : [
'opusencoder' : "io.element.android:opusencoder:1.1.0", 'opusencoder' : "io.element.android:opusencoder:1.1.0",
'wysiwyg' : "io.element.android:wysiwyg:2.37.3" 'wysiwyg' : "io.element.android:wysiwyg:2.37.4"
], ],
squareup : [ squareup : [
'moshi' : "com.squareup.moshi:moshi:$moshi", 'moshi' : "com.squareup.moshi:moshi:$moshi",
@ -173,7 +173,7 @@ ext.libs = [
'kluent' : "org.amshove.kluent:kluent-android:1.73", 'kluent' : "org.amshove.kluent:kluent-android:1.73",
'timberJunitRule' : "net.lachlanmckee:timber-junit-rule:1.0.1", 'timberJunitRule' : "net.lachlanmckee:timber-junit-rule:1.0.1",
'junit' : "junit:junit:4.13.2", 'junit' : "junit:junit:4.13.2",
'robolectric' : "org.robolectric:robolectric:4.9", 'robolectric' : "org.robolectric:robolectric:4.13",
] ]
] ]

View File

@ -97,6 +97,7 @@ ext.groups = [
'com.google.auto.value', 'com.google.auto.value',
'com.google.code.findbugs', 'com.google.code.findbugs',
'com.google.code.gson', 'com.google.code.gson',
'com.google.crypto.tink',
'com.google.dagger', 'com.google.dagger',
'com.google.devtools.ksp', 'com.google.devtools.ksp',
'com.google.errorprone', 'com.google.errorprone',
@ -142,6 +143,7 @@ ext.groups = [
'commons-codec', 'commons-codec',
'commons-io', 'commons-io',
'commons-logging', 'commons-logging',
'dev.drewhamilton.poko',
'info.picocli', 'info.picocli',
'io.element.android', 'io.element.android',
'io.github.davidburstrom.contester', 'io.github.davidburstrom.contester',

View File

@ -62,7 +62,7 @@ class PaparazziExampleScreenshotTest {
val view = paparazzi.inflate<ConstraintLayout>(R.layout.item_radio) val view = paparazzi.inflate<ConstraintLayout>(R.layout.item_radio)
// Bind data to the view // Bind data to the view
view.findViewById<TextView>(R.id.actionTitle).text = paparazzi.resources.getString(R.string.room_settings_all_messages) view.findViewById<TextView>(R.id.actionTitle).text = paparazzi.resources.getString(CommonStrings.room_settings_all_messages)
view.findViewById<ImageView>(R.id.radioIcon).setImageResource(R.drawable.ic_radio_on) view.findViewById<ImageView>(R.id.radioIcon).setImageResource(R.drawable.ic_radio_on)
// Record the bound view // Record the bound view

View File

@ -179,7 +179,7 @@ class SettingsRobot {
} }
fun advancedSettings(block: SettingsAdvancedRobot.() -> Unit) { fun advancedSettings(block: SettingsAdvancedRobot.() -> Unit) {
clickOn(R.string.settings_advanced_settings) clickOn(CommonStrings.settings_advanced_settings)
block(SettingsAdvancedRobot()) block(SettingsAdvancedRobot())
pressBack() pressBack()
} }
@ -187,7 +187,7 @@ class SettingsRobot {
class SettingsAdvancedRobot { class SettingsAdvancedRobot {
fun toggleDeveloperMode() { fun toggleDeveloperMode() {
clickOn(R.string.settings_developer_mode_summary) clickOn(CommonStrings.settings_developer_mode_summary)
} }
} }
``` ```

View File

@ -0,0 +1,2 @@
Main changes in this version: support authenticated media.
Full changelog: https://github.com/element-hq/element-android/releases

View File

@ -46,4 +46,4 @@ signing.element.nightly.keyPassword=Secret
# Customise the Lint version to use a more recent version than the one bundled with AGP # Customise the Lint version to use a more recent version than the one bundled with AGP
# https://googlesamples.github.io/android-custom-lint-rules/usage/newer-lint.md.html # https://googlesamples.github.io/android-custom-lint-rules/usage/newer-lint.md.html
android.experimental.lint.version=8.3.0-alpha12 android.experimental.lint.version=8.6.0-alpha08

Binary file not shown.

View File

@ -1,7 +1,8 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionSha256Sum=312eb12875e1747e05c2f81a4789902d7e4ec5defbd1eefeaccc08acf096505d distributionSha256Sum=f8b4f4772d302c8ff580bc40d0f56e715de69b163546944f787c87abf209c961
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-all.zip
networkTimeout=10000 networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

View File

@ -38,7 +38,7 @@ android {
targetCompatibility versions.targetCompat targetCompatibility versions.targetCompat
} }
kotlinOptions { kotlinOptions {
jvmTarget = '11' jvmTarget = versions.jvmTarget
} }
buildFeatures { buildFeatures {

View File

@ -31,7 +31,6 @@ import android.view.WindowManager
import android.widget.ImageView import android.widget.ImageView
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.GestureDetectorCompat
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
@ -39,6 +38,7 @@ import androidx.core.view.updatePadding
import androidx.transition.TransitionManager import androidx.transition.TransitionManager
import androidx.viewpager2.widget.ViewPager2 import androidx.viewpager2.widget.ViewPager2
import im.vector.lib.attachmentviewer.databinding.ActivityAttachmentViewerBinding import im.vector.lib.attachmentviewer.databinding.ActivityAttachmentViewerBinding
import im.vector.lib.ui.styles.R
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
import kotlin.math.abs import kotlin.math.abs
@ -71,7 +71,7 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
private lateinit var swipeDismissHandler: SwipeToDismissHandler private lateinit var swipeDismissHandler: SwipeToDismissHandler
private lateinit var directionDetector: SwipeDirectionDetector private lateinit var directionDetector: SwipeDirectionDetector
private lateinit var scaleDetector: ScaleGestureDetector private lateinit var scaleDetector: ScaleGestureDetector
private lateinit var gestureDetector: GestureDetectorCompat private lateinit var gestureDetector: GestureDetector
var currentPosition = 0 var currentPosition = 0
private set private set
@ -309,7 +309,7 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
ScaleGestureDetector(this, ScaleGestureDetector.SimpleOnScaleGestureListener()) ScaleGestureDetector(this, ScaleGestureDetector.SimpleOnScaleGestureListener())
private fun createGestureDetector() = private fun createGestureDetector() =
GestureDetectorCompat(this, object : GestureDetector.SimpleOnGestureListener() { GestureDetector(this, object : GestureDetector.SimpleOnGestureListener() {
override fun onSingleTapConfirmed(e: MotionEvent): Boolean { override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
if (isImagePagerIdle) { if (isImagePagerIdle) {
handleSingleTap(e, isOverlayWasClicked) handleSingleTap(e, isOverlayWasClicked)

View File

@ -28,7 +28,6 @@ android {
targetSdk versions.targetSdk targetSdk versions.targetSdk
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
} }
buildTypes { buildTypes {
@ -44,7 +43,7 @@ android {
} }
kotlinOptions { kotlinOptions {
jvmTarget = "11" jvmTarget = versions.jvmTarget
freeCompilerArgs += [ freeCompilerArgs += [
"-opt-in=kotlin.RequiresOptIn" "-opt-in=kotlin.RequiresOptIn"
] ]

View File

@ -17,7 +17,7 @@ android {
} }
kotlinOptions { kotlinOptions {
jvmTarget = "11" jvmTarget = versions.jvmTarget
} }
} }

View File

@ -13,7 +13,6 @@ import android.text.TextWatcher;
import android.util.Log; import android.util.Log;
import android.util.TypedValue; import android.util.TypedValue;
import android.view.Gravity; import android.view.Gravity;
import android.view.Window;
import android.view.WindowManager; import android.view.WindowManager;
import android.widget.EditText; import android.widget.EditText;
import android.widget.PopupWindow; import android.widget.PopupWindow;

View File

@ -17,7 +17,7 @@ android {
} }
kotlinOptions { kotlinOptions {
jvmTarget = "11" jvmTarget = versions.jvmTarget
} }
} }

View File

@ -20,7 +20,6 @@ import android.animation.AnimatorListenerAdapter;
import android.content.Context; import android.content.Context;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.os.Build; import android.os.Build;
import android.text.Spannable; import android.text.Spannable;
import android.text.TextUtils; import android.text.TextUtils;

View File

@ -20,6 +20,7 @@ import android.content.Context;
import android.content.res.TypedArray; import android.content.res.TypedArray;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.widget.EditText; import android.widget.EditText;
import com.android.dialer.dialpadview.R; import com.android.dialer.dialpadview.R;
import com.android.dialer.util.ViewUtil; import com.android.dialer.util.ViewUtil;

View File

@ -42,7 +42,7 @@ android {
} }
kotlinOptions { kotlinOptions {
jvmTarget = "11" jvmTarget = versions.jvmTarget
} }
} }

View File

@ -10,11 +10,11 @@ dependencies {
task javadocJar(type: Jar, dependsOn: 'javadoc') { task javadocJar(type: Jar, dependsOn: 'javadoc') {
from javadoc.destinationDir from javadoc.destinationDir
classifier = 'javadoc' archiveClassifier = 'javadoc'
} }
task sourcesJar(type: Jar, dependsOn: 'classes') { task sourcesJar(type: Jar, dependsOn: 'classes') {
from sourceSets.main.allSource from sourceSets.main.allSource
classifier = 'sources' archiveClassifier = 'sources'
} }
sourceSets { sourceSets {

View File

@ -13,6 +13,15 @@ android {
sourceSets { sourceSets {
main.java.srcDirs += 'src/main/kotlin' main.java.srcDirs += 'src/main/kotlin'
} }
compileOptions {
sourceCompatibility versions.sourceCompat
targetCompatibility versions.targetCompat
}
kotlinOptions {
jvmTarget = versions.jvmTarget
}
} }
dependencies { dependencies {

View File

@ -1,6 +1,13 @@
package com.amulyakhare.textdrawable; package com.amulyakhare.textdrawable;
import android.graphics.*; import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.graphics.drawable.ShapeDrawable; import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.OvalShape; import android.graphics.drawable.shapes.OvalShape;
import android.graphics.drawable.shapes.RectShape; import android.graphics.drawable.shapes.RectShape;
@ -313,4 +320,4 @@ public class TextDrawable extends ShapeDrawable {
public TextDrawable buildRound(String text, int color); public TextDrawable buildRound(String text, int color);
} }
} }

View File

@ -43,7 +43,7 @@ android {
} }
kotlinOptions { kotlinOptions {
jvmTarget = "11" jvmTarget = versions.jvmTarget
} }
} }

View File

@ -19,7 +19,7 @@ android {
} }
kotlinOptions { kotlinOptions {
jvmTarget = "11" jvmTarget = versions.jvmTarget
} }
} }

View File

@ -0,0 +1,20 @@
/*
* Copyright (c) 2024 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.lib.strings
typealias CommonStrings = R.string
typealias CommonPlurals = R.plurals

View File

@ -2966,4 +2966,4 @@
\n%s</string> \n%s</string>
<string name="create_room_unknown_users_dialog_submit">آغاز گپ به هر حال</string> <string name="create_room_unknown_users_dialog_submit">آغاز گپ به هر حال</string>
<string name="invite_unknown_users_dialog_submit">دعوت به هر حال</string> <string name="invite_unknown_users_dialog_submit">دعوت به هر حال</string>
</resources> </resources>

View File

@ -2865,4 +2865,4 @@
<string name="room_polls_wait_for_display">显示投票</string> <string name="room_polls_wait_for_display">显示投票</string>
<string name="rich_text_editor_quote">切换引用</string> <string name="rich_text_editor_quote">切换引用</string>
<string name="rich_text_editor_numbered_list">切换有序列表</string> <string name="rich_text_editor_numbered_list">切换有序列表</string>
</resources> </resources>

View File

@ -29,7 +29,6 @@ android {
targetSdk versions.targetSdk targetSdk versions.targetSdk
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
} }
buildTypes { buildTypes {
@ -43,7 +42,7 @@ android {
targetCompatibility versions.targetCompat targetCompatibility versions.targetCompat
} }
kotlinOptions { kotlinOptions {
jvmTarget = '11' jvmTarget = versions.jvmTarget
} }
buildFeatures { buildFeatures {
viewBinding true viewBinding true

View File

@ -28,7 +28,7 @@ android {
targetCompatibility versions.targetCompat targetCompatibility versions.targetCompat
} }
kotlinOptions { kotlinOptions {
jvmTarget = "11" jvmTarget = versions.jvmTarget
} }
// publishNonDefault true // publishNonDefault true

View File

@ -1,2 +1,2 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" /> <manifest />

View File

@ -17,7 +17,7 @@ buildscript {
} }
} }
dependencies { dependencies {
classpath "io.realm:realm-gradle-plugin:10.16.0" classpath "io.realm:realm-gradle-plugin:10.18.0"
} }
} }
@ -62,7 +62,7 @@ android {
// that the app's state is completely cleared between tests. // that the app's state is completely cleared between tests.
testInstrumentationRunnerArguments clearPackageData: 'true' testInstrumentationRunnerArguments clearPackageData: 'true'
buildConfigField "String", "SDK_VERSION", "\"1.6.18\"" buildConfigField "String", "SDK_VERSION", "\"1.6.20\""
buildConfigField "String", "GIT_SDK_REVISION", "\"${gitRevision()}\"" buildConfigField "String", "GIT_SDK_REVISION", "\"${gitRevision()}\""
buildConfigField "String", "GIT_SDK_REVISION_UNIX_DATE", "\"${gitRevisionUnixDate()}\"" buildConfigField "String", "GIT_SDK_REVISION_UNIX_DATE", "\"${gitRevisionUnixDate()}\""
@ -81,7 +81,7 @@ android {
buildTypes { buildTypes {
debug { debug {
if (project.hasProperty("coverage")) { if (project.hasProperty("coverage")) {
testCoverageEnabled = coverage == "true" testCoverageEnabled = project.properties["coverage"] == "true"
} }
// Set to true to log privacy or sensible data, such as token // Set to true to log privacy or sensible data, such as token
buildConfigField "boolean", "LOG_PRIVATE_DATA", project.property("vector.debugPrivateData") buildConfigField "boolean", "LOG_PRIVATE_DATA", project.property("vector.debugPrivateData")
@ -106,7 +106,7 @@ android {
} }
kotlinOptions { kotlinOptions {
jvmTarget = "11" jvmTarget = versions.jvmTarget
freeCompilerArgs += [ freeCompilerArgs += [
// Disabled for now, there are too many errors. Could be handled in another dedicated PR // Disabled for now, there are too many errors. Could be handled in another dedicated PR
// '-Xexplicit-api=strict', // or warning // '-Xexplicit-api=strict', // or warning
@ -125,6 +125,15 @@ android {
} }
} }
buildFeatures {
buildConfig true
}
packaging {
pickFirsts.add("META-INF/LICENSE.md")
pickFirsts.add("META-INF/LICENSE-notice.md")
pickFirsts.add("MANIFEST.MF")
}
} }
static def gitRevision() { static def gitRevision() {

View File

@ -23,7 +23,6 @@ import androidx.test.internal.runner.junit4.statement.UiThreadStatement
import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@ -58,6 +57,7 @@ import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import kotlin.coroutines.resume import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine import kotlin.coroutines.suspendCoroutine
import kotlin.time.Duration.Companion.milliseconds
/** /**
* This class exposes methods to be used in common cases * This class exposes methods to be used in common cases
@ -67,10 +67,9 @@ class CommonTestHelper internal constructor(context: Context, val cryptoConfig:
companion object { companion object {
@OptIn(ExperimentalCoroutinesApi::class)
internal fun runSessionTest(context: Context, cryptoConfig: MXCryptoConfig? = null, autoSignoutOnClose: Boolean = true, block: suspend CoroutineScope.(CommonTestHelper) -> Unit) { internal fun runSessionTest(context: Context, cryptoConfig: MXCryptoConfig? = null, autoSignoutOnClose: Boolean = true, block: suspend CoroutineScope.(CommonTestHelper) -> Unit) {
val testHelper = CommonTestHelper(context, cryptoConfig) val testHelper = CommonTestHelper(context, cryptoConfig)
return runTest(dispatchTimeoutMs = TestConstants.timeOutMillis) { return runTest(timeout = TestConstants.timeOutMillis.milliseconds) {
try { try {
withContext(Dispatchers.Default) { withContext(Dispatchers.Default) {
block(testHelper) block(testHelper)
@ -83,11 +82,10 @@ class CommonTestHelper internal constructor(context: Context, val cryptoConfig:
} }
} }
@OptIn(ExperimentalCoroutinesApi::class)
internal fun runCryptoTest(context: Context, cryptoConfig: MXCryptoConfig? = null, autoSignoutOnClose: Boolean = true, block: suspend CoroutineScope.(CryptoTestHelper, CommonTestHelper) -> Unit) { internal fun runCryptoTest(context: Context, cryptoConfig: MXCryptoConfig? = null, autoSignoutOnClose: Boolean = true, block: suspend CoroutineScope.(CryptoTestHelper, CommonTestHelper) -> Unit) {
val testHelper = CommonTestHelper(context, cryptoConfig) val testHelper = CommonTestHelper(context, cryptoConfig)
val cryptoTestHelper = CryptoTestHelper(testHelper) val cryptoTestHelper = CryptoTestHelper(testHelper)
return runTest(dispatchTimeoutMs = TestConstants.timeOutMillis) { return runTest(timeout = TestConstants.timeOutMillis.milliseconds) {
try { try {
withContext(Dispatchers.Default) { withContext(Dispatchers.Default) {
block(cryptoTestHelper, testHelper) block(cryptoTestHelper, testHelper)
@ -100,11 +98,10 @@ class CommonTestHelper internal constructor(context: Context, val cryptoConfig:
} }
} }
@OptIn(ExperimentalCoroutinesApi::class)
internal fun runLongCryptoTest(context: Context, cryptoConfig: MXCryptoConfig? = null, autoSignoutOnClose: Boolean = true, block: suspend CoroutineScope.(CryptoTestHelper, CommonTestHelper) -> Unit) { internal fun runLongCryptoTest(context: Context, cryptoConfig: MXCryptoConfig? = null, autoSignoutOnClose: Boolean = true, block: suspend CoroutineScope.(CryptoTestHelper, CommonTestHelper) -> Unit) {
val testHelper = CommonTestHelper(context, cryptoConfig) val testHelper = CommonTestHelper(context, cryptoConfig)
val cryptoTestHelper = CryptoTestHelper(testHelper) val cryptoTestHelper = CryptoTestHelper(testHelper)
return runTest(dispatchTimeoutMs = TestConstants.timeOutMillis * 4) { return runTest(timeout = (TestConstants.timeOutMillis * 4).milliseconds) {
try { try {
withContext(Dispatchers.Default) { withContext(Dispatchers.Default) {
block(cryptoTestHelper, testHelper) block(cryptoTestHelper, testHelper)

View File

@ -37,10 +37,10 @@ suspend fun <T> LiveData<T>.first(timeout: Long = TestConstants.timeOutMillis, p
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
suspendCancellableCoroutine { continuation -> suspendCancellableCoroutine { continuation ->
val observer = object : Observer<T> { val observer = object : Observer<T> {
override fun onChanged(data: T) { override fun onChanged(value: T) {
if (predicate(data)) { if (predicate(value)) {
removeObserver(this) removeObserver(this)
continuation.resume(data) continuation.resume(value)
} }
} }
} }

View File

@ -26,7 +26,6 @@ import org.junit.runners.JUnit4
import org.junit.runners.MethodSorters import org.junit.runners.MethodSorters
import org.matrix.android.sdk.InstrumentedTest import org.matrix.android.sdk.InstrumentedTest
import org.matrix.android.sdk.api.session.crypto.MXCryptoError import org.matrix.android.sdk.api.session.crypto.MXCryptoError
import org.matrix.android.sdk.api.session.getRoom
import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent
import org.matrix.android.sdk.common.CommonTestHelper.Companion.runCryptoTest import org.matrix.android.sdk.common.CommonTestHelper.Companion.runCryptoTest

View File

@ -21,7 +21,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest import androidx.test.filters.LargeTest
import junit.framework.TestCase.assertEquals import junit.framework.TestCase.assertEquals
import junit.framework.TestCase.assertNotNull import junit.framework.TestCase.assertNotNull
import junit.framework.TestCase.assertTrue
import org.amshove.kluent.shouldBeEqualTo import org.amshove.kluent.shouldBeEqualTo
import org.junit.Assert import org.junit.Assert
import org.junit.Assert.assertNull import org.junit.Assert.assertNull

View File

@ -56,7 +56,7 @@ class RealmSessionStoreMigration43Test {
} }
@Test @Test
fun migrationShouldBeNeeed() { fun migrationShouldBeNeeded() {
val realmName = "session_42.realm" val realmName = "session_42.realm"
val realmConfiguration = configurationFactory.createConfiguration( val realmConfiguration = configurationFactory.createConfiguration(
realmName, realmName,

View File

@ -27,11 +27,9 @@ import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
import java.io.IOException import java.io.IOException
import java.io.InputStream import java.io.InputStream
import java.lang.IllegalStateException
import java.util.Collections import java.util.Collections
import java.util.Locale import java.util.Locale
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import kotlin.Throws
/** /**
* Based on https://github.com/realm/realm-java/blob/master/realm/realm-library/src/testUtils/java/io/realm/TestRealmConfigurationFactory.java * Based on https://github.com/realm/realm-java/blob/master/realm/realm-library/src/testUtils/java/io/realm/TestRealmConfigurationFactory.java

View File

@ -1,5 +1,4 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android">
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

View File

@ -296,6 +296,11 @@ interface Session {
*/ */
fun getOkHttpClient(): OkHttpClient fun getOkHttpClient(): OkHttpClient
/**
* Same as [getOkHttpClient] but will add the access token to the request.
*/
fun getAuthenticatedOkHttpClient(): OkHttpClient
/** /**
* A global session listener to get notified for some events. * A global session listener to get notified for some events.
*/ */

View File

@ -61,6 +61,8 @@ interface ContentUrlResolver {
*/ */
fun resolveThumbnail(contentUrl: String?, width: Int, height: Int, method: ThumbnailMethod): String? fun resolveThumbnail(contentUrl: String?, width: Int, height: Int, method: ThumbnailMethod): String?
fun requiresAuthentication(resolvedUrl: String): Boolean
sealed class ResolvedMethod { sealed class ResolvedMethod {
data class GET(val url: String) : ResolvedMethod() data class GET(val url: String) : ResolvedMethod()
data class POST(val url: String, val jsonBody: String) : ResolvedMethod() data class POST(val url: String, val jsonBody: String) : ResolvedMethod()

View File

@ -95,6 +95,10 @@ data class HomeServerCapabilities(
* If set to true, the SDK will not use the network constraint when configuring Worker for the WorkManager, provided in Wellknown. * If set to true, the SDK will not use the network constraint when configuring Worker for the WorkManager, provided in Wellknown.
*/ */
val disableNetworkConstraint: Boolean? = null, val disableNetworkConstraint: Boolean? = null,
/**
* True if the home server supports authenticated media.
*/
val canUseAuthenticatedMedia: Boolean = false,
) { ) {
enum class RoomCapabilitySupport { enum class RoomCapabilitySupport {

View File

@ -35,6 +35,7 @@ import org.matrix.android.sdk.internal.auth.registration.RegisterAddThreePidTask
import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.network.executeRequest
import org.matrix.android.sdk.internal.session.content.DefaultContentUrlResolver import org.matrix.android.sdk.internal.session.content.DefaultContentUrlResolver
import org.matrix.android.sdk.internal.session.contentscanner.DisabledContentScannerService import org.matrix.android.sdk.internal.session.contentscanner.DisabledContentScannerService
import org.matrix.android.sdk.internal.session.media.IsAuthenticatedMediaSupported
internal class DefaultLoginWizard( internal class DefaultLoginWizard(
private val authAPI: AuthAPI, private val authAPI: AuthAPI,
@ -45,8 +46,14 @@ internal class DefaultLoginWizard(
private var pendingSessionData: PendingSessionData = pendingSessionStore.getPendingSessionData() ?: error("Pending session data should exist here") private var pendingSessionData: PendingSessionData = pendingSessionStore.getPendingSessionData() ?: error("Pending session data should exist here")
private val getProfileTask: GetProfileTask = DefaultGetProfileTask( private val getProfileTask: GetProfileTask = DefaultGetProfileTask(
authAPI, authAPI = authAPI,
DefaultContentUrlResolver(pendingSessionData.homeServerConnectionConfig, DisabledContentScannerService()) contentUrlResolver = DefaultContentUrlResolver(
homeServerConnectionConfig = pendingSessionData.homeServerConnectionConfig,
scannerService = DisabledContentScannerService(),
isAuthenticatedMediaSupported = object : IsAuthenticatedMediaSupported {
override fun invoke() = false
}
)
) )
override suspend fun getProfileInfo(matrixId: String): LoginProfileInfo { override suspend fun getProfileInfo(matrixId: String): LoginProfileInfo {

View File

@ -61,5 +61,6 @@ internal data class HomeServerVersion(
val r0_6_1 = HomeServerVersion(major = 0, minor = 6, patch = 1) val r0_6_1 = HomeServerVersion(major = 0, minor = 6, patch = 1)
val v1_3_0 = HomeServerVersion(major = 1, minor = 3, patch = 0) val v1_3_0 = HomeServerVersion(major = 1, minor = 3, patch = 0)
val v1_4_0 = HomeServerVersion(major = 1, minor = 4, patch = 0) val v1_4_0 = HomeServerVersion(major = 1, minor = 4, patch = 0)
val v1_11_0 = HomeServerVersion(major = 1, minor = 11, patch = 0)
} }
} }

View File

@ -54,6 +54,7 @@ private const val FEATURE_ID_ACCESS_TOKEN = "m.id_access_token"
private const val FEATURE_SEPARATE_ADD_AND_BIND = "m.separate_add_and_bind" private const val FEATURE_SEPARATE_ADD_AND_BIND = "m.separate_add_and_bind"
private const val FEATURE_THREADS_MSC3440 = "org.matrix.msc3440" private const val FEATURE_THREADS_MSC3440 = "org.matrix.msc3440"
private const val FEATURE_THREADS_MSC3440_STABLE = "org.matrix.msc3440.stable" private const val FEATURE_THREADS_MSC3440_STABLE = "org.matrix.msc3440.stable"
@Deprecated("The availability of stable get_login_token is now exposed as a capability and part of login flow") @Deprecated("The availability of stable get_login_token is now exposed as a capability and part of login flow")
private const val FEATURE_QR_CODE_LOGIN = "org.matrix.msc3882" private const val FEATURE_QR_CODE_LOGIN = "org.matrix.msc3882"
private const val FEATURE_THREADS_MSC3771 = "org.matrix.msc3771" private const val FEATURE_THREADS_MSC3771 = "org.matrix.msc3771"
@ -142,6 +143,15 @@ internal fun Versions.doesServerSupportLogoutDevices(): Boolean {
return getMaxVersion() >= HomeServerVersion.r0_6_1 return getMaxVersion() >= HomeServerVersion.r0_6_1
} }
/**
* Indicate if the server supports MSC3916 : https://github.com/matrix-org/matrix-spec-proposals/pull/3916
*
* @return true if authenticated media is supported
*/
internal fun Versions.doesServerSupportAuthenticatedMedia(): Boolean {
return getMaxVersion() >= HomeServerVersion.v1_11_0
}
private fun Versions.getMaxVersion(): HomeServerVersion { private fun Versions.getMaxVersion(): HomeServerVersion {
return supportedVersions return supportedVersions
?.mapNotNull { HomeServerVersion.parse(it) } ?.mapNotNull { HomeServerVersion.parse(it) }

View File

@ -17,7 +17,7 @@
package org.matrix.android.sdk.internal.crypto.store package org.matrix.android.sdk.internal.crypto.store
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations import androidx.lifecycle.map
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import io.realm.Realm import io.realm.Realm
import io.realm.RealmConfiguration import io.realm.RealmConfiguration
@ -280,7 +280,7 @@ internal class RustCryptoStore @Inject constructor(
{ entity -> myDeviceLastSeenInfoEntityMapper.map(entity) } { entity -> myDeviceLastSeenInfoEntityMapper.map(entity) }
) )
return Transformations.map(liveData) { return liveData.map {
it.firstOrNull().toOptional() it.firstOrNull().toOptional()
} }
} }
@ -351,7 +351,7 @@ internal class RustCryptoStore @Inject constructor(
) )
} }
) )
return Transformations.map(liveData) { return liveData.map {
it.firstOrNull() ?: GlobalCryptoConfig(false, false, false) it.firstOrNull() ?: GlobalCryptoConfig(false, false, false)
} }
} }
@ -372,7 +372,7 @@ internal class RustCryptoStore @Inject constructor(
it.blacklistUnverifiedDevices it.blacklistUnverifiedDevices
} }
) )
return Transformations.map(liveData) { return liveData.map {
it.firstOrNull() ?: false it.firstOrNull() ?: false
} }
} }

View File

@ -39,6 +39,7 @@ import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo020 import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo020
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo021 import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo021
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo022 import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo022
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo023
import org.matrix.android.sdk.internal.util.database.MatrixRealmMigration import org.matrix.android.sdk.internal.util.database.MatrixRealmMigration
import org.matrix.android.sdk.internal.util.time.Clock import org.matrix.android.sdk.internal.util.time.Clock
import javax.inject.Inject import javax.inject.Inject
@ -54,7 +55,7 @@ internal class RealmCryptoStoreMigration @Inject constructor(
private val rustMigrationInfoProvider: RustMigrationInfoProvider, private val rustMigrationInfoProvider: RustMigrationInfoProvider,
) : MatrixRealmMigration( ) : MatrixRealmMigration(
dbName = "Crypto", dbName = "Crypto",
schemaVersion = 22L, schemaVersion = 23L,
) { ) {
/** /**
* Forces all RealmCryptoStoreMigration instances to be equal. * Forces all RealmCryptoStoreMigration instances to be equal.
@ -91,5 +92,6 @@ internal class RealmCryptoStoreMigration @Inject constructor(
rustMigrationInfoProvider.rustEncryptionConfiguration, rustMigrationInfoProvider.rustEncryptionConfiguration,
rustMigrationInfoProvider.migrateMegolmGroupSessions rustMigrationInfoProvider.migrateMegolmGroupSessions
).perform() ).perform()
if (oldVersion < 23) MigrateCryptoTo023(realm).perform()
} }
} }

View File

@ -0,0 +1,30 @@
/*
* Copyright (c) 2024 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.internal.crypto.store.db.migration
import io.realm.DynamicRealm
import org.matrix.android.sdk.internal.crypto.store.db.model.OutgoingKeyRequestEntityFields
import org.matrix.android.sdk.internal.util.database.RealmMigrator
// Some fields are now required due to upgrade of Kotlin version.
// See https://github.com/realm/realm-java/issues/7810 for more details.
internal class MigrateCryptoTo023(realm: DynamicRealm) : RealmMigrator(realm, 23) {
override fun doMigrate(realm: DynamicRealm) {
realm.schema.get("OutgoingKeyRequestEntity")
?.setRequired(OutgoingKeyRequestEntityFields.REQUEST_STATE_STR, true)
}
}

View File

@ -78,6 +78,8 @@ import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo051
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo052 import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo052
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo053 import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo053
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo054 import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo054
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo055
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo056
import org.matrix.android.sdk.internal.util.Normalizer import org.matrix.android.sdk.internal.util.Normalizer
import org.matrix.android.sdk.internal.util.database.MatrixRealmMigration import org.matrix.android.sdk.internal.util.database.MatrixRealmMigration
import timber.log.Timber import timber.log.Timber
@ -101,7 +103,7 @@ internal class RealmSessionStoreMigration @Inject constructor(
private val scSchemaVersion = 7L private val scSchemaVersion = 7L
private val scSchemaVersionOffset = (1L shl 12) private val scSchemaVersionOffset = (1L shl 12)
val schemaVersion = 54L + val schemaVersion = 56L +
scSchemaVersion * scSchemaVersionOffset scSchemaVersion * scSchemaVersionOffset
} }
@ -172,6 +174,8 @@ internal class RealmSessionStoreMigration @Inject constructor(
if (oldVersion < 52) MigrateSessionTo052(realm).perform() if (oldVersion < 52) MigrateSessionTo052(realm).perform()
if (oldVersion < 53) MigrateSessionTo053(realm).perform() if (oldVersion < 53) MigrateSessionTo053(realm).perform()
if (oldVersion < 54) MigrateSessionTo054(realm).perform() if (oldVersion < 54) MigrateSessionTo054(realm).perform()
if (oldVersion < 55) MigrateSessionTo055(realm).perform()
if (oldVersion < 56) MigrateSessionTo056(realm).perform()
if (oldScVersion <= 0) MigrateScSessionTo001(realm).perform() if (oldScVersion <= 0) MigrateScSessionTo001(realm).perform()
if (oldScVersion <= 1) MigrateScSessionTo002(realm).perform() if (oldScVersion <= 1) MigrateScSessionTo002(realm).perform()

View File

@ -51,6 +51,7 @@ internal object HomeServerCapabilitiesMapper {
externalAccountManagementUrl = entity.externalAccountManagementUrl, externalAccountManagementUrl = entity.externalAccountManagementUrl,
authenticationIssuer = entity.authenticationIssuer, authenticationIssuer = entity.authenticationIssuer,
disableNetworkConstraint = entity.disableNetworkConstraint, disableNetworkConstraint = entity.disableNetworkConstraint,
canUseAuthenticatedMedia = entity.canUseAuthenticatedMedia,
) )
} }

View File

@ -0,0 +1,57 @@
/*
* Copyright (c) 2024 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.internal.database.migration
import io.realm.DynamicRealm
import org.matrix.android.sdk.internal.database.model.EventEntityFields
import org.matrix.android.sdk.internal.database.model.EventInsertEntityFields
import org.matrix.android.sdk.internal.database.model.LocalRoomSummaryEntityFields
import org.matrix.android.sdk.internal.database.model.PushRulesEntityFields
import org.matrix.android.sdk.internal.database.model.PusherEntityFields
import org.matrix.android.sdk.internal.database.model.RoomEntityFields
import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntityFields
import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields
import org.matrix.android.sdk.internal.database.model.presence.UserPresenceEntityFields
import org.matrix.android.sdk.internal.util.database.RealmMigrator
// Some fields are now required due to upgrade of Kotlin version.
// See https://github.com/realm/realm-java/issues/7810 for more details.
internal class MigrateSessionTo055(realm: DynamicRealm) : RealmMigrator(realm, 55) {
override fun doMigrate(realm: DynamicRealm) {
realm.schema.get("EventEntity")
?.setRequired(EventEntityFields.SEND_STATE_STR, true)
?.setRequired(EventEntityFields.THREAD_NOTIFICATION_STATE_STR, true)
realm.schema.get("EventInsertEntity")
?.setRequired(EventInsertEntityFields.INSERT_TYPE_STR, true)
realm.schema.get("LocalRoomSummaryEntity")
?.setRequired(LocalRoomSummaryEntityFields.STATE_STR, true)
realm.schema.get("PushRulesEntity")
?.setRequired(PushRulesEntityFields.KIND_STR, true)
realm.schema.get("PusherEntity")
?.setRequired(PusherEntityFields.STATE_STR, true)
realm.schema.get("RoomEntity")
?.setRequired(RoomEntityFields.MEMBERSHIP_STR, true)
?.setRequired(RoomEntityFields.MEMBERS_LOAD_STATUS_STR, true)
realm.schema.get("RoomMemberSummaryEntity")
?.setRequired(RoomMemberSummaryEntityFields.MEMBERSHIP_STR, true)
realm.schema.get("RoomSummaryEntity")
?.setRequired(RoomSummaryEntityFields.MEMBERSHIP_STR, true)
?.setRequired(RoomSummaryEntityFields.VERSIONING_STATE_STR, true)
realm.schema.get("UserPresenceEntity")
?.setRequired(UserPresenceEntityFields.PRESENCE_STR, true)
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright (c) 2024 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.internal.database.migration
import io.realm.DynamicRealm
import org.matrix.android.sdk.internal.database.model.HomeServerCapabilitiesEntityFields
import org.matrix.android.sdk.internal.extensions.forceRefreshOfHomeServerCapabilities
import org.matrix.android.sdk.internal.util.database.RealmMigrator
internal class MigrateSessionTo056(realm: DynamicRealm) : RealmMigrator(realm, 56) {
override fun doMigrate(realm: DynamicRealm) {
realm.schema.get("HomeServerCapabilitiesEntity")
?.addField(HomeServerCapabilitiesEntityFields.CAN_USE_AUTHENTICATED_MEDIA, Boolean::class.java)
?.forceRefreshOfHomeServerCapabilities()
}
}

View File

@ -38,6 +38,7 @@ internal open class HomeServerCapabilitiesEntity(
var externalAccountManagementUrl: String? = null, var externalAccountManagementUrl: String? = null,
var authenticationIssuer: String? = null, var authenticationIssuer: String? = null,
var disableNetworkConstraint: Boolean? = null, var disableNetworkConstraint: Boolean? = null,
var canUseAuthenticatedMedia: Boolean = false,
) : RealmObject() { ) : RealmObject() {
companion object companion object

View File

@ -84,10 +84,10 @@ internal class WorkManagerProvider @Inject constructor(
workManager.enqueue(checkWorkerRequest) workManager.enqueue(checkWorkerRequest)
val checkWorkerLiveState = workManager.getWorkInfoByIdLiveData(checkWorkerRequest.id) val checkWorkerLiveState = workManager.getWorkInfoByIdLiveData(checkWorkerRequest.id)
val observer = object : Observer<WorkInfo> { val observer = object : Observer<WorkInfo> {
override fun onChanged(workInfo: WorkInfo?) { override fun onChanged(value: WorkInfo) {
if (workInfo?.state?.isFinished == true) { if (value.state.isFinished) {
checkWorkerLiveState.removeObserver(this) checkWorkerLiveState.removeObserver(this)
if (workInfo.state == WorkInfo.State.FAILED) { if (value.state == WorkInfo.State.FAILED) {
throw RuntimeException( throw RuntimeException(
"MatrixWorkerFactory is not being set on your worker configuration.\n" + "MatrixWorkerFactory is not being set on your worker configuration.\n" +
"Makes sure to add it to a DelegatingWorkerFactory if you have your own factory or use it directly.\n" + "Makes sure to add it to a DelegatingWorkerFactory if you have your own factory or use it directly.\n" +

View File

@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.network
import okhttp3.Interceptor import okhttp3.Interceptor
import okhttp3.Response import okhttp3.Response
import org.matrix.android.sdk.internal.network.httpclient.addAuthenticationHeader
import org.matrix.android.sdk.internal.network.token.AccessTokenProvider import org.matrix.android.sdk.internal.network.token.AccessTokenProvider
internal class AccessTokenInterceptor(private val accessTokenProvider: AccessTokenProvider) : Interceptor { internal class AccessTokenInterceptor(private val accessTokenProvider: AccessTokenProvider) : Interceptor {
@ -28,7 +29,7 @@ internal class AccessTokenInterceptor(private val accessTokenProvider: AccessTok
// Add the access token to all requests if it is set // Add the access token to all requests if it is set
accessTokenProvider.getToken()?.let { token -> accessTokenProvider.getToken()?.let { token ->
val newRequestBuilder = request.newBuilder() val newRequestBuilder = request.newBuilder()
newRequestBuilder.header(HttpHeaders.Authorization, "Bearer $token") newRequestBuilder.addAuthenticationHeader(token)
request = newRequestBuilder.build() request = newRequestBuilder.build()
} }

View File

@ -22,6 +22,7 @@ import android.content.IntentFilter
import android.net.ConnectivityManager import android.net.ConnectivityManager
import android.net.Network import android.net.Network
import android.os.Build import android.os.Build
import androidx.core.content.ContextCompat
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
@ -43,7 +44,12 @@ internal class FallbackNetworkCallbackStrategy @Inject constructor(
networkInfoReceiver.isConnectedCallback = { networkInfoReceiver.isConnectedCallback = {
hasChanged() hasChanged()
} }
context.registerReceiver(networkInfoReceiver, filter) ContextCompat.registerReceiver(
context,
networkInfoReceiver,
filter,
ContextCompat.RECEIVER_NOT_EXPORTED,
)
} }
override fun unregister() { override fun unregister() {

View File

@ -17,9 +17,11 @@
package org.matrix.android.sdk.internal.network.httpclient package org.matrix.android.sdk.internal.network.httpclient
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request
import org.matrix.android.sdk.api.MatrixConfiguration import org.matrix.android.sdk.api.MatrixConfiguration
import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
import org.matrix.android.sdk.internal.network.AccessTokenInterceptor import org.matrix.android.sdk.internal.network.AccessTokenInterceptor
import org.matrix.android.sdk.internal.network.HttpHeaders
import org.matrix.android.sdk.internal.network.interceptors.CurlLoggingInterceptor import org.matrix.android.sdk.internal.network.interceptors.CurlLoggingInterceptor
import org.matrix.android.sdk.internal.network.ssl.CertUtil import org.matrix.android.sdk.internal.network.ssl.CertUtil
import org.matrix.android.sdk.internal.network.token.AccessTokenProvider import org.matrix.android.sdk.internal.network.token.AccessTokenProvider
@ -66,3 +68,10 @@ internal fun OkHttpClient.Builder.applyMatrixConfiguration(matrixConfiguration:
return this return this
} }
fun Request.Builder.addAuthenticationHeader(accessToken: String?): Request.Builder {
if (accessToken != null) {
header(HttpHeaders.Authorization, "Bearer $accessToken")
}
return this
}

View File

@ -34,8 +34,11 @@ import org.matrix.android.sdk.api.session.crypto.attachments.ElementToDecrypt
import org.matrix.android.sdk.api.session.file.FileService import org.matrix.android.sdk.api.session.file.FileService
import org.matrix.android.sdk.api.util.md5 import org.matrix.android.sdk.api.util.md5
import org.matrix.android.sdk.internal.crypto.attachments.MXEncryptedAttachments import org.matrix.android.sdk.internal.crypto.attachments.MXEncryptedAttachments
import org.matrix.android.sdk.internal.di.Authenticated
import org.matrix.android.sdk.internal.di.SessionDownloadsDirectory import org.matrix.android.sdk.internal.di.SessionDownloadsDirectory
import org.matrix.android.sdk.internal.di.UnauthenticatedWithCertificateWithProgress import org.matrix.android.sdk.internal.di.UnauthenticatedWithCertificateWithProgress
import org.matrix.android.sdk.internal.network.httpclient.addAuthenticationHeader
import org.matrix.android.sdk.internal.network.token.AccessTokenProvider
import org.matrix.android.sdk.internal.session.download.DownloadProgressInterceptor.Companion.DOWNLOAD_PROGRESS_INTERCEPTOR_HEADER import org.matrix.android.sdk.internal.session.download.DownloadProgressInterceptor.Companion.DOWNLOAD_PROGRESS_INTERCEPTOR_HEADER
import org.matrix.android.sdk.internal.util.file.AtomicFileCreator import org.matrix.android.sdk.internal.util.file.AtomicFileCreator
import org.matrix.android.sdk.internal.util.time.Clock import org.matrix.android.sdk.internal.util.time.Clock
@ -54,6 +57,7 @@ internal class DefaultFileService @Inject constructor(
private val okHttpClient: OkHttpClient, private val okHttpClient: OkHttpClient,
private val coroutineDispatchers: MatrixCoroutineDispatchers, private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val clock: Clock, private val clock: Clock,
@Authenticated private val accessTokenProvider: AccessTokenProvider,
) : FileService { ) : FileService {
// Legacy folder, will be deleted // Legacy folder, will be deleted
@ -124,21 +128,26 @@ internal class DefaultFileService @Inject constructor(
val cachedFiles = getFiles(url, fileName, mimeType, elementToDecrypt != null) val cachedFiles = getFiles(url, fileName, mimeType, elementToDecrypt != null)
if (!cachedFiles.file.exists()) { if (!cachedFiles.file.exists()) {
val resolvedUrl = contentUrlResolver.resolveForDownload(url, elementToDecrypt) ?: throw IllegalArgumentException("url is null") val resolvedMethod = contentUrlResolver.resolveForDownload(url, elementToDecrypt) ?: throw IllegalArgumentException("url is null")
val request = when (resolvedUrl) { val request = when (resolvedMethod) {
is ContentUrlResolver.ResolvedMethod.GET -> { is ContentUrlResolver.ResolvedMethod.GET -> {
Request.Builder() val requestBuilder = Request.Builder()
.url(resolvedUrl.url) .url(resolvedMethod.url)
.header(DOWNLOAD_PROGRESS_INTERCEPTOR_HEADER, url) .header(DOWNLOAD_PROGRESS_INTERCEPTOR_HEADER, url)
.build()
if (contentUrlResolver.requiresAuthentication(resolvedMethod.url)) {
val accessToken = accessTokenProvider.getToken()
requestBuilder.addAuthenticationHeader(accessToken)
}
requestBuilder.build()
} }
is ContentUrlResolver.ResolvedMethod.POST -> { is ContentUrlResolver.ResolvedMethod.POST -> {
Request.Builder() Request.Builder()
.url(resolvedUrl.url) .url(resolvedMethod.url)
.header(DOWNLOAD_PROGRESS_INTERCEPTOR_HEADER, url) .header(DOWNLOAD_PROGRESS_INTERCEPTOR_HEADER, url)
.post(resolvedUrl.jsonBody.toRequestBody("application/json".toMediaType())) .post(resolvedMethod.jsonBody.toRequestBody("application/json".toMediaType()))
.build() .build()
} }
} }

View File

@ -67,6 +67,7 @@ import org.matrix.android.sdk.api.util.appendParamToUrl
import org.matrix.android.sdk.internal.auth.SSO_UIA_FALLBACK_PATH import org.matrix.android.sdk.internal.auth.SSO_UIA_FALLBACK_PATH
import org.matrix.android.sdk.internal.auth.SessionParamsStore import org.matrix.android.sdk.internal.auth.SessionParamsStore
import org.matrix.android.sdk.internal.database.tools.RealmDebugTools import org.matrix.android.sdk.internal.database.tools.RealmDebugTools
import org.matrix.android.sdk.internal.di.Authenticated
import org.matrix.android.sdk.internal.di.ContentScannerDatabase import org.matrix.android.sdk.internal.di.ContentScannerDatabase
import org.matrix.android.sdk.internal.di.CryptoDatabase import org.matrix.android.sdk.internal.di.CryptoDatabase
import org.matrix.android.sdk.internal.di.IdentityDatabase import org.matrix.android.sdk.internal.di.IdentityDatabase
@ -131,6 +132,8 @@ internal class DefaultSession @Inject constructor(
private val eventStreamService: Lazy<EventStreamService>, private val eventStreamService: Lazy<EventStreamService>,
@UnauthenticatedWithCertificate @UnauthenticatedWithCertificate
private val unauthenticatedWithCertificateOkHttpClient: Lazy<OkHttpClient>, private val unauthenticatedWithCertificateOkHttpClient: Lazy<OkHttpClient>,
@Authenticated
private val authenticatedOkHttpClient: Lazy<OkHttpClient>,
private val sessionState: SessionState, private val sessionState: SessionState,
) : Session, ) : Session,
GlobalErrorHandler.Listener { GlobalErrorHandler.Listener {
@ -234,6 +237,10 @@ internal class DefaultSession @Inject constructor(
return unauthenticatedWithCertificateOkHttpClient.get() return unauthenticatedWithCertificateOkHttpClient.get()
} }
override fun getAuthenticatedOkHttpClient(): OkHttpClient {
return authenticatedOkHttpClient.get()
}
override fun addListener(listener: Session.Listener) { override fun addListener(listener: Session.Listener) {
sessionListeners.addListener(listener) sessionListeners.addListener(listener)
} }

View File

@ -83,6 +83,7 @@ import org.matrix.android.sdk.internal.session.events.DefaultEventService
import org.matrix.android.sdk.internal.session.homeserver.DefaultHomeServerCapabilitiesService import org.matrix.android.sdk.internal.session.homeserver.DefaultHomeServerCapabilitiesService
import org.matrix.android.sdk.internal.session.identity.DefaultIdentityService import org.matrix.android.sdk.internal.session.identity.DefaultIdentityService
import org.matrix.android.sdk.internal.session.integrationmanager.IntegrationManager import org.matrix.android.sdk.internal.session.integrationmanager.IntegrationManager
import org.matrix.android.sdk.internal.session.media.DefaultIsAuthenticatedMediaSupported
import org.matrix.android.sdk.internal.session.openid.DefaultOpenIdService import org.matrix.android.sdk.internal.session.openid.DefaultOpenIdService
import org.matrix.android.sdk.internal.session.permalinks.DefaultPermalinkService import org.matrix.android.sdk.internal.session.permalinks.DefaultPermalinkService
import org.matrix.android.sdk.internal.session.room.EventRelationsAggregationProcessor import org.matrix.android.sdk.internal.session.room.EventRelationsAggregationProcessor
@ -365,6 +366,10 @@ internal abstract class SessionModule {
@IntoSet @IntoSet
abstract fun bindEventInsertObserver(observer: EventInsertLiveObserver): SessionLifecycleObserver abstract fun bindEventInsertObserver(observer: EventInsertLiveObserver): SessionLifecycleObserver
@Binds
@IntoSet
abstract fun bindIsMediaAuthenticated(observer: DefaultIsAuthenticatedMediaSupported): SessionLifecycleObserver
@Binds @Binds
@IntoSet @IntoSet
abstract fun bindIntegrationManager(manager: IntegrationManager): SessionLifecycleObserver abstract fun bindIntegrationManager(manager: IntegrationManager): SessionLifecycleObserver

View File

@ -25,16 +25,18 @@ import org.matrix.android.sdk.api.session.crypto.attachments.ElementToDecrypt
import org.matrix.android.sdk.internal.network.NetworkConstants import org.matrix.android.sdk.internal.network.NetworkConstants
import org.matrix.android.sdk.internal.session.contentscanner.ScanEncryptorUtils import org.matrix.android.sdk.internal.session.contentscanner.ScanEncryptorUtils
import org.matrix.android.sdk.internal.session.contentscanner.model.toJson import org.matrix.android.sdk.internal.session.contentscanner.model.toJson
import org.matrix.android.sdk.internal.session.media.IsAuthenticatedMediaSupported
import org.matrix.android.sdk.internal.util.ensureTrailingSlash import org.matrix.android.sdk.internal.util.ensureTrailingSlash
import javax.inject.Inject import javax.inject.Inject
internal class DefaultContentUrlResolver @Inject constructor( internal class DefaultContentUrlResolver @Inject constructor(
homeServerConnectionConfig: HomeServerConnectionConfig, homeServerConnectionConfig: HomeServerConnectionConfig,
private val scannerService: ContentScannerService private val scannerService: ContentScannerService,
private val isAuthenticatedMediaSupported: IsAuthenticatedMediaSupported,
) : ContentUrlResolver { ) : ContentUrlResolver {
private val baseUrl = homeServerConnectionConfig.homeServerUriBase.toString().ensureTrailingSlash() private val baseUrl = homeServerConnectionConfig.homeServerUriBase.toString().ensureTrailingSlash()
private val authenticatedMediaApiPath = baseUrl + NetworkConstants.URI_API_PREFIX_PATH_V1 + "media/"
override val uploadUrl = baseUrl + NetworkConstants.URI_API_MEDIA_PREFIX_PATH_R0 + "upload" override val uploadUrl = baseUrl + NetworkConstants.URI_API_MEDIA_PREFIX_PATH_R0 + "upload"
override fun resolveForDownload(contentUrl: String?, elementToDecrypt: ElementToDecrypt?): ContentUrlResolver.ResolvedMethod? { override fun resolveForDownload(contentUrl: String?, elementToDecrypt: ElementToDecrypt?): ContentUrlResolver.ResolvedMethod? {
@ -80,15 +82,20 @@ internal class DefaultContentUrlResolver @Inject constructor(
} }
} }
override fun requiresAuthentication(resolvedUrl: String): Boolean {
return resolvedUrl.startsWith(authenticatedMediaApiPath)
}
private fun resolve( private fun resolve(
contentUrl: String, contentUrl: String,
toThumbnail: Boolean, toThumbnail: Boolean,
params: String = "" params: String = ""
): String { ): String {
var serverAndMediaId = contentUrl.removeMxcPrefix() var serverAndMediaId = contentUrl.removeMxcPrefix()
val apiPath = if (scannerService.isScannerEnabled()) { val apiPath = if (scannerService.isScannerEnabled()) {
NetworkConstants.URI_API_PREFIX_PATH_MEDIA_PROXY_UNSTABLE NetworkConstants.URI_API_PREFIX_PATH_MEDIA_PROXY_UNSTABLE
} else if (isAuthenticatedMediaSupported()) {
NetworkConstants.URI_API_PREFIX_PATH_V1 + "media/"
} else { } else {
NetworkConstants.URI_API_MEDIA_PREFIX_PATH_R0 NetworkConstants.URI_API_MEDIA_PREFIX_PATH_R0
} }

View File

@ -17,7 +17,7 @@
package org.matrix.android.sdk.internal.session.contentscanner.db package org.matrix.android.sdk.internal.session.contentscanner.db
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations import androidx.lifecycle.map
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import io.realm.Realm import io.realm.Realm
import io.realm.RealmConfiguration import io.realm.RealmConfiguration
@ -138,7 +138,7 @@ internal class RealmContentScannerStore @Inject constructor(
entity.toModel() entity.toModel()
} }
) )
return Transformations.map(liveData) { return liveData.map {
it.firstOrNull().toOptional() it.firstOrNull().toOptional()
} }
} }

View File

@ -20,9 +20,11 @@ import com.zhuinden.monarchy.Monarchy
import org.matrix.android.sdk.api.MatrixPatterns.getServerName import org.matrix.android.sdk.api.MatrixPatterns.getServerName
import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
import org.matrix.android.sdk.api.auth.wellknown.WellknownResult import org.matrix.android.sdk.api.auth.wellknown.WellknownResult
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.extensions.orTrue import org.matrix.android.sdk.api.extensions.orTrue
import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilities
import org.matrix.android.sdk.internal.auth.version.Versions import org.matrix.android.sdk.internal.auth.version.Versions
import org.matrix.android.sdk.internal.auth.version.doesServerSupportAuthenticatedMedia
import org.matrix.android.sdk.internal.auth.version.doesServerSupportLogoutDevices import org.matrix.android.sdk.internal.auth.version.doesServerSupportLogoutDevices
import org.matrix.android.sdk.internal.auth.version.doesServerSupportQrCodeLogin import org.matrix.android.sdk.internal.auth.version.doesServerSupportQrCodeLogin
import org.matrix.android.sdk.internal.auth.version.doesServerSupportRedactionOfRelatedEvents import org.matrix.android.sdk.internal.auth.version.doesServerSupportRedactionOfRelatedEvents
@ -38,8 +40,9 @@ import org.matrix.android.sdk.internal.di.UserId
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
import org.matrix.android.sdk.internal.network.executeRequest import org.matrix.android.sdk.internal.network.executeRequest
import org.matrix.android.sdk.internal.session.integrationmanager.IntegrationManagerConfigExtractor import org.matrix.android.sdk.internal.session.integrationmanager.IntegrationManagerConfigExtractor
import org.matrix.android.sdk.internal.session.media.AuthenticatedMediaAPI
import org.matrix.android.sdk.internal.session.media.GetMediaConfigResult import org.matrix.android.sdk.internal.session.media.GetMediaConfigResult
import org.matrix.android.sdk.internal.session.media.MediaAPI import org.matrix.android.sdk.internal.session.media.UnauthenticatedMediaAPI
import org.matrix.android.sdk.internal.task.Task import org.matrix.android.sdk.internal.task.Task
import org.matrix.android.sdk.internal.util.awaitTransaction import org.matrix.android.sdk.internal.util.awaitTransaction
import org.matrix.android.sdk.internal.wellknown.GetWellknownTask import org.matrix.android.sdk.internal.wellknown.GetWellknownTask
@ -55,7 +58,8 @@ internal interface GetHomeServerCapabilitiesTask : Task<GetHomeServerCapabilitie
internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor( internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor(
private val capabilitiesAPI: CapabilitiesAPI, private val capabilitiesAPI: CapabilitiesAPI,
private val mediaAPI: MediaAPI, private val unauthenticatedMediaAPI: UnauthenticatedMediaAPI,
private val authenticatedMediaAPI: AuthenticatedMediaAPI,
@SessionDatabase private val monarchy: Monarchy, @SessionDatabase private val monarchy: Monarchy,
private val globalErrorReceiver: GlobalErrorReceiver, private val globalErrorReceiver: GlobalErrorReceiver,
private val getWellknownTask: GetWellknownTask, private val getWellknownTask: GetWellknownTask,
@ -70,7 +74,6 @@ internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor(
if (!doRequest) { if (!doRequest) {
monarchy.awaitTransaction { realm -> monarchy.awaitTransaction { realm ->
val homeServerCapabilitiesEntity = HomeServerCapabilitiesEntity.getOrCreate(realm) val homeServerCapabilitiesEntity = HomeServerCapabilitiesEntity.getOrCreate(realm)
doRequest = homeServerCapabilitiesEntity.lastUpdatedTimestamp + MIN_DELAY_BETWEEN_TWO_REQUEST_MILLIS < Date().time doRequest = homeServerCapabilitiesEntity.lastUpdatedTimestamp + MIN_DELAY_BETWEEN_TWO_REQUEST_MILLIS < Date().time
} }
} }
@ -85,18 +88,22 @@ internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor(
} }
}.getOrNull() }.getOrNull()
val mediaConfig = runCatching {
executeRequest(globalErrorReceiver) {
mediaAPI.getMediaConfig()
}
}.getOrNull()
val versions = runCatching { val versions = runCatching {
executeRequest(null) { executeRequest(null) {
capabilitiesAPI.getVersions() capabilitiesAPI.getVersions()
} }
}.getOrNull() }.getOrNull()
val mediaConfig = runCatching {
executeRequest(globalErrorReceiver) {
if (versions?.doesServerSupportAuthenticatedMedia().orFalse()) {
authenticatedMediaAPI.getMediaConfig()
} else {
unauthenticatedMediaAPI.getMediaConfig()
}
}
}.getOrNull()
// Domain may include a port (eg, matrix.org:8080) // Domain may include a port (eg, matrix.org:8080)
// Per https://spec.matrix.org/latest/client-server-api/#well-known-uri we should extract the hostname from the server name // Per https://spec.matrix.org/latest/client-server-api/#well-known-uri we should extract the hostname from the server name
// So we take everything before the last : as the domain for the well-known task. // So we take everything before the last : as the domain for the well-known task.
@ -155,6 +162,8 @@ internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor(
getVersionResult.doesServerSupportRemoteToggleOfPushNotifications() getVersionResult.doesServerSupportRemoteToggleOfPushNotifications()
homeServerCapabilitiesEntity.canRedactEventWithRelations = homeServerCapabilitiesEntity.canRedactEventWithRelations =
getVersionResult.doesServerSupportRedactionOfRelatedEvents() getVersionResult.doesServerSupportRedactionOfRelatedEvents()
homeServerCapabilitiesEntity.canUseAuthenticatedMedia =
getVersionResult.doesServerSupportAuthenticatedMedia()
} }
if (getWellknownResult != null && getWellknownResult is WellknownResult.Prompt) { if (getWellknownResult != null && getWellknownResult is WellknownResult.Prompt) {

View File

@ -17,7 +17,7 @@
package org.matrix.android.sdk.internal.session.homeserver package org.matrix.android.sdk.internal.session.homeserver
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations import androidx.lifecycle.map
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import io.realm.Realm import io.realm.Realm
import io.realm.kotlin.where import io.realm.kotlin.where
@ -46,7 +46,7 @@ internal class HomeServerCapabilitiesDataSource @Inject constructor(
{ realm: Realm -> realm.where<HomeServerCapabilitiesEntity>() }, { realm: Realm -> realm.where<HomeServerCapabilitiesEntity>() },
{ HomeServerCapabilitiesMapper.map(it) } { HomeServerCapabilitiesMapper.map(it) }
) )
return Transformations.map(liveData) { return liveData.map {
it.firstOrNull().toOptional() it.firstOrNull().toOptional()
} }
} }

View File

@ -84,7 +84,10 @@ internal class DefaultIdentityService @Inject constructor(
private val sessionParams: SessionParams private val sessionParams: SessionParams
) : IdentityService, SessionLifecycleObserver { ) : IdentityService, SessionLifecycleObserver {
private val lifecycleOwner: LifecycleOwner = LifecycleOwner { lifecycleRegistry } private val lifecycleOwner: LifecycleOwner = object : LifecycleOwner {
override val lifecycle: Lifecycle
get() = lifecycleRegistry
}
private val lifecycleRegistry: LifecycleRegistry = LifecycleRegistry(lifecycleOwner) private val lifecycleRegistry: LifecycleRegistry = LifecycleRegistry(lifecycleOwner)
private val listeners = mutableSetOf<IdentityServiceListener>() private val listeners = mutableSetOf<IdentityServiceListener>()

View File

@ -64,7 +64,10 @@ internal class IntegrationManager @Inject constructor(
SessionLifecycleObserver { SessionLifecycleObserver {
private val currentConfigs = ArrayList<IntegrationManagerConfig>() private val currentConfigs = ArrayList<IntegrationManagerConfig>()
private val lifecycleOwner: LifecycleOwner = LifecycleOwner { lifecycleRegistry } private val lifecycleOwner: LifecycleOwner = object : LifecycleOwner {
override val lifecycle: Lifecycle
get() = lifecycleRegistry
}
private val lifecycleRegistry: LifecycleRegistry = LifecycleRegistry(lifecycleOwner) private val lifecycleRegistry: LifecycleRegistry = LifecycleRegistry(lifecycleOwner)
private val listeners = HashSet<IntegrationManagerService.Listener>() private val listeners = HashSet<IntegrationManagerService.Listener>()

View File

@ -0,0 +1,45 @@
/*
* Copyright (C) 2024 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.internal.session.media
import org.matrix.android.sdk.api.util.JsonDict
import org.matrix.android.sdk.internal.network.NetworkConstants
import retrofit2.http.GET
import retrofit2.http.Query
/**
* Implementation of the media repository API using the new Authenticated media API.
*/
internal interface AuthenticatedMediaAPI : MediaAPI {
/**
* Retrieve the configuration of the content repository
* Ref: https://spec.matrix.org/v1.11/client-server-api/#get_matrixclientv1mediaconfig
*/
@GET(NetworkConstants.URI_API_PREFIX_PATH_V1 + "media/config")
override suspend fun getMediaConfig(): GetMediaConfigResult
/**
* Get information about a URL for the client. Typically this is called when a client
* sees a URL in a message and wants to render a preview for the user.
* Ref: https://spec.matrix.org/v1.11/client-server-api/#get_matrixclientv1mediapreview_url
* @param url Required. The URL to get a preview of.
* @param ts The preferred point in time to return a preview for. The server may return a newer version
* if it does not have the requested version available.
*/
@GET(NetworkConstants.URI_API_PREFIX_PATH_V1 + "media/preview_url")
override suspend fun getPreviewUrlData(@Query("url") url: String, @Query("ts") ts: Long?): JsonDict
}

View File

@ -0,0 +1,60 @@
/*
* Copyright (C) 2024 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.internal.session.media
import com.zhuinden.monarchy.Monarchy
import io.realm.Realm
import io.realm.RealmResults
import org.matrix.android.sdk.internal.database.RealmLiveEntityObserver
import org.matrix.android.sdk.internal.database.model.HomeServerCapabilitiesEntity
import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.session.SessionScope
import timber.log.Timber
import javax.inject.Inject
@SessionScope
internal class DefaultIsAuthenticatedMediaSupported @Inject constructor(
@SessionDatabase private val monarchy: Monarchy,
) :
IsAuthenticatedMediaSupported,
RealmLiveEntityObserver<HomeServerCapabilitiesEntity>(monarchy.realmConfiguration) {
override fun invoke(): Boolean {
return canUseAuthenticatedMedia
}
override val query = Monarchy.Query {
it.where(HomeServerCapabilitiesEntity::class.java)
}
override fun onChange(results: RealmResults<HomeServerCapabilitiesEntity>) {
canUseAuthenticatedMedia = results.canUseAuthenticatedMedia()
Timber.d("canUseAuthenticatedMedia: $canUseAuthenticatedMedia")
}
private var canUseAuthenticatedMedia = getInitialValue()
private fun getInitialValue(): Boolean {
return Realm.getInstance(monarchy.realmConfiguration).use { realm ->
query.createQuery(realm).findAll().canUseAuthenticatedMedia()
}
}
private fun RealmResults<HomeServerCapabilitiesEntity>.canUseAuthenticatedMedia(): Boolean {
return firstOrNull()?.canUseAuthenticatedMedia ?: false
}
}

View File

@ -41,7 +41,7 @@ internal interface GetPreviewUrlTask : Task<GetPreviewUrlTask.Params, PreviewUrl
} }
internal class DefaultGetPreviewUrlTask @Inject constructor( internal class DefaultGetPreviewUrlTask @Inject constructor(
private val mediaAPI: MediaAPI, private val mediaAPIProvider: MediaAPIProvider,
private val globalErrorReceiver: GlobalErrorReceiver, private val globalErrorReceiver: GlobalErrorReceiver,
@SessionDatabase private val monarchy: Monarchy @SessionDatabase private val monarchy: Monarchy
) : GetPreviewUrlTask { ) : GetPreviewUrlTask {
@ -66,7 +66,7 @@ internal class DefaultGetPreviewUrlTask @Inject constructor(
private suspend fun doRequest(url: String, timestamp: Long?): PreviewUrlData { private suspend fun doRequest(url: String, timestamp: Long?): PreviewUrlData {
return executeRequest(globalErrorReceiver) { return executeRequest(globalErrorReceiver) {
mediaAPI.getPreviewUrlData(url, timestamp) mediaAPIProvider.getMediaAPI().getPreviewUrlData(url, timestamp)
} }
.toPreviewUrlData(url) .toPreviewUrlData(url)
} }

View File

@ -30,13 +30,13 @@ internal interface GetRawPreviewUrlTask : Task<GetRawPreviewUrlTask.Params, Json
} }
internal class DefaultGetRawPreviewUrlTask @Inject constructor( internal class DefaultGetRawPreviewUrlTask @Inject constructor(
private val mediaAPI: MediaAPI, private val mediaAPIProvider: MediaAPIProvider,
private val globalErrorReceiver: GlobalErrorReceiver private val globalErrorReceiver: GlobalErrorReceiver
) : GetRawPreviewUrlTask { ) : GetRawPreviewUrlTask {
override suspend fun execute(params: GetRawPreviewUrlTask.Params): JsonDict { override suspend fun execute(params: GetRawPreviewUrlTask.Params): JsonDict {
return executeRequest(globalErrorReceiver) { return executeRequest(globalErrorReceiver) {
mediaAPI.getPreviewUrlData(params.url, params.timestamp) mediaAPIProvider.getMediaAPI().getPreviewUrlData(params.url, params.timestamp)
} }
} }
} }

View File

@ -0,0 +1,21 @@
/*
* Copyright (C) 2024 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.internal.session.media
interface IsAuthenticatedMediaSupported {
operator fun invoke(): Boolean
}

View File

@ -1,11 +1,11 @@
/* /*
* Copyright 2020 The Matrix.org Foundation C.I.C. * Copyright (C) 2024 The Matrix.org Foundation C.I.C.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
@ -17,26 +17,11 @@
package org.matrix.android.sdk.internal.session.media package org.matrix.android.sdk.internal.session.media
import org.matrix.android.sdk.api.util.JsonDict import org.matrix.android.sdk.api.util.JsonDict
import org.matrix.android.sdk.internal.network.NetworkConstants
import retrofit2.http.GET
import retrofit2.http.Query
/**
* This defines some method to interact with the media repository.
*/
internal interface MediaAPI { internal interface MediaAPI {
/**
* Retrieve the configuration of the content repository
* Ref: https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-media-r0-config
*/
@GET(NetworkConstants.URI_API_MEDIA_PREFIX_PATH_R0 + "config")
suspend fun getMediaConfig(): GetMediaConfigResult suspend fun getMediaConfig(): GetMediaConfigResult
suspend fun getPreviewUrlData(url: String, ts: Long?): JsonDict
/**
* Get information about a URL for the client. Typically this is called when a client
* sees a URL in a message and wants to render a preview for the user.
* Ref: https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-media-r0-preview-url
* @param url Required. The URL to get a preview of.
* @param ts The preferred point in time to return a preview for. The server may return a newer version
* if it does not have the requested version available.
*/
@GET(NetworkConstants.URI_API_MEDIA_PREFIX_PATH_R0 + "preview_url")
suspend fun getPreviewUrlData(@Query("url") url: String, @Query("ts") ts: Long?): JsonDict
} }

View File

@ -0,0 +1,34 @@
/*
* Copyright (C) 2024 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.internal.session.media
import javax.inject.Inject
internal class MediaAPIProvider @Inject constructor(
private val isAuthenticatedMediaSupported: IsAuthenticatedMediaSupported,
private val authenticatedMediaAPI: AuthenticatedMediaAPI,
private val unauthenticatedMediaAPI: UnauthenticatedMediaAPI,
) {
fun getMediaAPI(): MediaAPI {
return if (isAuthenticatedMediaSupported()) {
authenticatedMediaAPI
} else {
unauthenticatedMediaAPI
}
}
}

View File

@ -31,11 +31,21 @@ internal abstract class MediaModule {
@Provides @Provides
@JvmStatic @JvmStatic
@SessionScope @SessionScope
fun providesMediaAPI(retrofit: Retrofit): MediaAPI { fun providesUnauthenticatedMediaAPI(retrofit: Retrofit): UnauthenticatedMediaAPI {
return retrofit.create(MediaAPI::class.java) return retrofit.create(UnauthenticatedMediaAPI::class.java)
}
@Provides
@JvmStatic
@SessionScope
fun providesAuthenticatedMediaAPI(retrofit: Retrofit): AuthenticatedMediaAPI {
return retrofit.create(AuthenticatedMediaAPI::class.java)
} }
} }
@Binds
abstract fun bindIsAuthenticatedMediaSupported(isAuthenticatedMediaSupported: DefaultIsAuthenticatedMediaSupported): IsAuthenticatedMediaSupported
@Binds @Binds
abstract fun bindMediaService(service: DefaultMediaService): MediaService abstract fun bindMediaService(service: DefaultMediaService): MediaService

View File

@ -0,0 +1,42 @@
/*
* Copyright (C) 2024 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.internal.session.media
import org.matrix.android.sdk.api.util.JsonDict
import org.matrix.android.sdk.internal.network.NetworkConstants
import retrofit2.http.GET
import retrofit2.http.Query
internal interface UnauthenticatedMediaAPI : MediaAPI {
/**
* Retrieve the configuration of the content repository
* Ref: https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-media-r0-config
*/
@GET(NetworkConstants.URI_API_MEDIA_PREFIX_PATH_R0 + "config")
override suspend fun getMediaConfig(): GetMediaConfigResult
/**
* Get information about a URL for the client. Typically this is called when a client
* sees a URL in a message and wants to render a preview for the user.
* Ref: https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-media-r0-preview-url
* @param url Required. The URL to get a preview of.
* @param ts The preferred point in time to return a preview for. The server may return a newer version
* if it does not have the requested version available.
*/
@GET(NetworkConstants.URI_API_MEDIA_PREFIX_PATH_R0 + "preview_url")
override suspend fun getPreviewUrlData(@Query("url") url: String, @Query("ts") ts: Long?): JsonDict
}

View File

@ -16,7 +16,7 @@
package org.matrix.android.sdk.internal.session.pushrules package org.matrix.android.sdk.internal.session.pushrules
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations import androidx.lifecycle.map
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.pushrules.Action import org.matrix.android.sdk.api.session.pushrules.Action
@ -160,7 +160,7 @@ internal class DefaultPushRuleService @Inject constructor(
result.pushRules.map(PushRuleEntity::ruleId).filter { !it.startsWith(".") } result.pushRules.map(PushRuleEntity::ruleId).filter { !it.startsWith(".") }
} }
) )
return Transformations.map(liveData) { results -> return liveData.map { results ->
results.firstOrNull().orEmpty().toSet() results.firstOrNull().orEmpty().toSet()
} }
} }

View File

@ -17,7 +17,7 @@
package org.matrix.android.sdk.internal.session.room package org.matrix.android.sdk.internal.session.room
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations import androidx.lifecycle.map
import androidx.paging.PagedList import androidx.paging.PagedList
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.Event
@ -242,7 +242,7 @@ internal class DefaultRoomService @Inject constructor(
}, },
{ it.asDomain() } { it.asDomain() }
) )
return Transformations.map(liveData) { results -> return liveData.map { results ->
results.firstOrNull().toOptional() results.firstOrNull().toOptional()
} }
} }

View File

@ -17,7 +17,7 @@
package org.matrix.android.sdk.internal.session.room package org.matrix.android.sdk.internal.session.room
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations import androidx.lifecycle.map
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import io.realm.Realm import io.realm.Realm
import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.extensions.orFalse
@ -48,7 +48,7 @@ internal class RoomDataSource @Inject constructor(
} }
) )
return Transformations.map(liveData) { results -> return liveData.map { results ->
results.firstOrNull().orFalse() results.firstOrNull().orFalse()
} }
} }

View File

@ -18,7 +18,7 @@ package org.matrix.android.sdk.internal.session.room.accountdata
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MediatorLiveData import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.Transformations import androidx.lifecycle.map
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import io.realm.Realm import io.realm.Realm
import io.realm.RealmQuery import io.realm.RealmQuery
@ -44,7 +44,7 @@ internal class RoomAccountDataDataSource @Inject constructor(
} }
fun getLiveAccountDataEvent(roomId: String, type: String): LiveData<Optional<RoomAccountDataEvent>> { fun getLiveAccountDataEvent(roomId: String, type: String): LiveData<Optional<RoomAccountDataEvent>> {
return Transformations.map(getLiveAccountDataEvents(roomId, setOf(type))) { return getLiveAccountDataEvents(roomId, setOf(type)).map {
it.firstOrNull().toOptional() it.firstOrNull().toOptional()
} }
} }

View File

@ -17,7 +17,7 @@
package org.matrix.android.sdk.internal.session.room.draft package org.matrix.android.sdk.internal.session.room.draft
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations import androidx.lifecycle.map
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import io.realm.Realm import io.realm.Realm
import io.realm.kotlin.createObject import io.realm.kotlin.createObject
@ -72,7 +72,7 @@ internal class DraftRepository @Inject constructor(
} }
} }
) )
return Transformations.map(liveData) { return liveData.map {
it.firstOrNull()?.firstOrNull().toOptional() it.firstOrNull()?.firstOrNull().toOptional()
} }
} }

View File

@ -17,7 +17,7 @@
package org.matrix.android.sdk.internal.session.room.location package org.matrix.android.sdk.internal.session.room.location
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations import androidx.lifecycle.map
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory import dagger.assisted.AssistedFactory
@ -119,13 +119,13 @@ internal class DefaultLocationSharingService @AssistedInject constructor(
} }
override fun getLiveLocationShareSummary(beaconInfoEventId: String): LiveData<Optional<LiveLocationShareAggregatedSummary>> { override fun getLiveLocationShareSummary(beaconInfoEventId: String): LiveData<Optional<LiveLocationShareAggregatedSummary>> {
return Transformations.map( return monarchy
monarchy.findAllMappedWithChanges( .findAllMappedWithChanges(
{ LiveLocationShareAggregatedSummaryEntity.where(it, roomId = roomId, eventId = beaconInfoEventId) }, { LiveLocationShareAggregatedSummaryEntity.where(it, roomId = roomId, eventId = beaconInfoEventId) },
liveLocationShareAggregatedSummaryMapper liveLocationShareAggregatedSummaryMapper
) )
) { .map {
it.firstOrNull().toOptional() it.firstOrNull().toOptional()
} }
} }
} }

View File

@ -17,7 +17,7 @@
package org.matrix.android.sdk.internal.session.room.notification package org.matrix.android.sdk.internal.session.room.notification
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations import androidx.lifecycle.map
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory import dagger.assisted.AssistedFactory
@ -42,7 +42,7 @@ internal class DefaultRoomPushRuleService @AssistedInject constructor(
} }
override fun getLiveRoomNotificationState(): LiveData<RoomNotificationState> { override fun getLiveRoomNotificationState(): LiveData<RoomNotificationState> {
return Transformations.map(getPushRuleForRoom()) { return getPushRuleForRoom().map {
it?.toRoomNotificationState() ?: RoomNotificationState.ALL_MESSAGES it?.toRoomNotificationState() ?: RoomNotificationState.ALL_MESSAGES
} }
} }
@ -60,7 +60,7 @@ internal class DefaultRoomPushRuleService @AssistedInject constructor(
result.toRoomPushRule() result.toRoomPushRule()
} }
) )
return Transformations.map(liveData) { results -> return liveData.map { results ->
results.firstOrNull() results.firstOrNull()
} }
} }

View File

@ -17,7 +17,8 @@
package org.matrix.android.sdk.internal.session.room.poll package org.matrix.android.sdk.internal.session.room.poll
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations import androidx.lifecycle.map
import androidx.lifecycle.switchMap
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory import dagger.assisted.AssistedFactory
@ -112,7 +113,7 @@ internal class DefaultPollHistoryService @AssistedInject constructor(
override fun getPollEvents(): LiveData<List<TimelineEvent>> { override fun getPollEvents(): LiveData<List<TimelineEvent>> {
val pollHistoryStatusLiveData = getPollHistoryStatus() val pollHistoryStatusLiveData = getPollHistoryStatus()
return Transformations.switchMap(pollHistoryStatusLiveData) { results -> return pollHistoryStatusLiveData.switchMap { results ->
val oldestTimestamp = results.firstOrNull()?.oldestTimestampTargetReachedMs ?: clock.epochMillis() val oldestTimestamp = results.firstOrNull()?.oldestTimestampTargetReachedMs ?: clock.epochMillis()
getPollStartEventsAfter(oldestTimestamp) getPollStartEventsAfter(oldestTimestamp)
} }
@ -132,7 +133,7 @@ internal class DefaultPollHistoryService @AssistedInject constructor(
} }
) )
return Transformations.map(eventsLiveData) { events -> return eventsLiveData.map { events ->
events.filter { it.root.getClearType() in EventType.POLL_START.values } events.filter { it.root.getClearType() in EventType.POLL_START.values }
.distinctBy { it.eventId } .distinctBy { it.eventId }
} }

View File

@ -17,7 +17,7 @@
package org.matrix.android.sdk.internal.session.room.read package org.matrix.android.sdk.internal.session.room.read
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations import androidx.lifecycle.map
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory import dagger.assisted.AssistedFactory
@ -112,7 +112,7 @@ internal class DefaultReadService @AssistedInject constructor(
{ ReadMarkerEntity.where(it, roomId) }, { ReadMarkerEntity.where(it, roomId) },
{ it.eventId } { it.eventId }
) )
return Transformations.map(liveRealmData) { return liveRealmData.map {
it.firstOrNull().toOptional() it.firstOrNull().toOptional()
} }
} }
@ -122,7 +122,7 @@ internal class DefaultReadService @AssistedInject constructor(
{ ReadReceiptEntity.where(it, roomId = roomId, userId = userId, threadId = threadId) }, { ReadReceiptEntity.where(it, roomId = roomId, userId = userId, threadId = threadId) },
{ it.eventId } { it.eventId }
) )
return Transformations.map(liveRealmData) { return liveRealmData.map {
it.firstOrNull().toOptional() it.firstOrNull().toOptional()
} }
} }
@ -143,7 +143,7 @@ internal class DefaultReadService @AssistedInject constructor(
{ ReadReceiptsSummaryEntity.where(it, eventId) }, { ReadReceiptsSummaryEntity.where(it, eventId) },
{ readReceiptsSummaryMapper.map(it) } { readReceiptsSummaryMapper.map(it) }
) )
return Transformations.map(liveRealmData) { return liveRealmData.map {
it.firstOrNull().orEmpty() it.firstOrNull().orEmpty()
} }
} }

View File

@ -16,7 +16,7 @@
package org.matrix.android.sdk.internal.session.room.relation package org.matrix.android.sdk.internal.session.room.relation
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations import androidx.lifecycle.map
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory import dagger.assisted.AssistedFactory
@ -163,7 +163,7 @@ internal class DefaultRelationService @AssistedInject constructor(
{ EventAnnotationsSummaryEntity.where(it, roomId, eventId) }, { EventAnnotationsSummaryEntity.where(it, roomId, eventId) },
{ it.asDomain() } { it.asDomain() }
) )
return Transformations.map(liveData) { results -> return liveData.map { results ->
results.firstOrNull().toOptional() results.firstOrNull().toOptional()
} }
} }

View File

@ -17,7 +17,7 @@
package org.matrix.android.sdk.internal.session.room.state package org.matrix.android.sdk.internal.session.room.state
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations import androidx.lifecycle.map
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import io.realm.Realm import io.realm.Realm
import io.realm.RealmQuery import io.realm.RealmQuery
@ -52,7 +52,7 @@ internal class StateEventDataSource @Inject constructor(
{ realm -> buildStateEventQuery(realm, roomId, setOf(eventType), stateKey) }, { realm -> buildStateEventQuery(realm, roomId, setOf(eventType), stateKey) },
{ it.root?.asDomain() } { it.root?.asDomain() }
) )
return Transformations.map(liveData) { results -> return liveData.map { results ->
results.firstOrNull().toOptional() results.firstOrNull().toOptional()
} }
} }
@ -72,7 +72,7 @@ internal class StateEventDataSource @Inject constructor(
{ realm -> buildStateEventQuery(realm, roomId, eventTypes, stateKey) }, { realm -> buildStateEventQuery(realm, roomId, eventTypes, stateKey) },
{ it.root?.asDomain() } { it.root?.asDomain() }
) )
return Transformations.map(liveData) { results -> return liveData.map { results ->
results.filterNotNull() results.filterNotNull()
} }
} }

View File

@ -19,7 +19,8 @@ package org.matrix.android.sdk.internal.session.room.summary
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Transformations import androidx.lifecycle.map
import androidx.lifecycle.switchMap
import androidx.paging.LivePagedListBuilder import androidx.paging.LivePagedListBuilder
import androidx.paging.PagedList import androidx.paging.PagedList
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
@ -86,7 +87,7 @@ internal class RoomSummaryDataSource @Inject constructor(
{ realm -> RoomSummaryEntity.where(realm, roomId).isNotEmpty(RoomSummaryEntityFields.DISPLAY_NAME) }, { realm -> RoomSummaryEntity.where(realm, roomId).isNotEmpty(RoomSummaryEntityFields.DISPLAY_NAME) },
{ roomSummaryMapper.map(it) } { roomSummaryMapper.map(it) }
) )
return Transformations.map(liveData) { results -> return liveData.map { results ->
results.firstOrNull().toOptional() results.firstOrNull().toOptional()
} }
} }
@ -115,7 +116,7 @@ internal class RoomSummaryDataSource @Inject constructor(
{ realm -> LocalRoomSummaryEntity.where(realm, roomId) }, { realm -> LocalRoomSummaryEntity.where(realm, roomId) },
{ localRoomSummaryMapper.map(it) } { localRoomSummaryMapper.map(it) }
) )
return Transformations.map(liveData) { results -> return liveData.map { results ->
results.firstOrNull().toOptional() results.firstOrNull().toOptional()
} }
} }
@ -167,7 +168,7 @@ internal class RoomSummaryDataSource @Inject constructor(
roomSummaryMapper.map(it) roomSummaryMapper.map(it)
} }
) )
return Transformations.map(liveData) { results -> return liveData.map { results ->
results.firstOrNull().toOptional() results.firstOrNull().toOptional()
} }
} }
@ -294,7 +295,7 @@ internal class RoomSummaryDataSource @Inject constructor(
val liveRooms = monarchy.findAllManagedWithChanges { val liveRooms = monarchy.findAllManagedWithChanges {
roomSummariesQuery(it, queryParams) roomSummariesQuery(it, queryParams)
} }
return Transformations.map(liveRooms) { return liveRooms.map {
it.realmResults.where().count().toInt() it.realmResults.where().count().toInt()
} }
} }
@ -417,7 +418,7 @@ internal class RoomSummaryDataSource @Inject constructor(
// and switch map to listen those? // and switch map to listen those?
val mediatorLiveData = HierarchyLiveDataHelper(spaceId, memberShips, this).liveData() val mediatorLiveData = HierarchyLiveDataHelper(spaceId, memberShips, this).liveData()
return Transformations.switchMap(mediatorLiveData) { allIds -> return mediatorLiveData.switchMap { allIds ->
monarchy.findAllMappedWithChanges( monarchy.findAllMappedWithChanges(
{ {
it.where<RoomSummaryEntity>() it.where<RoomSummaryEntity>()
@ -442,15 +443,16 @@ internal class RoomSummaryDataSource @Inject constructor(
} }
fun getFlattenOrphanRoomsLive(): LiveData<List<RoomSummary>> { fun getFlattenOrphanRoomsLive(): LiveData<List<RoomSummary>> {
return Transformations.map( return getRoomSummariesLive(
getRoomSummariesLive(roomSummaryQueryParams { roomSummaryQueryParams {
memberships = Membership.activeMemberships() memberships = Membership.activeMemberships()
excludeType = listOf(RoomType.SPACE) excludeType = listOf(RoomType.SPACE)
roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS roomCategoryFilter = RoomCategoryFilter.ONLY_ROOMS
}) }
) { )
it.filter { isOrphan(it) } .map {
} it.filter { isOrphan(it) }
}
} }
private fun isOrphan(roomSummary: RoomSummary): Boolean { private fun isOrphan(roomSummary: RoomSummary): Boolean {

View File

@ -17,7 +17,7 @@
package org.matrix.android.sdk.internal.session.user package org.matrix.android.sdk.internal.session.user
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations import androidx.lifecycle.map
import androidx.paging.DataSource import androidx.paging.DataSource
import androidx.paging.LivePagedListBuilder import androidx.paging.LivePagedListBuilder
import androidx.paging.PagedList import androidx.paging.PagedList
@ -73,7 +73,7 @@ internal class UserDataSource @Inject constructor(
{ UserEntity.where(it, userId) }, { UserEntity.where(it, userId) },
{ it.asDomain() } { it.asDomain() }
) )
return Transformations.map(liveData) { results -> return liveData.map { results ->
results.firstOrNull().toOptional() results.firstOrNull().toOptional()
} }
} }

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