Merge branch 'develop' into gradlew-update-9.6.1

This commit is contained in:
ganfra
2026-07-21 15:34:15 +02:00
committed by GitHub
41 changed files with 229 additions and 43 deletions
+1 -1
View File
@@ -51,7 +51,7 @@ jobs:
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 }}
persist-credentials: false persist-credentials: false
- name: Use JDK 21 - name: Use JDK 21
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0 uses: actions/setup-java@0f481fcb613427c0f801b606911222b5b6f3083a # v5.5.0
with: with:
distribution: 'temurin' # See 'Supported distributions' for available options distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21' java-version: '21'
+1 -1
View File
@@ -56,7 +56,7 @@ jobs:
- name: Clone submodules - name: Clone submodules
run: git submodule update --init --recursive run: git submodule update --init --recursive
- name: Use JDK 21 - name: Use JDK 21
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0 uses: actions/setup-java@0f481fcb613427c0f801b606911222b5b6f3083a # v5.5.0
with: with:
distribution: 'temurin' # See 'Supported distributions' for available options distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21' java-version: '21'
+1 -1
View File
@@ -19,7 +19,7 @@ jobs:
- name: ⏬ Checkout with LFS - name: ⏬ Checkout with LFS
uses: nschloe/action-cached-lfs-checkout@385a8ecc719e50b8c71af6ab01a624b486b7c3bc # v1.2.5 uses: nschloe/action-cached-lfs-checkout@385a8ecc719e50b8c71af6ab01a624b486b7c3bc # v1.2.5
- name: Use JDK 21 - name: Use JDK 21
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0 uses: actions/setup-java@0f481fcb613427c0f801b606911222b5b6f3083a # v5.5.0
with: with:
distribution: 'temurin' # See 'Supported distributions' for available options distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21' java-version: '21'
+1 -2
View File
@@ -16,9 +16,8 @@ jobs:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with: with:
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0 - uses: actions/setup-java@0f481fcb613427c0f801b606911222b5b6f3083a # v5.5.0
name: Use JDK 21 name: Use JDK 21
if: (github.event_name == 'pull_request' && github.event.pull_request.fork == null) || github.event_name == 'workflow_dispatch'
with: with:
distribution: 'temurin' # See 'Supported distributions' for available options distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21' java-version: '21'
+1 -1
View File
@@ -44,7 +44,7 @@ jobs:
# https://github.com/actions/checkout/issues/881 # https://github.com/actions/checkout/issues/881
ref: ${{ github.ref }} ref: ${{ github.ref }}
persist-credentials: false persist-credentials: false
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0 - uses: actions/setup-java@0f481fcb613427c0f801b606911222b5b6f3083a # v5.5.0
name: Use JDK 21 name: Use JDK 21
with: with:
distribution: 'temurin' # See 'Supported distributions' for available options distribution: 'temurin' # See 'Supported distributions' for available options
+1 -1
View File
@@ -36,7 +36,7 @@ jobs:
with: with:
persist-credentials: false persist-credentials: false
- name: Use JDK 21 - name: Use JDK 21
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0 uses: actions/setup-java@0f481fcb613427c0f801b606911222b5b6f3083a # v5.5.0
with: with:
distribution: 'temurin' # See 'Supported distributions' for available options distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21' java-version: '21'
+2 -2
View File
@@ -37,7 +37,7 @@ jobs:
uses: nschloe/action-cached-lfs-checkout@385a8ecc719e50b8c71af6ab01a624b486b7c3bc # v1.2.5 uses: nschloe/action-cached-lfs-checkout@385a8ecc719e50b8c71af6ab01a624b486b7c3bc # v1.2.5
- name: Use JDK 21 - name: Use JDK 21
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0 uses: actions/setup-java@0f481fcb613427c0f801b606911222b5b6f3083a # v5.5.0
with: with:
distribution: 'temurin' # See 'Supported distributions' for available options distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21' java-version: '21'
@@ -80,7 +80,7 @@ jobs:
with: with:
persist-credentials: false persist-credentials: false
- name: Use JDK 21 - name: Use JDK 21
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0 uses: actions/setup-java@0f481fcb613427c0f801b606911222b5b6f3083a # v5.5.0
with: with:
distribution: 'temurin' # See 'Supported distributions' for available options distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21' java-version: '21'
+6 -6
View File
@@ -69,7 +69,7 @@ jobs:
with: with:
persist-credentials: false persist-credentials: false
- name: Use JDK 21 - name: Use JDK 21
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0 uses: actions/setup-java@0f481fcb613427c0f801b606911222b5b6f3083a # v5.5.0
with: with:
distribution: 'temurin' # See 'Supported distributions' for available options distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21' java-version: '21'
@@ -108,7 +108,7 @@ jobs:
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
run: git submodule update --init --recursive run: git submodule update --init --recursive
- name: Use JDK 21 - name: Use JDK 21
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0 uses: actions/setup-java@0f481fcb613427c0f801b606911222b5b6f3083a # v5.5.0
with: with:
distribution: 'temurin' # See 'Supported distributions' for available options distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21' java-version: '21'
@@ -149,7 +149,7 @@ jobs:
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
run: git submodule update --init --recursive run: git submodule update --init --recursive
- name: Use JDK 21 - name: Use JDK 21
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0 uses: actions/setup-java@0f481fcb613427c0f801b606911222b5b6f3083a # v5.5.0
with: with:
distribution: 'temurin' # See 'Supported distributions' for available options distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21' java-version: '21'
@@ -183,7 +183,7 @@ jobs:
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
run: git submodule update --init --recursive run: git submodule update --init --recursive
- name: Use JDK 21 - name: Use JDK 21
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0 uses: actions/setup-java@0f481fcb613427c0f801b606911222b5b6f3083a # v5.5.0
with: with:
distribution: 'temurin' # See 'Supported distributions' for available options distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21' java-version: '21'
@@ -228,7 +228,7 @@ jobs:
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
run: git submodule update --init --recursive run: git submodule update --init --recursive
- name: Use JDK 21 - name: Use JDK 21
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0 uses: actions/setup-java@0f481fcb613427c0f801b606911222b5b6f3083a # v5.5.0
with: with:
distribution: 'temurin' # See 'Supported distributions' for available options distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21' java-version: '21'
@@ -269,7 +269,7 @@ jobs:
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
run: git submodule update --init --recursive run: git submodule update --init --recursive
- name: Use JDK 21 - name: Use JDK 21
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0 uses: actions/setup-java@0f481fcb613427c0f801b606911222b5b6f3083a # v5.5.0
with: with:
distribution: 'temurin' # See 'Supported distributions' for available options distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21' java-version: '21'
+1 -1
View File
@@ -54,7 +54,7 @@ jobs:
with: with:
persist-credentials: false persist-credentials: false
- name: ☕️ Use JDK 21 - name: ☕️ Use JDK 21
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0 uses: actions/setup-java@0f481fcb613427c0f801b606911222b5b6f3083a # v5.5.0
with: with:
distribution: 'temurin' # See 'Supported distributions' for available options distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21' java-version: '21'
+3 -3
View File
@@ -38,7 +38,7 @@ jobs:
with: with:
persist-credentials: false persist-credentials: false
- name: Use JDK 21 - name: Use JDK 21
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0 uses: actions/setup-java@0f481fcb613427c0f801b606911222b5b6f3083a # v5.5.0
with: with:
distribution: 'temurin' # See 'Supported distributions' for available options distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21' java-version: '21'
@@ -82,7 +82,7 @@ jobs:
- name: Clone submodules - name: Clone submodules
run: git submodule update --init --recursive run: git submodule update --init --recursive
- name: Use JDK 21 - name: Use JDK 21
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0 uses: actions/setup-java@0f481fcb613427c0f801b606911222b5b6f3083a # v5.5.0
with: with:
distribution: 'temurin' # See 'Supported distributions' for available options distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21' java-version: '21'
@@ -126,7 +126,7 @@ jobs:
with: with:
persist-credentials: false persist-credentials: false
- name: Use JDK 21 - name: Use JDK 21
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0 uses: actions/setup-java@0f481fcb613427c0f801b606911222b5b6f3083a # v5.5.0
with: with:
distribution: 'temurin' # See 'Supported distributions' for available options distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21' java-version: '21'
+1 -1
View File
@@ -45,7 +45,7 @@ jobs:
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 }}
persist-credentials: false persist-credentials: false
- name: Use JDK 21 - name: Use JDK 21
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0 uses: actions/setup-java@0f481fcb613427c0f801b606911222b5b6f3083a # v5.5.0
with: with:
distribution: 'temurin' # See 'Supported distributions' for available options distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21' java-version: '21'
+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
with: with:
persist-credentials: false persist-credentials: false
- name: Use JDK 21 - name: Use JDK 21
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0 uses: actions/setup-java@0f481fcb613427c0f801b606911222b5b6f3083a # v5.5.0
with: with:
distribution: 'temurin' # See 'Supported distributions' for available options distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21' java-version: '21'
+1 -1
View File
@@ -63,7 +63,7 @@ jobs:
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
run: git submodule update --init --recursive run: git submodule update --init --recursive
- name: ☕️ Use JDK 21 - name: ☕️ Use JDK 21
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0 uses: actions/setup-java@0f481fcb613427c0f801b606911222b5b6f3083a # v5.5.0
with: with:
distribution: 'temurin' # See 'Supported distributions' for available options distribution: 'temurin' # See 'Supported distributions' for available options
java-version: '21' java-version: '21'
+1
View File
@@ -17,4 +17,5 @@ dependencies {
implementation(projects.libraries.compound) implementation(projects.libraries.compound)
implementation(projects.libraries.architecture) implementation(projects.libraries.architecture)
implementation(projects.libraries.matrix.api) implementation(projects.libraries.matrix.api)
implementation(projects.libraries.wellknown.api)
} }
@@ -11,6 +11,7 @@ package io.element.android.features.enterprise.api
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import io.element.android.compound.colors.SemanticColorsLightDark import io.element.android.compound.colors.SemanticColorsLightDark
import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.wellknown.api.ElementWellKnown
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
interface EnterpriseService { interface EnterpriseService {
@@ -41,6 +42,11 @@ interface EnterpriseService {
*/ */
fun getNoisyNotificationChannelId(sessionId: SessionId): String? fun getNoisyNotificationChannelId(sessionId: SessionId): String?
/**
* Gets the overridden Element Well-Known data if it has been set, or null if not set.
*/
fun overriddenElementWellKnown(): ElementWellKnown?
companion object { companion object {
const val ANY_ACCOUNT_PROVIDER = "*" const val ANY_ACCOUNT_PROVIDER = "*"
} }
@@ -23,6 +23,7 @@ dependencies {
api(projects.features.enterprise.api) api(projects.features.enterprise.api)
implementation(projects.libraries.architecture) implementation(projects.libraries.architecture)
implementation(projects.libraries.matrix.api) implementation(projects.libraries.matrix.api)
implementation(projects.libraries.wellknown.api)
testCommonDependencies(libs) testCommonDependencies(libs)
testImplementation(projects.libraries.matrix.test) testImplementation(projects.libraries.matrix.test)
@@ -15,6 +15,7 @@ import io.element.android.compound.colors.SemanticColorsLightDark
import io.element.android.features.enterprise.api.BugReportUrl import io.element.android.features.enterprise.api.BugReportUrl
import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.features.enterprise.api.EnterpriseService
import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.wellknown.api.ElementWellKnown
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOf
@@ -45,4 +46,6 @@ class DefaultEnterpriseService : EnterpriseService {
} }
override fun getNoisyNotificationChannelId(sessionId: SessionId): String? = null override fun getNoisyNotificationChannelId(sessionId: SessionId): String? = null
override fun overriddenElementWellKnown(): ElementWellKnown? = null
} }
@@ -18,5 +18,6 @@ dependencies {
implementation(projects.libraries.architecture) implementation(projects.libraries.architecture)
implementation(projects.libraries.compound) implementation(projects.libraries.compound)
implementation(projects.libraries.matrix.api) implementation(projects.libraries.matrix.api)
implementation(projects.libraries.wellknown.api)
implementation(projects.tests.testutils) implementation(projects.tests.testutils)
} }
@@ -13,6 +13,7 @@ import io.element.android.compound.colors.SemanticColorsLightDark
import io.element.android.features.enterprise.api.BugReportUrl import io.element.android.features.enterprise.api.BugReportUrl
import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.features.enterprise.api.EnterpriseService
import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.wellknown.api.ElementWellKnown
import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.lambda.lambdaError
import io.element.android.tests.testutils.simulateLongTask import io.element.android.tests.testutils.simulateLongTask
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
@@ -31,6 +32,7 @@ class FakeEnterpriseService(
private val unifiedPushDefaultPushGatewayResult: () -> String? = { lambdaError() }, private val unifiedPushDefaultPushGatewayResult: () -> String? = { lambdaError() },
private val getNoisyNotificationChannelIdResult: (SessionId?) -> String? = { lambdaError() }, private val getNoisyNotificationChannelIdResult: (SessionId?) -> String? = { lambdaError() },
private val tweakMasUrlResult: (String, String) -> String = { _, _ -> lambdaError() }, private val tweakMasUrlResult: (String, String) -> String = { _, _ -> lambdaError() },
private val overrideWellKnownResult: () -> ElementWellKnown? = { lambdaError() },
) : EnterpriseService { ) : EnterpriseService {
private val brandColorState = MutableStateFlow(initialBrandColor) private val brandColorState = MutableStateFlow(initialBrandColor)
private val semanticColorsState = MutableStateFlow(initialSemanticColors) private val semanticColorsState = MutableStateFlow(initialSemanticColors)
@@ -79,4 +81,8 @@ class FakeEnterpriseService(
override fun getNoisyNotificationChannelId(sessionId: SessionId): String? { override fun getNoisyNotificationChannelId(sessionId: SessionId): String? {
return getNoisyNotificationChannelIdResult(sessionId) return getNoisyNotificationChannelIdResult(sessionId)
} }
override fun overriddenElementWellKnown(): ElementWellKnown? {
return overrideWellKnownResult()
}
} }
@@ -26,9 +26,11 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.rotate import androidx.compose.ui.draw.rotate
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.Role import androidx.compose.ui.semantics.Role
import androidx.compose.ui.semantics.clearAndSetSemantics import androidx.compose.ui.semantics.clearAndSetSemantics
import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.stateDescription
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import io.element.android.compound.theme.ElementTheme import io.element.android.compound.theme.ElementTheme
import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.compound.tokens.generated.CompoundIcons
@@ -37,6 +39,7 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.Surface import io.element.android.libraries.designsystem.theme.components.Surface
import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.ui.strings.CommonStrings
private val CORNER_RADIUS = 8.dp private val CORNER_RADIUS = 8.dp
@@ -52,6 +55,8 @@ fun GroupHeaderView(
val backgroundColor = Color.Transparent val backgroundColor = Color.Transparent
val shape = RoundedCornerShape(CORNER_RADIUS) val shape = RoundedCornerShape(CORNER_RADIUS)
val expandedStateDescription = stringResource(CommonStrings.a11y_state_expanded)
val collapsedStateDescription = stringResource(CommonStrings.a11y_state_collapsed)
Box( Box(
modifier = modifier modifier = modifier
.fillMaxWidth() .fillMaxWidth()
@@ -62,6 +67,7 @@ fun GroupHeaderView(
) )
.clearAndSetSemantics { .clearAndSetSemantics {
contentDescription = text contentDescription = text
stateDescription = if (isExpanded) expandedStateDescription else collapsedStateDescription
}, },
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
+1 -1
View File
@@ -6,7 +6,7 @@
* Please see LICENSE files in the repository root for full details. * Please see LICENSE files in the repository root for full details.
*/ */
plugins { plugins {
id("io.element.android-library") id("io.element.android-compose-library")
id("kotlin-parcelize") id("kotlin-parcelize")
} }
@@ -0,0 +1,19 @@
/*
* Copyright (c) 2026 Element Creations Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.features.preferences.api
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
/**
* Component for rendering extra developer options in the preferences screen.
*/
interface ExtraDeveloperOptionsRenderer {
@Composable
fun Render(modifier: Modifier)
}
@@ -18,6 +18,7 @@ import com.bumble.appyx.core.plugin.Plugin
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode import io.element.android.annotations.ContributesNode
import io.element.android.features.preferences.api.ExtraDeveloperOptionsRenderer
import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.callback
import io.element.android.libraries.designsystem.showkase.getBrowserIntent import io.element.android.libraries.designsystem.showkase.getBrowserIntent
import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.SessionScope
@@ -28,6 +29,7 @@ class DeveloperSettingsNode(
@Assisted buildContext: BuildContext, @Assisted buildContext: BuildContext,
@Assisted plugins: List<Plugin>, @Assisted plugins: List<Plugin>,
private val presenter: DeveloperSettingsPresenter, private val presenter: DeveloperSettingsPresenter,
private val extraDeveloperOptionsRenderer: ExtraDeveloperOptionsRenderer,
) : Node(buildContext, plugins = plugins) { ) : Node(buildContext, plugins = plugins) {
interface Callback : Plugin { interface Callback : Plugin {
fun navigateToPushHistory() fun navigateToPushHistory()
@@ -51,6 +53,7 @@ class DeveloperSettingsNode(
onOpenShowkase = ::openShowkase, onOpenShowkase = ::openShowkase,
onPushHistoryClick = callback::navigateToPushHistory, onPushHistoryClick = callback::navigateToPushHistory,
onBackClick = callback::onDone, onBackClick = callback::onDone,
extraOptions = { extraDeveloperOptionsRenderer.Render(Modifier) },
) )
} }
} }
@@ -43,6 +43,7 @@ fun DeveloperSettingsView(
onPushHistoryClick: () -> Unit, onPushHistoryClick: () -> Unit,
onBackClick: () -> Unit, onBackClick: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
extraOptions: @Composable () -> Unit = {},
) { ) {
if (state.showLoader) { if (state.showLoader) {
ProgressDialog() ProgressDialog()
@@ -97,6 +98,9 @@ fun DeveloperSettingsView(
) )
} }
} }
extraOptions()
val cache = state.cacheSize val cache = state.cacheSize
PreferenceCategory(title = "Cache") { PreferenceCategory(title = "Cache") {
ListItem( ListItem(
@@ -0,0 +1,22 @@
/*
* Copyright (c) 2026 Element Creations Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.features.preferences.impl.developer
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesBinding
import io.element.android.features.preferences.api.ExtraDeveloperOptionsRenderer
@ContributesBinding(AppScope::class)
class NoopExtraDeveloperOptionsRenderer : ExtraDeveloperOptionsRenderer {
@Composable
override fun Render(modifier: Modifier) {
// No-op
}
}
@@ -18,6 +18,7 @@ import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.Assisted import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode import io.element.android.annotations.ContributesNode
import io.element.android.features.preferences.api.ExtraDeveloperOptionsRenderer
import io.element.android.features.preferences.api.PreferencesEntryPoint import io.element.android.features.preferences.api.PreferencesEntryPoint
import io.element.android.libraries.architecture.callback import io.element.android.libraries.architecture.callback
import io.element.android.libraries.designsystem.showkase.getBrowserIntent import io.element.android.libraries.designsystem.showkase.getBrowserIntent
@@ -28,6 +29,7 @@ class AppDeveloperSettingsNode(
@Assisted buildContext: BuildContext, @Assisted buildContext: BuildContext,
@Assisted plugins: List<Plugin>, @Assisted plugins: List<Plugin>,
private val presenter: AppDeveloperSettingsPresenter, private val presenter: AppDeveloperSettingsPresenter,
private val extraDeveloperOptionsRenderer: ExtraDeveloperOptionsRenderer,
) : Node(buildContext, plugins = plugins) { ) : Node(buildContext, plugins = plugins) {
private val callback: PreferencesEntryPoint.DeveloperSettingsCallback = callback() private val callback: PreferencesEntryPoint.DeveloperSettingsCallback = callback()
@@ -45,6 +47,7 @@ class AppDeveloperSettingsNode(
modifier = modifier, modifier = modifier,
onOpenShowkase = ::openShowkase, onOpenShowkase = ::openShowkase,
onBackClick = callback::onDone, onBackClick = callback::onDone,
extraOptions = { extraDeveloperOptionsRenderer.Render(Modifier) },
) )
} }
} }
@@ -24,6 +24,7 @@ fun AppDeveloperSettingsPage(
onOpenShowkase: () -> Unit, onOpenShowkase: () -> Unit,
onBackClick: () -> Unit, onBackClick: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
extraOptions: @Composable () -> Unit = {},
) { ) {
BackHandler( BackHandler(
onBack = onBackClick, onBack = onBackClick,
@@ -40,6 +41,7 @@ fun AppDeveloperSettingsPage(
onOpenShowkase = onOpenShowkase, onOpenShowkase = onOpenShowkase,
modifier = Modifier.padding(top = 8.dp) modifier = Modifier.padding(top = 8.dp)
) )
extraOptions()
} }
} }
+6 -6
View File
@@ -5,7 +5,7 @@
# Project # Project
android_gradle_plugin = "9.2.1" android_gradle_plugin = "9.2.1"
# When updating this, please also update the version in the file ./idea/kotlinc.xml # When updating this, please also update the version in the file ./idea/kotlinc.xml
kotlin = "2.4.0" kotlin = "2.4.10"
kotlinpoet = "2.3.0" kotlinpoet = "2.3.0"
ksp = "2.3.10" ksp = "2.3.10"
firebaseAppDistribution = "5.3.0" firebaseAppDistribution = "5.3.0"
@@ -32,7 +32,7 @@ accompanist = "0.37.3"
# Test # Test
test_core = "1.7.0" test_core = "1.7.0"
roborazzi = "1.67.0" roborazzi = "1.68.0"
# Jetbrain # Jetbrain
datetime = "0.8.0" datetime = "0.8.0"
@@ -51,10 +51,10 @@ telephoto = "0.19.0"
haze = "1.7.2" haze = "1.7.2"
# Dependency analysis # Dependency analysis
dependencyAnalysis = "3.16.0" dependencyAnalysis = "3.16.1"
# DI # DI
metro = "1.3.0" metro = "1.3.2"
# Auto service # Auto service
autoservice = "1.1.1" autoservice = "1.1.1"
@@ -84,7 +84,7 @@ kotlinpoet-ksp = { module = "com.squareup:kotlinpoet-ksp", version.ref = "kotlin
kover_gradle_plugin = { module = "org.jetbrains.kotlinx:kover-gradle-plugin", version.ref = "kover" } kover_gradle_plugin = { module = "org.jetbrains.kotlinx:kover-gradle-plugin", version.ref = "kover" }
ksp_gradle_plugin = { module = "com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin", version.ref = "ksp" } ksp_gradle_plugin = { module = "com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin", version.ref = "ksp" }
# https://firebase.google.com/docs/android/setup#available-libraries # https://firebase.google.com/docs/android/setup#available-libraries
google_firebase_bom = "com.google.firebase:firebase-bom:34.15.0" google_firebase_bom = "com.google.firebase:firebase-bom:34.16.0"
firebase_appdistribution_gradle = { module = "com.google.firebase:firebase-appdistribution-gradle", version.ref = "firebaseAppDistribution" } firebase_appdistribution_gradle = { module = "com.google.firebase:firebase-appdistribution-gradle", version.ref = "firebaseAppDistribution" }
autonomousapps_dependencyanalysis_plugin = { module = "com.autonomousapps:dependency-analysis-gradle-plugin", version.ref = "dependencyAnalysis" } autonomousapps_dependencyanalysis_plugin = { module = "com.autonomousapps:dependency-analysis-gradle-plugin", version.ref = "dependencyAnalysis" }
ksp_plugin = { module = "com.google.devtools.ksp:symbol-processing-api", version.ref = "ksp" } ksp_plugin = { module = "com.google.devtools.ksp:symbol-processing-api", version.ref = "ksp" }
@@ -242,7 +242,7 @@ sigpwned_emoji4j = "com.sigpwned:emoji4j-core:16.0.0"
metro_runtime = { module = "dev.zacsweers.metro:runtime", version.ref = "metro" } metro_runtime = { module = "dev.zacsweers.metro:runtime", version.ref = "metro" }
# Element Call # Element Call
element_call_embedded = "io.element.android:element-call-embedded:0.20.3" element_call_embedded = "io.element.android:element-call-embedded:0.22.0"
# Auto services # Auto services
google_autoservice = { module = "com.google.auto.service:auto-service", version.ref = "autoservice" } google_autoservice = { module = "com.google.auto.service:auto-service", version.ref = "autoservice" }
@@ -8,6 +8,8 @@
<resources> <resources>
<string name="action_mark_as_read">"Mark as read"</string> <string name="action_mark_as_read">"Mark as read"</string>
<string name="a11y_jump_to_unread_messages">"Jump to first unread message"</string> <string name="a11y_jump_to_unread_messages">"Jump to first unread message"</string>
<string name="a11y_state_expanded">"Expanded"</string>
<string name="a11y_state_collapsed">"Collapsed"</string>
<string name="screen_local_network_opt_in_title">Allow access to local network</string> <string name="screen_local_network_opt_in_title">Allow access to local network</string>
<string name="screen_local_network_opt_in_subtitle">Your homeserver is on your local network. To connect, Element needs permission to reach other devices on this network.</string> <string name="screen_local_network_opt_in_subtitle">Your homeserver is on your local network. To connect, Element needs permission to reach other devices on this network.</string>
<string name="a11y_active">Active</string> <string name="a11y_active">Active</string>
@@ -0,0 +1,12 @@
/*
* Copyright (c) 2026 Element Creations Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.wellknown.api
fun interface ElementWellKnownParser {
fun parse(json: String): Result<ElementWellKnown>
}
@@ -34,6 +34,7 @@ dependencies {
implementation(projects.libraries.matrix.api) implementation(projects.libraries.matrix.api)
implementation(projects.libraries.network) implementation(projects.libraries.network)
implementation(projects.libraries.cachestore.api) implementation(projects.libraries.cachestore.api)
implementation(projects.features.enterprise.api)
implementation(projects.services.toolbox.api) implementation(projects.services.toolbox.api)
testCommonDependencies(libs) testCommonDependencies(libs)
@@ -41,5 +42,6 @@ dependencies {
testImplementation(projects.libraries.cachestore.test) testImplementation(projects.libraries.cachestore.test)
testImplementation(projects.libraries.matrix.test) testImplementation(projects.libraries.matrix.test)
testImplementation(projects.libraries.wellknown.test) testImplementation(projects.libraries.wellknown.test)
testImplementation(projects.features.enterprise.test)
testImplementation(projects.services.toolbox.test) testImplementation(projects.services.toolbox.test)
} }
@@ -0,0 +1,27 @@
/*
* Copyright (c) 2026 Element Creations Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.wellknown.impl
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesBinding
import io.element.android.libraries.androidutils.json.JsonProvider
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.wellknown.api.ElementWellKnown
import io.element.android.libraries.wellknown.api.ElementWellKnownParser
@ContributesBinding(AppScope::class)
class DefaultElementWellKnownParser(
private val jsonProvider: JsonProvider,
) : ElementWellKnownParser {
override fun parse(json: String): Result<ElementWellKnown> {
return runCatchingExceptions {
val decoder = jsonProvider()
decoder.decodeFromString<InternalElementWellKnown>(json).map()
}
}
}
@@ -9,13 +9,14 @@
package io.element.android.libraries.wellknown.impl package io.element.android.libraries.wellknown.impl
import dev.zacsweers.metro.ContributesBinding import dev.zacsweers.metro.ContributesBinding
import io.element.android.libraries.androidutils.json.JsonProvider import io.element.android.features.enterprise.api.EnterpriseService
import io.element.android.libraries.core.extensions.mapCatchingExceptions import io.element.android.libraries.core.extensions.mapCatchingExceptions
import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.di.annotations.SessionCoroutineScope import io.element.android.libraries.di.annotations.SessionCoroutineScope
import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.exception.ClientException import io.element.android.libraries.matrix.api.exception.ClientException
import io.element.android.libraries.wellknown.api.ElementWellKnown import io.element.android.libraries.wellknown.api.ElementWellKnown
import io.element.android.libraries.wellknown.api.ElementWellKnownParser
import io.element.android.libraries.wellknown.api.ElementWellknownStore import io.element.android.libraries.wellknown.api.ElementWellknownStore
import io.element.android.libraries.wellknown.api.SessionWellknownRetriever import io.element.android.libraries.wellknown.api.SessionWellknownRetriever
import io.element.android.libraries.wellknown.api.WellknownRetrieverResult import io.element.android.libraries.wellknown.api.WellknownRetrieverResult
@@ -26,14 +27,20 @@ import timber.log.Timber
@ContributesBinding(SessionScope::class) @ContributesBinding(SessionScope::class)
class DefaultSessionWellknownRetriever( class DefaultSessionWellknownRetriever(
private val matrixClient: MatrixClient, private val matrixClient: MatrixClient,
private val json: JsonProvider,
@SessionCoroutineScope @SessionCoroutineScope
private val sessionCoroutineScope: CoroutineScope, private val sessionCoroutineScope: CoroutineScope,
private val elementWellknownStore: ElementWellknownStore, private val elementWellknownStore: ElementWellknownStore,
private val elementWellKnownParser: ElementWellKnownParser,
private val enterpriseService: EnterpriseService,
) : SessionWellknownRetriever { ) : SessionWellknownRetriever {
private val domain by lazy { matrixClient.userIdServerName() } private val domain by lazy { matrixClient.userIdServerName() }
override suspend fun getElementWellKnown(): WellknownRetrieverResult<ElementWellKnown> { override suspend fun getElementWellKnown(): WellknownRetrieverResult<ElementWellKnown> {
val overriddenElementWellKnown = enterpriseService.overriddenElementWellKnown()
if (overriddenElementWellKnown != null) {
return WellknownRetrieverResult.Success(overriddenElementWellKnown)
}
val cacheData = elementWellknownStore.get(domain) val cacheData = elementWellknownStore.get(domain)
return when (cacheData) { return when (cacheData) {
is WellknownRetrieverResult.Success -> { is WellknownRetrieverResult.Success -> {
@@ -69,7 +76,7 @@ class DefaultSessionWellknownRetriever(
.getUrl(url) .getUrl(url)
.mapCatchingExceptions { .mapCatchingExceptions {
val data = String(it) val data = String(it)
val parsed = json().decodeFromString<InternalElementWellKnown>(data).map() val parsed = elementWellKnownParser.parse(data).getOrThrow()
// Also store in cache, if valid // Also store in cache, if valid
elementWellknownStore.update(domain, data) elementWellknownStore.update(domain, data)
.onFailure { exception -> .onFailure { exception ->
@@ -9,11 +9,11 @@ package io.element.android.libraries.wellknown.impl
import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesBinding import dev.zacsweers.metro.ContributesBinding
import io.element.android.libraries.androidutils.json.JsonProvider
import io.element.android.libraries.cachestore.api.CacheData import io.element.android.libraries.cachestore.api.CacheData
import io.element.android.libraries.cachestore.api.CacheStore import io.element.android.libraries.cachestore.api.CacheStore
import io.element.android.libraries.core.extensions.runCatchingExceptions import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.wellknown.api.ElementWellKnown import io.element.android.libraries.wellknown.api.ElementWellKnown
import io.element.android.libraries.wellknown.api.ElementWellKnownParser
import io.element.android.libraries.wellknown.api.ElementWellknownStore import io.element.android.libraries.wellknown.api.ElementWellknownStore
import io.element.android.libraries.wellknown.api.WellknownRetrieverResult import io.element.android.libraries.wellknown.api.WellknownRetrieverResult
import io.element.android.services.toolbox.api.systemclock.SystemClock import io.element.android.services.toolbox.api.systemclock.SystemClock
@@ -21,14 +21,14 @@ import io.element.android.services.toolbox.api.systemclock.SystemClock
@ContributesBinding(AppScope::class) @ContributesBinding(AppScope::class)
class DefaultWellKnownStore( class DefaultWellKnownStore(
private val cacheStore: CacheStore, private val cacheStore: CacheStore,
private val json: JsonProvider, private val elementWellKnownParser: ElementWellKnownParser,
private val systemClock: SystemClock, private val systemClock: SystemClock,
) : ElementWellknownStore { ) : ElementWellknownStore {
override suspend fun get(domain: String): WellknownRetrieverResult<ElementWellKnown> { override suspend fun get(domain: String): WellknownRetrieverResult<ElementWellKnown> {
return runCatchingExceptions { return runCatchingExceptions {
val cachedData = cacheStore.getData(key(domain)) val cachedData = cacheStore.getData(key(domain))
if (cachedData != null) { if (cachedData != null) {
val data = json().decodeFromString<InternalElementWellKnown>(cachedData.value).map() val data = elementWellKnownParser.parse(cachedData.value).getOrThrow()
if (systemClock.epochMillis() > cachedData.updatedAt + CACHE_VALIDITY_MILLIS) { if (systemClock.epochMillis() > cachedData.updatedAt + CACHE_VALIDITY_MILLIS) {
WellknownRetrieverResult.Outdated(data) WellknownRetrieverResult.Outdated(data)
} else { } else {
@@ -10,6 +10,7 @@ package io.element.android.libraries.wellknown.impl
import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesBinding import dev.zacsweers.metro.ContributesBinding
import io.element.android.features.enterprise.api.EnterpriseService
import io.element.android.libraries.androidutils.json.JsonProvider import io.element.android.libraries.androidutils.json.JsonProvider
import io.element.android.libraries.core.extensions.runCatchingExceptions import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.core.uri.ensureProtocol import io.element.android.libraries.core.uri.ensureProtocol
@@ -28,8 +29,14 @@ class DefaultWellknownRetriever(
private val retrofitFactory: RetrofitFactory, private val retrofitFactory: RetrofitFactory,
private val elementWellknownStore: ElementWellknownStore, private val elementWellknownStore: ElementWellknownStore,
private val jsonProvider: JsonProvider, private val jsonProvider: JsonProvider,
private val enterpriseService: EnterpriseService,
) : WellknownRetriever { ) : WellknownRetriever {
override suspend fun getElementWellKnown(baseUrl: String): WellknownRetrieverResult<ElementWellKnown> { override suspend fun getElementWellKnown(baseUrl: String): WellknownRetrieverResult<ElementWellKnown> {
val overriddenElementWellKnown = enterpriseService.overriddenElementWellKnown()
if (overriddenElementWellKnown != null) {
return WellknownRetrieverResult.Success(overriddenElementWellKnown)
}
val domain = URL(baseUrl).host val domain = URL(baseUrl).host
return buildWellknownApi(baseUrl) return buildWellknownApi(baseUrl)
.map { wellknownApi -> .map { wellknownApi ->
@@ -11,15 +11,16 @@
package io.element.android.libraries.wellknown.impl package io.element.android.libraries.wellknown.impl
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import io.element.android.features.enterprise.test.FakeEnterpriseService
import io.element.android.features.wellknown.test.FakeElementWellknownStore import io.element.android.features.wellknown.test.FakeElementWellknownStore
import io.element.android.features.wellknown.test.anElementWellKnown import io.element.android.features.wellknown.test.anElementWellKnown
import io.element.android.libraries.androidutils.json.DefaultJsonProvider import io.element.android.libraries.androidutils.json.DefaultJsonProvider
import io.element.android.libraries.androidutils.json.JsonProvider
import io.element.android.libraries.matrix.api.exception.ClientException import io.element.android.libraries.matrix.api.exception.ClientException
import io.element.android.libraries.matrix.test.AN_EXCEPTION import io.element.android.libraries.matrix.test.AN_EXCEPTION
import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.libraries.wellknown.api.CustomRecoveryPassphrase import io.element.android.libraries.wellknown.api.CustomRecoveryPassphrase
import io.element.android.libraries.wellknown.api.ElementWellKnown import io.element.android.libraries.wellknown.api.ElementWellKnown
import io.element.android.libraries.wellknown.api.ElementWellKnownParser
import io.element.android.libraries.wellknown.api.WellknownRetrieverResult import io.element.android.libraries.wellknown.api.WellknownRetrieverResult
import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.lambda.lambdaError
import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.lambdaRecorder
@@ -276,7 +277,7 @@ class DefaultSessionWellknownRetrieverTest {
Result.success("{}".toByteArray()) Result.success("{}".toByteArray())
}, },
cacheStore = cacheStore, cacheStore = cacheStore,
jsonProvider = JsonProvider { error("Failed to parse JSON") } elementWellKnownParser = { Result.failure(IllegalStateException("Failed to parse JSON")) }
) )
assertThat(sut.getElementWellKnown()).isInstanceOf(WellknownRetrieverResult.Error::class.java) assertThat(sut.getElementWellKnown()).isInstanceOf(WellknownRetrieverResult.Error::class.java)
// Ensure that the cache is deleted after the failure to parse it // Ensure that the cache is deleted after the failure to parse it
@@ -320,20 +321,43 @@ class DefaultSessionWellknownRetrieverTest {
) )
} }
@Test
fun `get element wellknown was overridden`() = runTest {
val getLambda = lambdaRecorder<String, Result<ByteArray>> { Result.failure(IllegalStateException("BOOM")) }
val wellKnown = anElementWellKnown()
val sut = createDefaultSessionWellknownRetriever(
getUrlLambda = getLambda,
enterpriseService = FakeEnterpriseService(
overrideWellKnownResult = { wellKnown }
)
)
// The overridden value is returned
assertThat(sut.getElementWellKnown()).isEqualTo(
WellknownRetrieverResult.Success(wellKnown)
)
// And the endpoint is never hit
getLambda.assertions().isNeverCalled()
}
private fun parsedWellKnownContent() = DefaultJsonProvider().invoke().decodeFromString<InternalElementWellKnown>(WELLKNOWN_CONTENT).map() private fun parsedWellKnownContent() = DefaultJsonProvider().invoke().decodeFromString<InternalElementWellKnown>(WELLKNOWN_CONTENT).map()
private fun TestScope.createDefaultSessionWellknownRetriever( private fun TestScope.createDefaultSessionWellknownRetriever(
cacheStore: FakeElementWellknownStore = FakeElementWellknownStore(), cacheStore: FakeElementWellknownStore = FakeElementWellknownStore(),
getUrlLambda: (String) -> Result<ByteArray>, getUrlLambda: (String) -> Result<ByteArray>,
jsonProvider: JsonProvider = DefaultJsonProvider(), elementWellKnownParser: ElementWellKnownParser = DefaultElementWellKnownParser(DefaultJsonProvider()),
enterpriseService: FakeEnterpriseService = FakeEnterpriseService(overrideWellKnownResult = { null }),
) = DefaultSessionWellknownRetriever( ) = DefaultSessionWellknownRetriever(
matrixClient = FakeMatrixClient( matrixClient = FakeMatrixClient(
userIdServerNameLambda = { "user.domain.org" }, userIdServerNameLambda = { "user.domain.org" },
getUrlLambda = getUrlLambda, getUrlLambda = getUrlLambda,
), ),
json = jsonProvider,
sessionCoroutineScope = backgroundScope, sessionCoroutineScope = backgroundScope,
elementWellknownStore = cacheStore, elementWellknownStore = cacheStore,
elementWellKnownParser = elementWellKnownParser,
enterpriseService = enterpriseService,
) )
companion object { companion object {
@@ -13,6 +13,7 @@ import io.element.android.libraries.androidutils.json.DefaultJsonProvider
import io.element.android.libraries.cachestore.api.CacheData import io.element.android.libraries.cachestore.api.CacheData
import io.element.android.libraries.sessionstorage.test.InMemoryCacheStore import io.element.android.libraries.sessionstorage.test.InMemoryCacheStore
import io.element.android.libraries.wellknown.api.CustomRecoveryPassphrase import io.element.android.libraries.wellknown.api.CustomRecoveryPassphrase
import io.element.android.libraries.wellknown.api.ElementWellKnownParser
import io.element.android.libraries.wellknown.api.WellknownRetrieverResult import io.element.android.libraries.wellknown.api.WellknownRetrieverResult
import io.element.android.services.toolbox.test.systemclock.A_FAKE_TIMESTAMP import io.element.android.services.toolbox.test.systemclock.A_FAKE_TIMESTAMP
import io.element.android.services.toolbox.test.systemclock.FakeSystemClock import io.element.android.services.toolbox.test.systemclock.FakeSystemClock
@@ -150,9 +151,10 @@ class DefaultWellKnownStoreTest {
private fun createDefaultWellKnownStore( private fun createDefaultWellKnownStore(
cacheStore: InMemoryCacheStore = InMemoryCacheStore(), cacheStore: InMemoryCacheStore = InMemoryCacheStore(),
systemClock: FakeSystemClock = FakeSystemClock(), systemClock: FakeSystemClock = FakeSystemClock(),
elementWellKnownParser: ElementWellKnownParser = DefaultElementWellKnownParser(jsonProvider),
) = DefaultWellKnownStore( ) = DefaultWellKnownStore(
cacheStore = cacheStore, cacheStore = cacheStore,
json = jsonProvider,
systemClock = systemClock, systemClock = systemClock,
elementWellKnownParser = elementWellKnownParser,
) )
} }
@@ -11,6 +11,7 @@
package io.element.android.libraries.wellknown.impl package io.element.android.libraries.wellknown.impl
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import io.element.android.features.enterprise.test.FakeEnterpriseService
import io.element.android.features.wellknown.test.FakeElementWellknownStore import io.element.android.features.wellknown.test.FakeElementWellknownStore
import io.element.android.features.wellknown.test.anElementWellKnown import io.element.android.features.wellknown.test.anElementWellKnown
import io.element.android.libraries.androidutils.json.DefaultJsonProvider import io.element.android.libraries.androidutils.json.DefaultJsonProvider
@@ -19,6 +20,7 @@ import io.element.android.libraries.network.RetrofitFactory
import io.element.android.libraries.wellknown.api.CustomRecoveryPassphrase import io.element.android.libraries.wellknown.api.CustomRecoveryPassphrase
import io.element.android.libraries.wellknown.api.ElementWellKnown import io.element.android.libraries.wellknown.api.ElementWellKnown
import io.element.android.libraries.wellknown.api.WellknownRetrieverResult import io.element.android.libraries.wellknown.api.WellknownRetrieverResult
import io.element.android.tests.testutils.lambda.lambdaRecorder
import io.mockk.every import io.mockk.every
import io.mockk.mockk import io.mockk.mockk
import io.mockk.verify import io.mockk.verify
@@ -236,6 +238,27 @@ class DefaultWellknownRetrieverTest {
assertThat(cacheStore.get(WELLKNOWN_URL)).isEqualTo(WellknownRetrieverResult.NotFound) assertThat(cacheStore.get(WELLKNOWN_URL)).isEqualTo(WellknownRetrieverResult.NotFound)
} }
@Test
fun `get element wellknown was overridden`() = runTest {
val getLambda = lambdaRecorder<Request, Call> { mockCall() }
val wellKnown = anElementWellKnown()
val sut = createDefaultWellknownRetriever(
callFactory = getLambda,
enterpriseService = FakeEnterpriseService(
overrideWellKnownResult = { wellKnown }
)
)
// The overridden value is returned
assertThat(sut.getElementWellKnown(WELLKNOWN_URL)).isEqualTo(
WellknownRetrieverResult.Success(wellKnown)
)
// And the endpoint is never hit
getLambda.assertions().isNeverCalled()
}
private fun defaultResponse( private fun defaultResponse(
body: String = WELLKNOWN_CONTENT, body: String = WELLKNOWN_CONTENT,
status: Int = 200, status: Int = 200,
@@ -251,6 +274,7 @@ class DefaultWellknownRetrieverTest {
callFactory: Call.Factory = Call.Factory { _: Request -> mockCall(defaultResponse()) }, callFactory: Call.Factory = Call.Factory { _: Request -> mockCall(defaultResponse()) },
cacheStore: FakeElementWellknownStore = FakeElementWellknownStore(), cacheStore: FakeElementWellknownStore = FakeElementWellknownStore(),
jsonProvider: JsonProvider = DefaultJsonProvider(), jsonProvider: JsonProvider = DefaultJsonProvider(),
enterpriseService: FakeEnterpriseService = FakeEnterpriseService(overrideWellKnownResult = { null }),
) = DefaultWellknownRetriever( ) = DefaultWellknownRetriever(
retrofitFactory = RetrofitFactory( retrofitFactory = RetrofitFactory(
callFactory = { callFactory }, callFactory = { callFactory },
@@ -258,6 +282,7 @@ class DefaultWellknownRetrieverTest {
), ),
jsonProvider = jsonProvider, jsonProvider = jsonProvider,
elementWellknownStore = cacheStore, elementWellknownStore = cacheStore,
enterpriseService = enterpriseService,
) )
companion object { companion object {
@@ -88,6 +88,8 @@ class KonsistPreviewTest {
"BackgroundVerticalGradientPreview", "BackgroundVerticalGradientPreview",
"ColorAliasesPreview", "ColorAliasesPreview",
"EmojiItemWithPopupPreview", "EmojiItemWithPopupPreview",
"EnterpriseExtraDeveloperOptionsRendererBottomSheetPreview",
"EnterpriseExtraDeveloperOptionsRendererListItemPreview",
"FocusedEventPreview", "FocusedEventPreview",
"GradientFloatingActionButtonCircleShapePreview", "GradientFloatingActionButtonCircleShapePreview",
"HeaderFooterPageScrollablePreview", "HeaderFooterPageScrollablePreview",