Merge branch 'develop' into poljar/log-olm-trace

This commit is contained in:
Jorge Martin Espinosa
2024-02-08 09:50:58 +01:00
committed by GitHub
19 changed files with 69 additions and 100 deletions

View File

@@ -28,6 +28,7 @@ import io.element.android.features.rageshake.test.crash.FakeCrashDataStore
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.libraries.matrix.test.core.aBuildMeta
import io.element.android.services.apperror.api.AppErrorState
import io.element.android.services.apperror.api.AppErrorStateService
import io.element.android.services.apperror.impl.DefaultAppErrorStateService
@@ -83,6 +84,7 @@ class RootPresenterTest {
val rageshake = FakeRageShake()
val screenshotHolder = FakeScreenshotHolder()
val crashDetectionPresenter = DefaultCrashDetectionPresenter(
buildMeta = aBuildMeta(),
crashDataStore = crashDataStore
)
val rageshakeDetectionPresenter = DefaultRageshakeDetectionPresenter(

View File

@@ -57,7 +57,6 @@ import io.element.android.libraries.designsystem.theme.components.ButtonSize
import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.TextButton
import io.element.android.libraries.designsystem.theme.temporaryColorBgSpecial
import io.element.android.libraries.designsystem.utils.LogCompositions
import io.element.android.libraries.ui.strings.CommonStrings
import kotlinx.collections.immutable.persistentListOf
@@ -67,7 +66,6 @@ fun AnalyticsOptInView(
onClickTerms: () -> Unit,
modifier: Modifier = Modifier,
) {
LogCompositions(tag = "Analytics", msg = "Root")
val eventSink = state.eventSink
fun onTermsAccepted() {

View File

@@ -86,7 +86,7 @@ fun LogoutView(
onForceLogoutClicked = {
eventSink(LogoutEvents.Logout(ignoreSdkError = true))
},
onDismissError = {
onDismissDialog = {
eventSink(LogoutEvents.CloseDialogs)
},
onSuccessLogout = {

View File

@@ -41,7 +41,7 @@ class DefaultDirectLogoutView @Inject constructor() : DirectLogoutView {
onForceLogoutClicked = {
eventSink(DirectLogoutEvents.Logout(ignoreSdkError = true))
},
onDismissError = {
onDismissDialog = {
eventSink(DirectLogoutEvents.CloseDialogs)
},
onSuccessLogout = {

View File

@@ -32,8 +32,7 @@ fun LogoutActionDialog(
state: AsyncAction<String?>,
onConfirmClicked: () -> Unit,
onForceLogoutClicked: () -> Unit,
// TODO Rename
onDismissError: () -> Unit,
onDismissDialog: () -> Unit,
onSuccessLogout: (String?) -> Unit,
) {
when (state) {
@@ -42,7 +41,7 @@ fun LogoutActionDialog(
AsyncAction.Confirming ->
LogoutConfirmationDialog(
onSubmitClicked = onConfirmClicked,
onDismiss = onDismissError
onDismiss = onDismissDialog
)
is AsyncAction.Loading ->
ProgressDialog(text = stringResource(id = R.string.screen_signout_in_progress_dialog_content))
@@ -52,7 +51,7 @@ fun LogoutActionDialog(
content = stringResource(id = CommonStrings.error_unknown),
retryText = stringResource(id = CommonStrings.action_signout_anyway),
onRetry = onForceLogoutClicked,
onDismiss = onDismissError,
onDismiss = onDismissDialog,
)
is AsyncAction.Success -> {
val latestOnSuccessLogout by rememberUpdatedState(onSuccessLogout)

View File

@@ -102,7 +102,6 @@ import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TopAppBar
import io.element.android.libraries.designsystem.utils.KeepScreenOn
import io.element.android.libraries.designsystem.utils.LogCompositions
import io.element.android.libraries.designsystem.utils.OnLifecycleEvent
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarHost
import io.element.android.libraries.designsystem.utils.snackbar.rememberSnackbarHostState
@@ -127,8 +126,6 @@ fun MessagesView(
onJoinCallClicked: () -> Unit,
modifier: Modifier = Modifier,
) {
LogCompositions(tag = "MessagesScreen", msg = "Root")
OnLifecycleEvent { _, event ->
state.voiceMessageComposerState.eventSink(VoiceMessageComposerEvents.LifecycleEvent(event))
}
@@ -146,8 +143,6 @@ fun MessagesView(
// This is needed because the composer is inside an AndroidView that can't be affected by the FocusManager in Compose
val localView = LocalView.current
LogCompositions(tag = "MessagesScreen", msg = "Content")
fun onMessageClicked(event: TimelineItem.Event) {
Timber.v("OnMessageClicked= ${event.id}")
val hideKeyboard = onEventClicked(event)

View File

@@ -20,6 +20,7 @@ import androidx.compose.runtime.Immutable
@Immutable
data class CrashDetectionState(
val appName: String,
val crashDetected: Boolean,
val eventSink: (CrashDetectionEvents) -> Unit
)

View File

@@ -17,6 +17,7 @@
package io.element.android.features.rageshake.api.crash
fun aCrashDetectionState() = CrashDetectionState(
appName = "Element",
crashDetected = false,
eventSink = {}
)

View File

@@ -22,7 +22,6 @@ import io.element.android.features.rageshake.api.R
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.utils.LogCompositions
import io.element.android.libraries.ui.strings.CommonStrings
@Composable
@@ -30,17 +29,13 @@ fun CrashDetectionView(
state: CrashDetectionState,
onOpenBugReport: () -> Unit = { },
) {
LogCompositions(
tag = "Crash",
msg = "CrashDetectionScreen"
)
fun onPopupDismissed() {
state.eventSink(CrashDetectionEvents.ResetAllCrashData)
}
if (state.crashDetected) {
CrashDetectionContent(
appName = state.appName,
onYesClicked = onOpenBugReport,
onNoClicked = ::onPopupDismissed,
onDismiss = ::onPopupDismissed,
@@ -50,14 +45,14 @@ fun CrashDetectionView(
@Composable
private fun CrashDetectionContent(
appName: String,
onNoClicked: () -> Unit = { },
onYesClicked: () -> Unit = { },
onDismiss: () -> Unit = { },
) {
ConfirmationDialog(
title = stringResource(id = CommonStrings.action_report_bug),
// TODO Replace with app name
content = stringResource(id = R.string.crash_detection_dialog_content, "Element"),
content = stringResource(id = R.string.crash_detection_dialog_content, appName),
submitText = stringResource(id = CommonStrings.action_yes),
cancelText = stringResource(id = CommonStrings.action_no),
onCancelClicked = onNoClicked,

View File

@@ -31,7 +31,6 @@ import io.element.android.libraries.androidutils.hardware.vibrate
import io.element.android.libraries.designsystem.components.dialogs.ConfirmationDialog
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.utils.LogCompositions
import io.element.android.libraries.designsystem.utils.OnLifecycleEvent
import io.element.android.libraries.ui.strings.CommonStrings
@@ -40,10 +39,6 @@ fun RageshakeDetectionView(
state: RageshakeDetectionState,
onOpenBugReport: () -> Unit = { },
) {
LogCompositions(
tag = "Rageshake",
msg = "RageshakeDetectionScreen"
)
val eventSink = state.eventSink
val context = LocalContext.current
OnLifecycleEvent { _, event ->

View File

@@ -51,7 +51,6 @@ import io.element.android.libraries.designsystem.preview.debugPlaceholderBackgro
import io.element.android.libraries.designsystem.theme.components.Button
import io.element.android.libraries.designsystem.theme.components.OutlinedTextField
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.utils.LogCompositions
import io.element.android.libraries.ui.strings.CommonStrings
@Composable
@@ -62,7 +61,6 @@ fun BugReportView(
onBackPressed: () -> Unit,
modifier: Modifier = Modifier,
) {
LogCompositions(tag = "Rageshake", msg = "Root")
val eventSink = state.eventSink
Box(modifier = modifier) {

View File

@@ -24,13 +24,17 @@ import io.element.android.features.rageshake.api.crash.CrashDataStore
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.launch
import javax.inject.Inject
@ContributesBinding(AppScope::class)
class DefaultCrashDetectionPresenter @Inject constructor(private val crashDataStore: CrashDataStore) :
class DefaultCrashDetectionPresenter @Inject constructor(
private val buildMeta: BuildMeta,
private val crashDataStore: CrashDataStore,
) :
CrashDetectionPresenter {
@Composable
override fun present(): CrashDetectionState {
@@ -45,6 +49,7 @@ class DefaultCrashDetectionPresenter @Inject constructor(private val crashDataSt
}
return CrashDetectionState(
appName = buildMeta.applicationName,
crashDetected = crashDetected.value,
eventSink = ::handleEvents
)

View File

@@ -24,6 +24,8 @@ import io.element.android.features.rageshake.api.crash.CrashDetectionEvents
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.libraries.core.meta.BuildMeta
import io.element.android.libraries.matrix.test.core.aBuildMeta
import io.element.android.tests.testutils.WarmUpRule
import kotlinx.coroutines.test.runTest
import org.junit.Rule
@@ -35,9 +37,7 @@ class CrashDetectionPresenterTest {
@Test
fun `present - initial state no crash`() = runTest {
val presenter = DefaultCrashDetectionPresenter(
FakeCrashDataStore()
)
val presenter = createPresenter()
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
@@ -48,7 +48,7 @@ class CrashDetectionPresenterTest {
@Test
fun `present - initial state crash`() = runTest {
val presenter = DefaultCrashDetectionPresenter(
val presenter = createPresenter(
FakeCrashDataStore(appHasCrashed = true)
)
moleculeFlow(RecompositionMode.Immediate) {
@@ -62,7 +62,7 @@ class CrashDetectionPresenterTest {
@Test
fun `present - reset app has crashed`() = runTest {
val presenter = DefaultCrashDetectionPresenter(
val presenter = createPresenter(
FakeCrashDataStore(appHasCrashed = true)
)
moleculeFlow(RecompositionMode.Immediate) {
@@ -78,7 +78,7 @@ class CrashDetectionPresenterTest {
@Test
fun `present - reset all crash data`() = runTest {
val presenter = DefaultCrashDetectionPresenter(
val presenter = createPresenter(
FakeCrashDataStore(appHasCrashed = true, crashData = A_CRASH_DATA)
)
moleculeFlow(RecompositionMode.Immediate) {
@@ -91,4 +91,12 @@ class CrashDetectionPresenterTest {
assertThat(awaitItem().crashDetected).isFalse()
}
}
private fun createPresenter(
crashDataStore: FakeCrashDataStore = FakeCrashDataStore(),
buildMeta: BuildMeta = aBuildMeta(),
) = DefaultCrashDetectionPresenter(
buildMeta = buildMeta,
crashDataStore = crashDataStore,
)
}

View File

@@ -67,7 +67,6 @@ import io.element.android.libraries.designsystem.theme.components.IconSource
import io.element.android.libraries.designsystem.theme.components.Scaffold
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.utils.CommonDrawables
import io.element.android.libraries.designsystem.utils.LogCompositions
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarHost
import io.element.android.libraries.designsystem.utils.snackbar.rememberSnackbarHostState
import io.element.android.libraries.matrix.api.core.RoomId
@@ -190,11 +189,6 @@ private fun RoomListContent(
}
}
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(appBarState)
LogCompositions(
tag = "RoomListScreen",
msg = "Content"
)
val nestedScrollConnection = remember {
object : NestedScrollConnection {
override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {

View File

@@ -69,7 +69,6 @@ import io.element.android.libraries.designsystem.theme.components.Icon
import io.element.android.libraries.designsystem.theme.components.IconButton
import io.element.android.libraries.designsystem.theme.components.MediumTopAppBar
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.utils.LogCompositions
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.matrix.ui.model.getAvatarData
@@ -92,11 +91,6 @@ fun RoomListTopBar(
scrollBehavior: TopAppBarScrollBehavior,
modifier: Modifier = Modifier,
) {
LogCompositions(
tag = "RoomListScreen",
msg = "TopBar"
)
fun closeFilter() {
onFilterChanged("")
}

View File

@@ -142,7 +142,7 @@ coil = { module = "io.coil-kt:coil", version.ref = "coil" }
coil_compose = { module = "io.coil-kt:coil-compose", version.ref = "coil" }
coil_gif = { module = "io.coil-kt:coil-gif", version.ref = "coil" }
coil_test = { module = "io.coil-kt:coil-test", version.ref = "coil" }
compound = { module = "io.element.android:compound-android", version = "0.0.3" }
compound = { module = "io.element.android:compound-android", version = "0.0.4" }
datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "datetime" }
serialization_json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "serialization_json" }
kotlinx_collections_immutable = "org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.7"
@@ -152,7 +152,7 @@ jsoup = "org.jsoup:jsoup:1.17.2"
appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" }
molecule-runtime = "app.cash.molecule:molecule-runtime:1.3.2"
timber = "com.jakewharton.timber:timber:5.0.1"
matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.97"
matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.98"
matrix_richtexteditor = { module = "io.element.android:wysiwyg", version.ref = "wysiwyg" }
matrix_richtexteditor_compose = { module = "io.element.android:wysiwyg-compose", version.ref = "wysiwyg" }
sqldelight-driver-android = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" }

View File

@@ -1,39 +0,0 @@
/*
* Copyright (c) 2023 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 io.element.android.libraries.designsystem.utils
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.remember
import io.element.android.libraries.designsystem.BuildConfig
import timber.log.Timber
// Note the inline function below which ensures that this function is essentially
// copied at the call site to ensure that its logging only recompositions from the
// original call site.
@Composable
fun LogCompositions(tag: String, msg: String) {
if (BuildConfig.DEBUG) {
val ref = remember { Ref() }
SideEffect { ref.value++ }
Timber.tag(tag).d("Compositions: $msg ${ref.value}")
}
}
private class Ref {
var value: Int = 0
}

View File

@@ -60,6 +60,7 @@ import io.element.android.libraries.matrix.impl.roomlist.roomOrNull
import io.element.android.libraries.matrix.impl.sync.RustSyncService
import io.element.android.libraries.matrix.impl.usersearch.UserProfileMapper
import io.element.android.libraries.matrix.impl.usersearch.UserSearchResultMapper
import io.element.android.libraries.matrix.impl.util.SessionDirectoryNameProvider
import io.element.android.libraries.matrix.impl.util.cancelAndDestroy
import io.element.android.libraries.matrix.impl.verification.RustSessionVerificationService
import io.element.android.libraries.sessionstorage.api.SessionStore
@@ -134,6 +135,7 @@ class RustMatrixClient(
sessionCoroutineScope = sessionCoroutineScope,
dispatchers = dispatchers,
).apply { start() }
private val sessionDirectoryNameProvider = SessionDirectoryNameProvider()
private val isLoggingOut = AtomicBoolean(false)
@@ -397,13 +399,12 @@ class RustMatrixClient(
}
override suspend fun getCacheSize(): Long {
// Do not use client.userId since it can throw if client has been closed (during clear cache)
return baseDirectory.getCacheSize(userID = sessionId.value)
return baseDirectory.getCacheSize()
}
override suspend fun clearCache() {
close()
baseDirectory.deleteSessionDirectory(userID = sessionId.value, deleteCryptoDb = false)
baseDirectory.deleteSessionDirectory(deleteCryptoDb = false)
}
override suspend fun logout(ignoreSdkError: Boolean): String? = doLogout(
@@ -432,7 +433,7 @@ class RustMatrixClient(
}
}
close()
baseDirectory.deleteSessionDirectory(userID = sessionId.value, deleteCryptoDb = true)
baseDirectory.deleteSessionDirectory(deleteCryptoDb = true)
if (removeSession) {
sessionStore.removeSession(sessionId.value)
}
@@ -478,12 +479,10 @@ class RustMatrixClient(
override fun roomMembershipObserver(): RoomMembershipObserver = roomMembershipObserver
private suspend fun File.getCacheSize(
userID: String,
includeCryptoDb: Boolean = false,
): Long = withContext(sessionDispatcher) {
// Rust sanitises the user ID replacing invalid characters with an _
val sanitisedUserID = userID.replace(":", "_")
val sessionDirectory = File(this@getCacheSize, sanitisedUserID)
val sessionDirectoryName = sessionDirectoryNameProvider.provides(sessionId)
val sessionDirectory = File(this@getCacheSize, sessionDirectoryName)
if (includeCryptoDb) {
sessionDirectory.getSizeOfFiles()
} else {
@@ -500,12 +499,10 @@ class RustMatrixClient(
}
private suspend fun File.deleteSessionDirectory(
userID: String,
deleteCryptoDb: Boolean = false,
): Boolean = withContext(sessionDispatcher) {
// Rust sanitises the user ID replacing invalid characters with an _
val sanitisedUserID = userID.replace(":", "_")
val sessionDirectory = File(this@deleteSessionDirectory, sanitisedUserID)
val sessionDirectoryName = sessionDirectoryNameProvider.provides(sessionId)
val sessionDirectory = File(this@deleteSessionDirectory, sessionDirectoryName)
if (deleteCryptoDb) {
// Delete the folder and all its content
sessionDirectory.deleteRecursively()

View File

@@ -0,0 +1,26 @@
/*
* 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 io.element.android.libraries.matrix.impl.util
import io.element.android.libraries.matrix.api.core.SessionId
class SessionDirectoryNameProvider {
// Rust sanitises the user ID replacing invalid characters with an _
fun provides(sessionId: SessionId): String {
return sessionId.value.replace(":", "_")
}
}