diff --git a/app/src/main/kotlin/com/wire/android/ui/debug/DebugDataOptions.kt b/app/src/main/kotlin/com/wire/android/ui/debug/DebugDataOptions.kt index 75e183b8bf8..030be999d54 100644 --- a/app/src/main/kotlin/com/wire/android/ui/debug/DebugDataOptions.kt +++ b/app/src/main/kotlin/com/wire/android/ui/debug/DebugDataOptions.kt @@ -17,7 +17,6 @@ */ package com.wire.android.ui.debug -import android.content.Context import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size @@ -26,19 +25,12 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.setValue +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.hilt.navigation.compose.hiltViewModel -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope import com.wire.android.BuildConfig import com.wire.android.R -import com.wire.android.datastore.GlobalDataStore -import com.wire.android.di.CurrentAccount -import com.wire.android.migration.failure.UserMigrationStatus import com.wire.android.model.Clickable import com.wire.android.ui.common.RowItemTemplate import com.wire.android.ui.common.WireDialog @@ -53,214 +45,13 @@ import com.wire.android.ui.home.settings.SettingsItem import com.wire.android.ui.theme.wireColorScheme import com.wire.android.ui.theme.wireDimensions import com.wire.android.ui.theme.wireTypography -import com.wire.android.util.getDependenciesVersion -import com.wire.android.util.getDeviceIdString -import com.wire.android.util.getGitBuildId import com.wire.android.util.ui.PreviewMultipleThemes import com.wire.kalium.logic.CoreFailure -import com.wire.kalium.logic.E2EIFailure import com.wire.kalium.logic.data.user.UserId -import com.wire.kalium.logic.feature.debug.DisableEventProcessingUseCase -import com.wire.kalium.logic.feature.e2ei.CheckCrlRevocationListUseCase import com.wire.kalium.logic.feature.e2ei.usecase.E2EIEnrollmentResult -import com.wire.kalium.logic.feature.keypackage.MLSKeyPackageCountResult -import com.wire.kalium.logic.feature.keypackage.MLSKeyPackageCountUseCase import com.wire.kalium.logic.functional.Either -import com.wire.kalium.logic.functional.fold -import com.wire.kalium.logic.sync.periodic.UpdateApiVersionsScheduler -import com.wire.kalium.logic.sync.slow.RestartSlowSyncProcessForRecoveryUseCase -import dagger.hilt.android.lifecycle.HiltViewModel -import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.collections.immutable.ImmutableMap import kotlinx.collections.immutable.persistentMapOf -import kotlinx.collections.immutable.toImmutableMap -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.launch -import javax.inject.Inject - -//region DebugDataOptionsViewModel -data class DebugDataOptionsState( - val isEncryptedProteusStorageEnabled: Boolean = false, - val isEventProcessingDisabled: Boolean = false, - val keyPackagesCount: Int = 0, - val mslClientId: String = "null", - val mlsErrorMessage: String = "null", - val isManualMigrationAllowed: Boolean = false, - val debugId: String = "null", - val commitish: String = "null", - val certificate: String = "null", - val showCertificate: Boolean = false, - val startGettingE2EICertificate: Boolean = false, - val dependencies: ImmutableMap = persistentMapOf() -) - -@Suppress("LongParameterList") -@HiltViewModel -class DebugDataOptionsViewModel -@Inject constructor( - @ApplicationContext private val context: Context, - @CurrentAccount val currentAccount: UserId, - private val globalDataStore: GlobalDataStore, - private val updateApiVersions: UpdateApiVersionsScheduler, - private val mlsKeyPackageCountUseCase: MLSKeyPackageCountUseCase, - private val restartSlowSyncProcessForRecovery: RestartSlowSyncProcessForRecoveryUseCase, - private val disableEventProcessingUseCase: DisableEventProcessingUseCase, - private val checkCrlRevocationListUseCase: CheckCrlRevocationListUseCase -) : ViewModel() { - - var state by mutableStateOf( - DebugDataOptionsState() - ) - - init { - observeEncryptedProteusStorageState() - observeMlsMetadata() - checkIfCanTriggerManualMigration() - checkDependenciesVersion() - setGitHashAndDeviceId() - } - - private fun setGitHashAndDeviceId() { - viewModelScope.launch { - val deviceId = context.getDeviceIdString() ?: "null" - val gitBuildId = context.getGitBuildId() - state = state.copy( - debugId = deviceId, - commitish = gitBuildId - ) - } - } - - fun checkDependenciesVersion() { - viewModelScope.launch { - val dependencies = context.getDependenciesVersion().toImmutableMap() - state = state.copy( - dependencies = dependencies - ) - } - } - - fun checkCrlRevocationList() { - viewModelScope.launch { - checkCrlRevocationListUseCase( - forceUpdate = true - ) - } - } - - fun enableEncryptedProteusStorage(enabled: Boolean) { - if (enabled) { - viewModelScope.launch { - globalDataStore.setEncryptedProteusStorageEnabled(true) - } - } - } - - fun restartSlowSyncForRecovery() { - viewModelScope.launch { - restartSlowSyncProcessForRecovery() - } - } - - fun enrollE2EICertificate() { - state = state.copy(startGettingE2EICertificate = true) - } - - fun handleE2EIEnrollmentResult(result: Either) { - result.fold({ - state = state.copy( - certificate = (it as E2EIFailure.OAuth).reason, - showCertificate = true, - startGettingE2EICertificate = false - ) - }, { - if (it is E2EIEnrollmentResult.Finalized) { - state = state.copy( - certificate = it.certificate, - showCertificate = true, - startGettingE2EICertificate = false - ) - } else { - state.copy( - certificate = it.toString(), - showCertificate = true, - startGettingE2EICertificate = false - ) - } - }) - } - - fun dismissCertificateDialog() { - state = state.copy( - showCertificate = false, - ) - } - - fun forceUpdateApiVersions() { - updateApiVersions.scheduleImmediateApiVersionUpdate() - } - - fun disableEventProcessing(disabled: Boolean) { - viewModelScope.launch { - disableEventProcessingUseCase(disabled) - state = state.copy(isEventProcessingDisabled = disabled) - } - } - - //region Private - private fun observeEncryptedProteusStorageState() { - viewModelScope.launch { - globalDataStore.isEncryptedProteusStorageEnabled().collect { - state = state.copy(isEncryptedProteusStorageEnabled = it) - } - } - } - - // If status is NoNeed, it means that the user has already been migrated in and older app version, - // or it is a new install - // this is why we check the existence of the database file - private fun checkIfCanTriggerManualMigration() { - viewModelScope.launch { - globalDataStore.getUserMigrationStatus(currentAccount.value).first() - .let { migrationStatus -> - if (migrationStatus != UserMigrationStatus.NoNeed) { - context.getDatabasePath(currentAccount.value).let { - state = state.copy( - isManualMigrationAllowed = (it.exists() && it.isFile) - ) - } - } - } - } - } - - private fun observeMlsMetadata() { - viewModelScope.launch { - mlsKeyPackageCountUseCase().let { - when (it) { - is MLSKeyPackageCountResult.Success -> { - state = state.copy( - keyPackagesCount = it.count, - mslClientId = it.clientId.value - ) - } - - is MLSKeyPackageCountResult.Failure.NetworkCallFailure -> { - state = state.copy(mlsErrorMessage = "Network Error!") - } - - is MLSKeyPackageCountResult.Failure.FetchClientIdFailure -> { - state = state.copy(mlsErrorMessage = "ClientId Fetch Error!") - } - - is MLSKeyPackageCountResult.Failure.Generic -> {} - } - } - } - } - //endregion -} -//endregion @Composable fun DebugDataOptions( @@ -283,7 +74,8 @@ fun DebugDataOptions( enrollE2EICertificate = viewModel::enrollE2EICertificate, handleE2EIEnrollmentResult = viewModel::handleE2EIEnrollmentResult, dismissCertificateDialog = viewModel::dismissCertificateDialog, - checkCrlRevocationList = viewModel::checkCrlRevocationList + checkCrlRevocationList = viewModel::checkCrlRevocationList, + dependenciesMap = viewModel.state.dependencies ) } @@ -302,7 +94,8 @@ fun DebugDataOptionsContent( enrollE2EICertificate: () -> Unit, handleE2EIEnrollmentResult: (Either) -> Unit, dismissCertificateDialog: () -> Unit, - checkCrlRevocationList: () -> Unit + checkCrlRevocationList: () -> Unit, + dependenciesMap: ImmutableMap ) { Column { @@ -337,6 +130,7 @@ fun DebugDataOptionsContent( onClick = { onCopyText(state.commitish) } ) ) + DependenciesItem(dependenciesMap) if (BuildConfig.PRIVATE_BUILD) { SettingsItem( @@ -399,7 +193,6 @@ fun DebugDataOptionsContent( onDisableEventProcessingChange = onDisableEventProcessingChange, onRestartSlowSyncForRecovery = onRestartSlowSyncForRecovery, onForceUpdateApiVersions = onForceUpdateApiVersions, - dependenciesMap = state.dependencies, checkCrlRevocationList = checkCrlRevocationList ) } @@ -569,7 +362,6 @@ private fun DebugToolsOptions( onDisableEventProcessingChange: (Boolean) -> Unit, onRestartSlowSyncForRecovery: () -> Unit, onForceUpdateApiVersions: () -> Unit, - dependenciesMap: ImmutableMap, checkCrlRevocationList: () -> Unit ) { FolderHeader(stringResource(R.string.label_debug_tools_title)) @@ -641,20 +433,32 @@ private fun DebugToolsOptions( ) } ) - RowItemTemplate( - modifier = Modifier.wrapContentWidth(), - title = { - Text( - style = MaterialTheme.wireTypography.body01, - color = MaterialTheme.wireColorScheme.onBackground, - text = prettyPrintMap(dependenciesMap), - modifier = Modifier.padding(start = dimensions().spacing8x) - ) - } - ) } } +/** + * Compose function that will display the list of dependencies + * @param dependencies an Immutable map of a dependency name to its version number + */ +@Composable +fun DependenciesItem(dependencies: ImmutableMap) { + val title = stringResource(id = R.string.item_dependencies_title) + val text = remember { + prettyPrintMap(dependencies, title) + } + RowItemTemplate( + modifier = Modifier.wrapContentWidth(), + title = { + Text( + style = MaterialTheme.wireTypography.body01, + color = MaterialTheme.wireColorScheme.onBackground, + text = text, + modifier = Modifier.padding(start = dimensions().spacing8x) + ) + } + ) +} + @Composable private fun DisableEventProcessingSwitch( isEnabled: Boolean = false, @@ -685,8 +489,8 @@ private fun DisableEventProcessingSwitch( } @Stable -private fun prettyPrintMap(map: ImmutableMap): String = StringBuilder().apply { - append("Dependencies:\n") +private fun prettyPrintMap(map: ImmutableMap, title: String): String = StringBuilder().apply { + append("$title\n") map.forEach { (key, value) -> append("$key: $value\n") } @@ -718,6 +522,7 @@ fun PreviewOtherDebugOptions() { enrollE2EICertificate = {}, handleE2EIEnrollmentResult = {}, dismissCertificateDialog = {}, - checkCrlRevocationList = {} + checkCrlRevocationList = {}, + dependenciesMap = persistentMapOf() ) } diff --git a/app/src/main/kotlin/com/wire/android/ui/debug/DebugDataOptionsState.kt b/app/src/main/kotlin/com/wire/android/ui/debug/DebugDataOptionsState.kt new file mode 100644 index 00000000000..c0f648184dc --- /dev/null +++ b/app/src/main/kotlin/com/wire/android/ui/debug/DebugDataOptionsState.kt @@ -0,0 +1,36 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ +package com.wire.android.ui.debug + +import kotlinx.collections.immutable.ImmutableMap +import kotlinx.collections.immutable.persistentMapOf + +data class DebugDataOptionsState( + val isEncryptedProteusStorageEnabled: Boolean = false, + val isEventProcessingDisabled: Boolean = false, + val keyPackagesCount: Int = 0, + val mslClientId: String = "null", + val mlsErrorMessage: String = "null", + val isManualMigrationAllowed: Boolean = false, + val debugId: String = "null", + val commitish: String = "null", + val certificate: String = "null", + val showCertificate: Boolean = false, + val startGettingE2EICertificate: Boolean = false, + val dependencies: ImmutableMap = persistentMapOf() +) diff --git a/app/src/main/kotlin/com/wire/android/ui/debug/DebugDataOptionsViewModel.kt b/app/src/main/kotlin/com/wire/android/ui/debug/DebugDataOptionsViewModel.kt new file mode 100644 index 00000000000..944c27e0336 --- /dev/null +++ b/app/src/main/kotlin/com/wire/android/ui/debug/DebugDataOptionsViewModel.kt @@ -0,0 +1,215 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ +package com.wire.android.ui.debug + +import android.content.Context +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.wire.android.datastore.GlobalDataStore +import com.wire.android.di.CurrentAccount +import com.wire.android.migration.failure.UserMigrationStatus +import com.wire.android.util.getDependenciesVersion +import com.wire.android.util.getDeviceIdString +import com.wire.android.util.getGitBuildId +import com.wire.kalium.logic.CoreFailure +import com.wire.kalium.logic.E2EIFailure +import com.wire.kalium.logic.data.user.UserId +import com.wire.kalium.logic.feature.e2ei.CheckCrlRevocationListUseCase +import com.wire.kalium.logic.feature.e2ei.usecase.E2EIEnrollmentResult +import com.wire.kalium.logic.feature.keypackage.MLSKeyPackageCountResult +import com.wire.kalium.logic.feature.keypackage.MLSKeyPackageCountUseCase +import com.wire.kalium.logic.functional.Either +import com.wire.kalium.logic.functional.fold +import com.wire.kalium.logic.sync.periodic.UpdateApiVersionsScheduler +import com.wire.kalium.logic.sync.slow.RestartSlowSyncProcessForRecoveryUseCase +import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext +import kotlinx.collections.immutable.toImmutableMap +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.launch +import javax.inject.Inject + +@Suppress("LongParameterList") +@HiltViewModel +class DebugDataOptionsViewModel +@Inject constructor( + @ApplicationContext private val context: Context, + @CurrentAccount val currentAccount: UserId, + private val globalDataStore: GlobalDataStore, + private val updateApiVersions: UpdateApiVersionsScheduler, + private val mlsKeyPackageCount: MLSKeyPackageCountUseCase, + private val restartSlowSyncProcessForRecovery: RestartSlowSyncProcessForRecoveryUseCase, + private val checkCrlRevocationList: CheckCrlRevocationListUseCase +) : ViewModel() { + + var state by mutableStateOf( + DebugDataOptionsState() + ) + + init { + observeEncryptedProteusStorageState() + observeMlsMetadata() + checkIfCanTriggerManualMigration() + setGitHashAndDeviceId() + checkDependenciesVersion() + } + + private fun checkDependenciesVersion() { + viewModelScope.launch { + val dependencies = context.getDependenciesVersion().toImmutableMap() + state = state.copy( + dependencies = dependencies + ) + } + } + + private fun setGitHashAndDeviceId() { + viewModelScope.launch { + val deviceId = context.getDeviceIdString() ?: "null" + val gitBuildId = context.getGitBuildId() + state = state.copy( + debugId = deviceId, + commitish = gitBuildId + ) + } + } + + fun checkCrlRevocationList() { + viewModelScope.launch { + checkCrlRevocationList( + forceUpdate = true + ) + } + } + + fun enableEncryptedProteusStorage(enabled: Boolean) { + if (enabled) { + viewModelScope.launch { + globalDataStore.setEncryptedProteusStorageEnabled(true) + } + } + } + + fun restartSlowSyncForRecovery() { + viewModelScope.launch { + restartSlowSyncProcessForRecovery() + } + } + + fun enrollE2EICertificate() { + state = state.copy(startGettingE2EICertificate = true) + } + + fun handleE2EIEnrollmentResult(result: Either) { + result.fold({ + state = state.copy( + certificate = (it as E2EIFailure.OAuth).reason, + showCertificate = true, + startGettingE2EICertificate = false + ) + }, { + if (it is E2EIEnrollmentResult.Finalized) { + state = state.copy( + certificate = it.certificate, + showCertificate = true, + startGettingE2EICertificate = false + ) + } else { + state.copy( + certificate = it.toString(), + showCertificate = true, + startGettingE2EICertificate = false + ) + } + }) + } + + fun dismissCertificateDialog() { + state = state.copy( + showCertificate = false, + ) + } + + fun forceUpdateApiVersions() { + updateApiVersions.scheduleImmediateApiVersionUpdate() + } + + fun disableEventProcessing(disabled: Boolean) { + viewModelScope.launch { + disableEventProcessing(disabled) + state = state.copy(isEventProcessingDisabled = disabled) + } + } + + //region Private + private fun observeEncryptedProteusStorageState() { + viewModelScope.launch { + globalDataStore.isEncryptedProteusStorageEnabled().collect { + state = state.copy(isEncryptedProteusStorageEnabled = it) + } + } + } + + // If status is NoNeed, it means that the user has already been migrated in and older app version, + // or it is a new install + // this is why we check the existence of the database file + private fun checkIfCanTriggerManualMigration() { + viewModelScope.launch { + globalDataStore.getUserMigrationStatus(currentAccount.value).first() + .let { migrationStatus -> + if (migrationStatus != UserMigrationStatus.NoNeed) { + context.getDatabasePath(currentAccount.value).let { + state = state.copy( + isManualMigrationAllowed = (it.exists() && it.isFile) + ) + } + } + } + } + } + + private fun observeMlsMetadata() { + viewModelScope.launch { + mlsKeyPackageCount().let { + when (it) { + is MLSKeyPackageCountResult.Success -> { + state = state.copy( + keyPackagesCount = it.count, + mslClientId = it.clientId.value + ) + } + + is MLSKeyPackageCountResult.Failure.NetworkCallFailure -> { + state = state.copy(mlsErrorMessage = "Network Error!") + } + + is MLSKeyPackageCountResult.Failure.FetchClientIdFailure -> { + state = state.copy(mlsErrorMessage = "ClientId Fetch Error!") + } + + is MLSKeyPackageCountResult.Failure.Generic -> {} + } + } + } + } + //endregion +} +//endregion diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index cbf1236e7a2..860d6d27dd7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -201,6 +201,7 @@ API VERSIONING E2EI Manual Enrollment Force API versioning update + Dependencies: Update Support Back up & Restore Conversations diff --git a/buildSrc/src/main/kotlin/scripts/compilation.gradle.kts b/buildSrc/src/main/kotlin/scripts/compilation.gradle.kts index ef75caaec30..fbd0013248d 100644 --- a/buildSrc/src/main/kotlin/scripts/compilation.gradle.kts +++ b/buildSrc/src/main/kotlin/scripts/compilation.gradle.kts @@ -36,7 +36,7 @@ val dependenciesVersionTask = project.tasks.register("dependenciesVersionTask", val catalog = catalogs.named("klibs") val pairs = mapOf( "avs" to catalog.findVersion("avs").get().requiredVersion, - "core-crypto" to catalog.findVersion("core-crypto-multiplatform").get().requiredVersion + "core-crypto" to catalog.findVersion("core-crypto").get().requiredVersion ) keyValues.set(pairs) }