Element config (#4471)

* Add handy extension "VariantDimension.buildConfigFieldStr"

* Update configuration for MapTiler.

* Update configuration for Sentry.

* Build AnalyticsConfig depending on analytics configuration.

* Configure analytics policy url.

* Add handy extension "VariantDimension.buildConfigFieldBoolean"

* Configure legal urls.

* Add a way to disable rageshake / reporting bugs.

* Update screenshots

* Quality

* Fix test

* Use `ifBlank` extension

* Add missing configuration for PostHog

* Update configuration for Rageshake.

* Add build log.

* Disable crash detection if rageshake feature is not available.
Disabled twice.

* Hide link to analytics policy if the link is missing.

* Fix test when run in enterprise context.

* Use RageshakeFeatureAvailability where appropriate.

* Rename file.

* Move some classes to their correct module.

* Update screenshots

---------

Co-authored-by: ElementBot <android@element.io>
This commit is contained in:
Benoit Marty 2025-03-27 11:25:04 +01:00 committed by GitHub
parent c6b99c853c
commit 3c1deff79c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
95 changed files with 613 additions and 273 deletions

View file

@ -0,0 +1,22 @@
/*
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.features.rageshake.impl
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.appconfig.RageshakeConfig
import io.element.android.appconfig.isEnabled
import io.element.android.features.rageshake.api.RageshakeFeatureAvailability
import io.element.android.libraries.di.AppScope
import javax.inject.Inject
@ContributesBinding(AppScope::class)
class DefaultRageshakeFeatureAvailability @Inject constructor() : RageshakeFeatureAvailability {
override fun isAvailable(): Boolean {
return RageshakeConfig.isEnabled
}
}

View file

@ -16,10 +16,10 @@ import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import io.element.android.features.rageshake.api.crash.CrashDataStore
import io.element.android.features.rageshake.api.reporter.BugReporter
import io.element.android.features.rageshake.api.reporter.BugReporterListener
import io.element.android.features.rageshake.api.screenshot.ScreenshotHolder
import io.element.android.features.rageshake.impl.crash.CrashDataStore
import io.element.android.features.rageshake.impl.screenshot.ScreenshotHolder
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.architecture.Presenter
import kotlinx.coroutines.CoroutineScope

View file

@ -0,0 +1,20 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.features.rageshake.impl.crash
import kotlinx.coroutines.flow.Flow
interface CrashDataStore {
fun setCrashData(crashData: String)
suspend fun resetAppHasCrashed()
fun appHasCrashed(): Flow<Boolean>
fun crashInfo(): Flow<String>
suspend fun reset()
}

View file

@ -9,15 +9,17 @@ package io.element.android.features.rageshake.impl.crash
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.features.rageshake.api.crash.CrashDataStore
import io.element.android.features.rageshake.api.RageshakeFeatureAvailability
import io.element.android.features.rageshake.api.crash.CrashDetectionEvents
import io.element.android.features.rageshake.api.crash.CrashDetectionPresenter
import io.element.android.features.rageshake.api.crash.CrashDetectionState
import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.di.AppScope
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.launch
import javax.inject.Inject
@ -25,12 +27,18 @@ import javax.inject.Inject
class DefaultCrashDetectionPresenter @Inject constructor(
private val buildMeta: BuildMeta,
private val crashDataStore: CrashDataStore,
) :
CrashDetectionPresenter {
private val rageshakeFeatureAvailability: RageshakeFeatureAvailability,
) : CrashDetectionPresenter {
@Composable
override fun present(): CrashDetectionState {
val localCoroutineScope = rememberCoroutineScope()
val crashDetected = crashDataStore.appHasCrashed().collectAsState(initial = false)
val crashDetected = remember {
if (rageshakeFeatureAvailability.isAvailable()) {
crashDataStore.appHasCrashed()
} else {
flowOf(false)
}
}.collectAsState(false)
fun handleEvents(event: CrashDetectionEvents) {
when (event) {

View file

@ -15,7 +15,6 @@ import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.features.rageshake.api.crash.CrashDataStore
import io.element.android.libraries.core.bool.orFalse
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.ApplicationContext

View file

@ -20,9 +20,9 @@ import io.element.android.features.rageshake.api.detection.RageshakeDetectionPre
import io.element.android.features.rageshake.api.detection.RageshakeDetectionState
import io.element.android.features.rageshake.api.preferences.RageshakePreferencesEvents
import io.element.android.features.rageshake.api.preferences.RageshakePreferencesPresenter
import io.element.android.features.rageshake.api.rageshake.RageShake
import io.element.android.features.rageshake.api.screenshot.ImageResult
import io.element.android.features.rageshake.api.screenshot.ScreenshotHolder
import io.element.android.features.rageshake.impl.rageshake.RageShake
import io.element.android.features.rageshake.impl.screenshot.ScreenshotHolder
import io.element.android.libraries.di.AppScope
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
@ -75,7 +75,8 @@ class DefaultRageshakeDetectionPresenter @Inject constructor(
LaunchedEffect(preferencesState.sensitivity) {
rageShake.setSensitivity(preferencesState.sensitivity)
}
val shouldStart = preferencesState.isEnabled &&
val shouldStart = preferencesState.isFeatureEnabled &&
preferencesState.isEnabled &&
preferencesState.isSupported &&
isStarted.value &&
!takeScreenshot.value &&

View file

@ -11,14 +11,16 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.features.rageshake.api.RageshakeFeatureAvailability
import io.element.android.features.rageshake.api.preferences.RageshakePreferencesEvents
import io.element.android.features.rageshake.api.preferences.RageshakePreferencesPresenter
import io.element.android.features.rageshake.api.preferences.RageshakePreferencesState
import io.element.android.features.rageshake.api.rageshake.RageShake
import io.element.android.features.rageshake.api.rageshake.RageshakeDataStore
import io.element.android.features.rageshake.impl.rageshake.RageShake
import io.element.android.features.rageshake.impl.rageshake.RageshakeDataStore
import io.element.android.libraries.di.AppScope
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
@ -28,6 +30,7 @@ import javax.inject.Inject
class DefaultRageshakePreferencesPresenter @Inject constructor(
private val rageshake: RageShake,
private val rageshakeDataStore: RageshakeDataStore,
private val rageshakeFeatureAvailability: RageshakeFeatureAvailability,
) : RageshakePreferencesPresenter {
@Composable
override fun present(): RageshakePreferencesState {
@ -35,6 +38,7 @@ class DefaultRageshakePreferencesPresenter @Inject constructor(
val isSupported: MutableState<Boolean> = rememberSaveable {
mutableStateOf(rageshake.isAvailable())
}
val isFeatureAvailable = remember { rageshakeFeatureAvailability.isAvailable() }
val isEnabled = rageshakeDataStore
.isEnabled()
.collectAsState(initial = false)
@ -51,6 +55,7 @@ class DefaultRageshakePreferencesPresenter @Inject constructor(
}
return RageshakePreferencesState(
isFeatureEnabled = isFeatureAvailable,
isEnabled = isEnabled.value,
isSupported = isSupported.value,
sensitivity = sensitivity.value,

View file

@ -13,7 +13,6 @@ import android.hardware.SensorManager
import androidx.core.content.getSystemService
import com.squareup.anvil.annotations.ContributesBinding
import com.squareup.seismic.ShakeDetector
import io.element.android.features.rageshake.api.rageshake.RageShake
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.ApplicationContext
import io.element.android.libraries.di.SingleIn

View file

@ -15,7 +15,6 @@ import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.floatPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.features.rageshake.api.rageshake.RageshakeDataStore
import io.element.android.libraries.core.bool.orFalse
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.ApplicationContext

View file

@ -0,0 +1,27 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.features.rageshake.impl.rageshake
interface RageShake {
/**
* Check if the feature is available on this device.
*/
fun isAvailable(): Boolean
fun start(sensitivity: Float)
fun stop()
/**
* sensitivity will be {0, O.25, 0.5, 0.75, 1} and converted to
* [ShakeDetector.SENSITIVITY_LIGHT (=11), ShakeDetector.SENSITIVITY_HARD (=15)].
*/
fun setSensitivity(sensitivity: Float)
fun setInterceptor(interceptor: (() -> Unit)?)
}

View file

@ -0,0 +1,22 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.features.rageshake.impl.rageshake
import kotlinx.coroutines.flow.Flow
interface RageshakeDataStore {
fun isEnabled(): Flow<Boolean>
suspend fun setIsEnabled(isEnabled: Boolean)
fun sensitivity(): Flow<Float>
suspend fun setSensitivity(sensitivity: Float)
suspend fun reset()
}

View file

@ -13,10 +13,10 @@ import androidx.core.net.toFile
import androidx.core.net.toUri
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.appconfig.RageshakeConfig
import io.element.android.features.rageshake.api.crash.CrashDataStore
import io.element.android.features.rageshake.api.reporter.BugReporter
import io.element.android.features.rageshake.api.reporter.BugReporterListener
import io.element.android.features.rageshake.api.screenshot.ScreenshotHolder
import io.element.android.features.rageshake.impl.crash.CrashDataStore
import io.element.android.features.rageshake.impl.screenshot.ScreenshotHolder
import io.element.android.libraries.androidutils.file.compressFile
import io.element.android.libraries.androidutils.file.safeDelete
import io.element.android.libraries.core.coroutine.CoroutineDispatchers

View file

@ -11,7 +11,6 @@ import android.content.Context
import android.graphics.Bitmap
import androidx.core.net.toUri
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.features.rageshake.api.screenshot.ScreenshotHolder
import io.element.android.libraries.androidutils.bitmap.writeBitmap
import io.element.android.libraries.androidutils.file.safeDelete
import io.element.android.libraries.di.AppScope

View file

@ -0,0 +1,16 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.features.rageshake.impl.screenshot
import android.graphics.Bitmap
interface ScreenshotHolder {
fun writeBitmap(data: Bitmap)
fun getFileUri(): String?
fun reset()
}

View file

@ -11,13 +11,13 @@ import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
import io.element.android.features.rageshake.api.crash.CrashDataStore
import io.element.android.features.rageshake.api.reporter.BugReporter
import io.element.android.features.rageshake.api.screenshot.ScreenshotHolder
import io.element.android.features.rageshake.test.crash.A_CRASH_DATA
import io.element.android.features.rageshake.test.crash.FakeCrashDataStore
import io.element.android.features.rageshake.test.screenshot.A_SCREENSHOT_URI
import io.element.android.features.rageshake.test.screenshot.FakeScreenshotHolder
import io.element.android.features.rageshake.impl.crash.A_CRASH_DATA
import io.element.android.features.rageshake.impl.crash.CrashDataStore
import io.element.android.features.rageshake.impl.crash.FakeCrashDataStore
import io.element.android.features.rageshake.impl.screenshot.A_SCREENSHOT_URI
import io.element.android.features.rageshake.impl.screenshot.FakeScreenshotHolder
import io.element.android.features.rageshake.impl.screenshot.ScreenshotHolder
import io.element.android.libraries.architecture.AsyncAction
import io.element.android.libraries.matrix.test.A_FAILURE_REASON
import io.element.android.tests.testutils.WarmUpRule

View file

@ -0,0 +1,38 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.features.rageshake.impl.crash
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
const val A_CRASH_DATA = "Some crash data"
class FakeCrashDataStore(
crashData: String = "",
appHasCrashed: Boolean = false,
) : CrashDataStore {
private val appHasCrashedFlow = MutableStateFlow(appHasCrashed)
private val crashDataFlow = MutableStateFlow(crashData)
override fun setCrashData(crashData: String) {
crashDataFlow.value = crashData
}
override suspend fun resetAppHasCrashed() {
appHasCrashedFlow.value = false
}
override fun appHasCrashed(): Flow<Boolean> = appHasCrashedFlow
override fun crashInfo(): Flow<String> = crashDataFlow
override suspend fun reset() {
appHasCrashedFlow.value = false
crashDataFlow.value = ""
}
}

View file

@ -12,9 +12,9 @@ import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
import io.element.android.features.rageshake.api.crash.CrashDetectionEvents
import io.element.android.features.rageshake.impl.crash.A_CRASH_DATA
import io.element.android.features.rageshake.impl.crash.DefaultCrashDetectionPresenter
import io.element.android.features.rageshake.test.crash.A_CRASH_DATA
import io.element.android.features.rageshake.test.crash.FakeCrashDataStore
import io.element.android.features.rageshake.impl.crash.FakeCrashDataStore
import io.element.android.libraries.core.meta.BuildMeta
import io.element.android.libraries.matrix.test.core.aBuildMeta
import io.element.android.tests.testutils.WarmUpRule
@ -51,6 +51,20 @@ class CrashDetectionPresenterTest {
}
}
@Test
fun `present - initial state crash is ignored if the feature is not available`() = runTest {
val presenter = createPresenter(
FakeCrashDataStore(appHasCrashed = true),
isFeatureAvailable = false,
)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
val initialState = awaitItem()
assertThat(initialState.crashDetected).isFalse()
}
}
@Test
fun `present - reset app has crashed`() = runTest {
val presenter = createPresenter(
@ -86,8 +100,10 @@ class CrashDetectionPresenterTest {
private fun createPresenter(
crashDataStore: FakeCrashDataStore = FakeCrashDataStore(),
buildMeta: BuildMeta = aBuildMeta(),
isFeatureAvailable: Boolean = true,
) = DefaultCrashDetectionPresenter(
buildMeta = buildMeta,
crashDataStore = crashDataStore,
rageshakeFeatureAvailability = { isFeatureAvailable },
)
}

View file

@ -15,9 +15,9 @@ import com.google.common.truth.Truth.assertThat
import io.element.android.features.rageshake.api.detection.RageshakeDetectionEvents
import io.element.android.features.rageshake.api.screenshot.ImageResult
import io.element.android.features.rageshake.impl.preferences.DefaultRageshakePreferencesPresenter
import io.element.android.features.rageshake.test.rageshake.FakeRageShake
import io.element.android.features.rageshake.test.rageshake.FakeRageshakeDataStore
import io.element.android.features.rageshake.test.screenshot.FakeScreenshotHolder
import io.element.android.features.rageshake.impl.rageshake.FakeRageShake
import io.element.android.features.rageshake.impl.rageshake.FakeRageshakeDataStore
import io.element.android.features.rageshake.impl.screenshot.FakeScreenshotHolder
import io.element.android.libraries.matrix.test.AN_EXCEPTION
import io.element.android.tests.testutils.WarmUpRule
import io.mockk.mockk
@ -52,6 +52,7 @@ class RageshakeDetectionPresenterTest {
preferencesPresenter = DefaultRageshakePreferencesPresenter(
rageshake = rageshake,
rageshakeDataStore = rageshakeDataStore,
rageshakeFeatureAvailability = { true },
)
)
moleculeFlow(RecompositionMode.Immediate) {
@ -76,6 +77,7 @@ class RageshakeDetectionPresenterTest {
preferencesPresenter = DefaultRageshakePreferencesPresenter(
rageshake = rageshake,
rageshakeDataStore = rageshakeDataStore,
rageshakeFeatureAvailability = { true },
)
)
moleculeFlow(RecompositionMode.Immediate) {
@ -101,6 +103,7 @@ class RageshakeDetectionPresenterTest {
preferencesPresenter = DefaultRageshakePreferencesPresenter(
rageshake = rageshake,
rageshakeDataStore = rageshakeDataStore,
rageshakeFeatureAvailability = { true },
)
)
moleculeFlow(RecompositionMode.Immediate) {
@ -135,6 +138,7 @@ class RageshakeDetectionPresenterTest {
preferencesPresenter = DefaultRageshakePreferencesPresenter(
rageshake = rageshake,
rageshakeDataStore = rageshakeDataStore,
rageshakeFeatureAvailability = { true },
)
)
moleculeFlow(RecompositionMode.Immediate) {
@ -169,6 +173,7 @@ class RageshakeDetectionPresenterTest {
preferencesPresenter = DefaultRageshakePreferencesPresenter(
rageshake = rageshake,
rageshakeDataStore = rageshakeDataStore,
rageshakeFeatureAvailability = { true },
)
)
moleculeFlow(RecompositionMode.Immediate) {

View file

@ -12,9 +12,9 @@ import app.cash.molecule.moleculeFlow
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
import io.element.android.features.rageshake.api.preferences.RageshakePreferencesEvents
import io.element.android.features.rageshake.test.rageshake.A_SENSITIVITY
import io.element.android.features.rageshake.test.rageshake.FakeRageShake
import io.element.android.features.rageshake.test.rageshake.FakeRageshakeDataStore
import io.element.android.features.rageshake.impl.rageshake.A_SENSITIVITY
import io.element.android.features.rageshake.impl.rageshake.FakeRageShake
import io.element.android.features.rageshake.impl.rageshake.FakeRageshakeDataStore
import io.element.android.tests.testutils.WarmUpRule
import kotlinx.coroutines.test.runTest
import org.junit.Rule
@ -28,7 +28,8 @@ class RageshakePreferencesPresenterTest {
fun `present - initial state available`() = runTest {
val presenter = DefaultRageshakePreferencesPresenter(
FakeRageShake(isAvailableValue = true),
FakeRageshakeDataStore(isEnabled = true)
FakeRageshakeDataStore(isEnabled = true),
rageshakeFeatureAvailability = { true },
)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
@ -44,7 +45,8 @@ class RageshakePreferencesPresenterTest {
fun `present - initial state not available`() = runTest {
val presenter = DefaultRageshakePreferencesPresenter(
FakeRageShake(isAvailableValue = false),
FakeRageshakeDataStore(isEnabled = true)
FakeRageshakeDataStore(isEnabled = true),
rageshakeFeatureAvailability = { true },
)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
@ -60,7 +62,8 @@ class RageshakePreferencesPresenterTest {
fun `present - enable and disable`() = runTest {
val presenter = DefaultRageshakePreferencesPresenter(
FakeRageShake(isAvailableValue = true),
FakeRageshakeDataStore(isEnabled = true)
FakeRageshakeDataStore(isEnabled = true),
rageshakeFeatureAvailability = { true },
)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
@ -79,7 +82,8 @@ class RageshakePreferencesPresenterTest {
fun `present - set sensitivity`() = runTest {
val presenter = DefaultRageshakePreferencesPresenter(
FakeRageShake(isAvailableValue = true),
FakeRageshakeDataStore(isEnabled = true)
FakeRageshakeDataStore(isEnabled = true),
rageshakeFeatureAvailability = { true },
)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()

View file

@ -0,0 +1,31 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.features.rageshake.impl.rageshake
class FakeRageShake(
private var isAvailableValue: Boolean = true
) : RageShake {
private var interceptor: (() -> Unit)? = null
override fun isAvailable() = isAvailableValue
override fun start(sensitivity: Float) {
}
override fun stop() {
}
override fun setSensitivity(sensitivity: Float) {
}
override fun setInterceptor(interceptor: (() -> Unit)?) {
this.interceptor = interceptor
}
fun triggerPhoneRageshake() = interceptor?.invoke()
}

View file

@ -0,0 +1,34 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.features.rageshake.impl.rageshake
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
const val A_SENSITIVITY = 1f
class FakeRageshakeDataStore(
isEnabled: Boolean = false,
sensitivity: Float = A_SENSITIVITY,
) : RageshakeDataStore {
private val isEnabledFlow = MutableStateFlow(isEnabled)
override fun isEnabled(): Flow<Boolean> = isEnabledFlow
override suspend fun setIsEnabled(isEnabled: Boolean) {
isEnabledFlow.value = isEnabled
}
private val sensitivityFlow = MutableStateFlow(sensitivity)
override fun sensitivity(): Flow<Float> = sensitivityFlow
override suspend fun setSensitivity(sensitivity: Float) {
sensitivityFlow.value = sensitivity
}
override suspend fun reset() = Unit
}

View file

@ -8,9 +8,10 @@
package io.element.android.features.rageshake.impl.reporter
import com.google.common.truth.Truth.assertThat
import io.element.android.appconfig.RageshakeConfig
import io.element.android.features.rageshake.api.reporter.BugReporterListener
import io.element.android.features.rageshake.test.crash.FakeCrashDataStore
import io.element.android.features.rageshake.test.screenshot.FakeScreenshotHolder
import io.element.android.features.rageshake.impl.crash.FakeCrashDataStore
import io.element.android.features.rageshake.impl.screenshot.FakeScreenshotHolder
import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.libraries.matrix.test.FakeMatrixClientProvider
import io.element.android.libraries.matrix.test.FakeSdkMetadata
@ -138,7 +139,7 @@ class DefaultBugReporterTest {
val foundValues = collectValuesFromFormData(request)
assertThat(foundValues["app"]).isEqualTo("element-x-android")
assertThat(foundValues["app"]).isEqualTo(RageshakeConfig.BUG_REPORT_APP_NAME)
assertThat(foundValues["can_contact"]).isEqualTo("true")
assertThat(foundValues["device_id"]).isEqualTo("ABCDEFGH")
assertThat(foundValues["sdk_sha"]).isEqualTo("123456789")

View file

@ -16,7 +16,9 @@ class DefaultBugReporterUrlProviderTest {
@Test
fun `test DefaultBugReporterUrlProvider`() {
val sut = DefaultBugReporterUrlProvider()
val result = sut.provide()
assertThat(result).isEqualTo(RageshakeConfig.BUG_REPORT_URL.toHttpUrl())
if (RageshakeConfig.BUG_REPORT_URL.isNotEmpty()) {
val result = sut.provide()
assertThat(result).isEqualTo(RageshakeConfig.BUG_REPORT_URL.toHttpUrl())
}
}
}

View file

@ -0,0 +1,20 @@
/*
* Copyright 2023, 2024 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.features.rageshake.impl.screenshot
import android.graphics.Bitmap
const val A_SCREENSHOT_URI = "file://content/uri"
class FakeScreenshotHolder(private val screenshotUri: String? = null) : ScreenshotHolder {
override fun writeBitmap(data: Bitmap) = Unit
override fun getFileUri() = screenshotUri
override fun reset() = Unit
}