[#1854] Crash Reporting Opt In/Out

* [#1854] Crash Reporting Opt In

- Add Crash reporting option to Advanced settings

* Remove crash reporting switcher from Troubleshoot

menu on the Settings screen.

* Crash Reporting Opt In UI

* Feature enabled only for STORE build type

* Changelogs
This commit is contained in:
Honza Rychnovský 2025-04-22 10:58:12 +02:00 committed by GitHub
parent 7cc98528e9
commit 312a974b1c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
29 changed files with 628 additions and 30 deletions

View File

@ -6,6 +6,9 @@ and this application adheres to [Semantic Versioning](https://semver.org/spec/v2
## [Unreleased]
### Added
- The new Crash Reporting Opt In/Out screen has been added
## [1.5.2 (929)] - 2025-04-09
### Changed

View File

@ -90,6 +90,7 @@ class ZcashApplication : CoroutineApplication() {
if (GlobalCrashReporter.register(this, getAvailableCrashReporters())) {
applicationScope.launch {
StandardPreferenceKeys.IS_ANALYTICS_ENABLED.observe(standardPreferenceProvider()).collect {
Twig.debug { "Is crashlytics enabled: $it" }
if (it) {
GlobalCrashReporter.enable()
} else {

View File

@ -78,11 +78,11 @@ private class FirebaseCrashReporterImpl(
}
override fun enable() {
firebaseCrashlytics.setCrashlyticsCollectionEnabled(true)
firebaseCrashlytics.isCrashlyticsCollectionEnabled = true
}
override fun disableAndDelete() {
firebaseCrashlytics.setCrashlyticsCollectionEnabled(false)
firebaseCrashlytics.isCrashlyticsCollectionEnabled = false
firebaseCrashlytics.deleteUnsentReports()
firebaseInstallations.delete()
}

View File

@ -12,6 +12,9 @@ directly impact users rather than highlighting other key architectural updates.*
## [Unreleased]
### Added
- The new Crash Reporting Opt In/Out screen has been added
## [1.5.2 (929)] - 2025-04-09
### Fixed

View File

@ -12,6 +12,9 @@ directly impact users rather than highlighting other key architectural updates.*
## [Unreleased]
### Añadido
- The new Crash Reporting Opt In/Out screen has been added
## [1.5.2 (929)] - 2025-04-09
### Corregido

View File

@ -47,6 +47,7 @@ android {
"src/main/res/ui/common",
"src/main/res/ui/contact",
"src/main/res/ui/connect_keystone",
"src/main/res/ui/crash_reporting_opt_in",
"src/main/res/ui/delete_wallet",
"src/main/res/ui/export_data",
"src/main/res/ui/home",

View File

@ -11,6 +11,7 @@ import co.electriccoin.zcash.ui.screen.advancedsettings.viewmodel.AdvancedSettin
import co.electriccoin.zcash.ui.screen.chooseserver.ChooseServerViewModel
import co.electriccoin.zcash.ui.screen.contact.viewmodel.AddContactViewModel
import co.electriccoin.zcash.ui.screen.contact.viewmodel.UpdateContactViewModel
import co.electriccoin.zcash.ui.screen.crashreporting.viewmodel.CrashReportingViewModel
import co.electriccoin.zcash.ui.screen.feedback.viewmodel.FeedbackViewModel
import co.electriccoin.zcash.ui.screen.integrations.viewmodel.IntegrationsViewModel
import co.electriccoin.zcash.ui.screen.onboarding.viewmodel.OnboardingViewModel
@ -138,4 +139,5 @@ val viewModelModule =
)
}
viewModelOf(::TaxExportViewModel)
viewModelOf(::CrashReportingViewModel)
}

View File

@ -28,6 +28,7 @@ import co.electriccoin.zcash.ui.NavigationArguments.SEND_SCAN_ZIP_321_URI
import co.electriccoin.zcash.ui.NavigationTargets.ABOUT
import co.electriccoin.zcash.ui.NavigationTargets.ADVANCED_SETTINGS
import co.electriccoin.zcash.ui.NavigationTargets.CHOOSE_SERVER
import co.electriccoin.zcash.ui.NavigationTargets.CRASH_REPORTING_OPT_IN
import co.electriccoin.zcash.ui.NavigationTargets.DELETE_WALLET
import co.electriccoin.zcash.ui.NavigationTargets.EXCHANGE_RATE_OPT_IN
import co.electriccoin.zcash.ui.NavigationTargets.EXPORT_PRIVATE_DATA
@ -66,6 +67,7 @@ import co.electriccoin.zcash.ui.screen.contact.AddContactArgs
import co.electriccoin.zcash.ui.screen.contact.UpdateContactArgs
import co.electriccoin.zcash.ui.screen.contact.WrapAddContact
import co.electriccoin.zcash.ui.screen.contact.WrapUpdateContact
import co.electriccoin.zcash.ui.screen.crashreporting.AndroidCrashReportingOptIn
import co.electriccoin.zcash.ui.screen.deletewallet.WrapDeleteWallet
import co.electriccoin.zcash.ui.screen.disconnected.WrapDisconnected
import co.electriccoin.zcash.ui.screen.exchangerate.optin.AndroidExchangeRateOptIn
@ -296,6 +298,9 @@ internal fun MainActivity.Navigation() {
composable(SETTINGS_EXCHANGE_RATE_OPT_IN) {
AndroidSettingsExchangeRateOptIn()
}
composable(CRASH_REPORTING_OPT_IN) {
AndroidCrashReportingOptIn()
}
composable<ScanKeystoneSignInRequest> {
WrapScanKeystoneSignInRequest()
}
@ -617,6 +622,7 @@ object NavigationTargets {
const val SEED_RECOVERY = "seed_recovery"
const val SETTINGS = "settings"
const val SETTINGS_EXCHANGE_RATE_OPT_IN = "settings_exchange_rate_opt_in"
const val CRASH_REPORTING_OPT_IN = "crash_reporting_opt_in"
const val SUPPORT = "support"
const val WHATS_NEW = "whats_new"
}

View File

@ -164,6 +164,11 @@ private fun AdvancedSettingsPreview() =
title = stringRes(R.string.advanced_settings_currency_conversion),
icon = R.drawable.ic_advanced_settings_currency_conversion,
onClick = {}
),
ZashiListItemState(
title = stringRes(R.string.advanced_settings_crash_reporting),
icon = R.drawable.ic_advanced_settings_crash_reporting,
onClick = {}
)
),
deleteButton =

View File

@ -1,12 +1,15 @@
package co.electriccoin.zcash.ui.screen.advancedsettings.viewmodel
import androidx.compose.runtime.mutableStateListOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import cash.z.ecc.sdk.ANDROID_STATE_FLOW_TIMEOUT
import co.electriccoin.zcash.ui.NavigationRouter
import co.electriccoin.zcash.ui.NavigationTargets
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.common.model.DistributionDimension
import co.electriccoin.zcash.ui.common.model.WalletRestoringState
import co.electriccoin.zcash.ui.common.provider.GetVersionInfoProvider
import co.electriccoin.zcash.ui.common.usecase.GetWalletRestoringStateUseCase
import co.electriccoin.zcash.ui.common.usecase.NavigateToTaxExportUseCase
import co.electriccoin.zcash.ui.design.component.ButtonState
@ -25,7 +28,10 @@ class AdvancedSettingsViewModel(
getWalletRestoringState: GetWalletRestoringStateUseCase,
private val navigationRouter: NavigationRouter,
private val navigateToTaxExport: NavigateToTaxExportUseCase,
private val getVersionInfo: GetVersionInfoProvider,
) : ViewModel() {
private val versionInfo by lazy { getVersionInfo() }
val state: StateFlow<AdvancedSettingsState> =
getWalletRestoringState
.observe()
@ -41,7 +47,7 @@ class AdvancedSettingsViewModel(
AdvancedSettingsState(
onBack = ::onBack,
items =
listOfNotNull(
mutableStateListOf(
ZashiListItemState(
title = stringRes(R.string.advanced_settings_recovery),
icon = R.drawable.ic_advanced_settings_recovery,
@ -74,8 +80,19 @@ class AdvancedSettingsViewModel(
icon =
R.drawable.ic_advanced_settings_currency_conversion,
onClick = ::onCurrencyConversionClick
)
).toImmutableList(),
),
).apply {
if (versionInfo.distributionDimension == DistributionDimension.STORE) {
add(
ZashiListItemState(
title = stringRes(R.string.advanced_settings_crash_reporting),
icon =
R.drawable.ic_advanced_settings_crash_reporting,
onClick = ::onCrashReportingClick
)
)
}
}.toImmutableList(),
deleteButton =
ButtonState(
text = stringRes(R.string.advanced_settings_delete_button),
@ -89,6 +106,8 @@ class AdvancedSettingsViewModel(
private fun onCurrencyConversionClick() = navigationRouter.forward(NavigationTargets.SETTINGS_EXCHANGE_RATE_OPT_IN)
private fun onCrashReportingClick() = navigationRouter.forward(NavigationTargets.CRASH_REPORTING_OPT_IN)
private fun onTaxExportClick() =
viewModelScope.launch {
navigateToTaxExport()

View File

@ -0,0 +1,20 @@
package co.electriccoin.zcash.ui.screen.crashreporting
import androidx.activity.compose.BackHandler
import androidx.compose.runtime.Composable
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import co.electriccoin.zcash.ui.screen.crashreporting.view.CrashReportingOptIn
import co.electriccoin.zcash.ui.screen.crashreporting.viewmodel.CrashReportingViewModel
import org.koin.androidx.compose.koinViewModel
@Composable
fun AndroidCrashReportingOptIn() {
val crashReportingViewModel = koinViewModel<CrashReportingViewModel>()
val state = crashReportingViewModel.state.collectAsStateWithLifecycle().value
BackHandler {
state.onBack()
}
CrashReportingOptIn(state = state)
}

View File

@ -0,0 +1,19 @@
package co.electriccoin.zcash.ui.screen.crashreporting.model
data class CrashReportingOptInState(
val isOptedIn: Boolean,
val onBack: () -> Unit,
val onSaveClicked: (enabled: Boolean) -> Unit,
) {
companion object {
fun new(
isOptedIn: Boolean = true,
onBack: () -> Unit = {},
onSaveClicked: (enabled: Boolean) -> Unit = {}
) = CrashReportingOptInState(
isOptedIn = isOptedIn,
onBack = onBack,
onSaveClicked = onSaveClicked
)
}
}

View File

@ -0,0 +1,295 @@
package co.electriccoin.zcash.ui.screen.crashreporting.view
import androidx.annotation.DrawableRes
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import co.electriccoin.zcash.ui.R
import co.electriccoin.zcash.ui.design.component.BlankSurface
import co.electriccoin.zcash.ui.design.component.ZashiButton
import co.electriccoin.zcash.ui.design.component.ZashiButtonDefaults
import co.electriccoin.zcash.ui.design.newcomponent.PreviewScreens
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
import co.electriccoin.zcash.ui.design.theme.colors.ZashiColors
import co.electriccoin.zcash.ui.design.theme.typography.ZashiTypography
import co.electriccoin.zcash.ui.design.util.scaffoldPadding
import co.electriccoin.zcash.ui.screen.crashreporting.model.CrashReportingOptInState
import co.electriccoin.zcash.ui.screen.exchangerate.SecondaryCard
@Composable
fun CrashReportingOptIn(state: CrashReportingOptInState) {
Scaffold { paddingValues ->
Column(
modifier =
Modifier
.fillMaxSize()
.scaffoldPadding(paddingValues)
) {
Button(
contentPadding = PaddingValues(0.dp),
modifier = Modifier.size(40.dp),
onClick = state.onBack,
shape = RoundedCornerShape(12.dp),
colors =
ButtonDefaults.buttonColors(
containerColor = ZashiColors.Btns.Tertiary.btnTertiaryBg
)
) {
Image(
painter = painterResource(id = R.drawable.ic_crash_reporting_opt_in_close),
contentDescription = stringResource(R.string.close_navigation_content_description),
colorFilter = ColorFilter.tint(ZashiColors.Btns.Tertiary.btnTertiaryFg)
)
}
Spacer(modifier = Modifier.height(28.dp))
CrashReportingOptInContent(state)
}
}
}
@Composable
fun CrashReportingOptInContent(state: CrashReportingOptInState) {
var isOptInSelected by remember(state.isOptedIn) { mutableStateOf(state.isOptedIn) }
val isButtonDisabled by remember(state.isOptedIn) {
derivedStateOf {
(state.isOptedIn && isOptInSelected) || (!state.isOptedIn && !isOptInSelected)
}
}
Column(
modifier =
Modifier
.fillMaxHeight()
.verticalScroll(rememberScrollState())
) {
Image(painter = painterResource(R.drawable.crash_reporting_opt_in_all_icons), contentDescription = null)
Spacer(modifier = Modifier.height(16.dp))
Text(
text =
stringResource(
id = R.string.crash_reporting_opt_in_title,
stringResource(R.string.app_name)
),
color = ZashiColors.Text.textPrimary,
style = ZashiTypography.header6,
fontWeight = FontWeight.SemiBold
)
Spacer(modifier = Modifier.height(8.dp))
Text(
text = stringResource(R.string.crash_reporting_opt_in_subtitle),
style = ZashiTypography.textSm,
color = ZashiColors.Text.textTertiary,
)
CrashReportingOptInOptions(
isOptInSelected = isOptInSelected,
setOptInSelected = { isOptInSelected = it }
)
Spacer(modifier = Modifier.height(24.dp))
Spacer(modifier = Modifier.weight(1f))
CrashReportingOptInFooter(
isOptInSelected = isOptInSelected,
isSaveDisabled = isButtonDisabled,
state = state
)
}
}
@Composable
fun CrashReportingOptInFooter(
isOptInSelected: Boolean,
isSaveDisabled: Boolean,
state: CrashReportingOptInState
) {
Column {
Row {
Image(
painter = painterResource(R.drawable.ic_crash_reporting_opt_in_info),
contentDescription = null,
colorFilter = ColorFilter.tint(ZashiColors.Text.textTertiary)
)
Spacer(modifier = Modifier.width(12.dp))
Text(
text = stringResource(R.string.crash_reporting_opt_in_info),
color = ZashiColors.Text.textTertiary,
style = ZashiTypography.textXs
)
}
Spacer(modifier = Modifier.height(20.dp))
ZashiButton(
modifier = Modifier.fillMaxWidth(),
text = stringResource(R.string.crash_reporting_opt_in_save),
onClick = { state.onSaveClicked(isOptInSelected) },
enabled = !isSaveDisabled,
colors = ZashiButtonDefaults.primaryColors()
)
}
}
@Composable
fun CrashReportingOptInOptions(
isOptInSelected: Boolean,
setOptInSelected: (Boolean) -> Unit,
) {
Column {
Spacer(modifier = Modifier.height(24.dp))
Option(
modifier = Modifier.fillMaxWidth(),
image = OptIn,
selectionImage = if (isOptInSelected) Checked else Unchecked,
title = stringResource(R.string.crash_reporting_opt_in_positive),
subtitle = stringResource(R.string.crash_reporting_opt_in_positive_desc),
onClick = { setOptInSelected(true) }
)
Spacer(modifier = Modifier.height(12.dp))
Option(
modifier = Modifier.fillMaxWidth(),
image = OptOut,
selectionImage = if (!isOptInSelected) Checked else Unchecked,
title = stringResource(R.string.crash_reporting_opt_in_negative),
subtitle = stringResource(R.string.crash_reporting_opt_in_negative_desc),
onClick = { setOptInSelected(false) }
)
}
}
@Suppress("LongParameterList")
@Composable
private fun Option(
@DrawableRes image: Int,
@DrawableRes selectionImage: Int,
title: String,
subtitle: String,
onClick: () -> Unit,
modifier: Modifier = Modifier,
) {
SecondaryCard(
modifier =
modifier.clickable(
onClick = onClick,
indication = null,
interactionSource = remember { MutableInteractionSource() },
)
) {
Row(
Modifier.padding(20.dp)
) {
Image(
painter = painterResource(image),
contentDescription = null
)
Spacer(modifier = Modifier.width(12.dp))
Column(
modifier = Modifier.weight(1f),
verticalArrangement = Arrangement.Center
) {
Text(
text = title,
style = ZashiTypography.textSm,
color = ZashiColors.Text.textPrimary,
fontWeight = FontWeight.SemiBold,
)
Spacer(modifier = Modifier.height(4.dp))
Text(
text = subtitle,
style = ZashiTypography.textSm,
color = ZashiColors.Text.textTertiary,
)
}
Image(
painter = painterResource(selectionImage),
contentDescription = null
)
}
}
}
private val OptIn: Int
@DrawableRes
@Composable
get() =
if (isSystemInDarkTheme()) {
R.drawable.ic_opt_in
} else {
R.drawable.ic_opt_in_light
}
private val OptOut: Int
@DrawableRes
@Composable
get() =
if (isSystemInDarkTheme()) {
R.drawable.ic_opt_out
} else {
R.drawable.ic_opt_out_light
}
private val Checked: Int
@DrawableRes
@Composable
get() =
if (isSystemInDarkTheme()) {
R.drawable.ic_checkbox_checked
} else {
R.drawable.ic_checkbox_checked_light
}
private val Unchecked: Int
@DrawableRes
@Composable
get() =
if (isSystemInDarkTheme()) {
R.drawable.ic_checkbox_unchecked
} else {
R.drawable.ic_checkbox_unchecked_light
}
@Suppress("UnusedPrivateMember")
@PreviewScreens
@Composable
private fun CrashReportingOptInPreviews() =
ZcashTheme {
BlankSurface {
CrashReportingOptIn(CrashReportingOptInState.new())
}
}

View File

@ -0,0 +1,71 @@
package co.electriccoin.zcash.ui.screen.crashreporting.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import cash.z.ecc.sdk.ANDROID_STATE_FLOW_TIMEOUT
import co.electriccoin.zcash.preference.StandardPreferenceProvider
import co.electriccoin.zcash.preference.model.entry.BooleanPreferenceDefault
import co.electriccoin.zcash.ui.NavigationRouter
import co.electriccoin.zcash.ui.preference.StandardPreferenceKeys
import co.electriccoin.zcash.ui.screen.crashreporting.model.CrashReportingOptInState
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.WhileSubscribed
import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
class CrashReportingViewModel(
private val standardPreferenceProvider: StandardPreferenceProvider,
private val navigationRouter: NavigationRouter,
) : ViewModel() {
private val isAnalyticsEnabled = booleanStateFlow(StandardPreferenceKeys.IS_ANALYTICS_ENABLED)
val state: StateFlow<CrashReportingOptInState> =
isAnalyticsEnabled
.filterNotNull()
.map { currentOptInState ->
CrashReportingOptInState.new(
isOptedIn = currentOptInState,
onBack = ::onBack,
onSaveClicked = {
onSaveClicked(it)
onBack()
}
)
}.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT),
initialValue =
CrashReportingOptInState.new(
onBack = ::onBack,
onSaveClicked = {
onSaveClicked(it)
onBack()
}
)
)
private fun onSaveClicked(enabled: Boolean) {
setBooleanPreference(StandardPreferenceKeys.IS_ANALYTICS_ENABLED, enabled)
}
private fun setBooleanPreference(
default: BooleanPreferenceDefault,
newState: Boolean
) {
viewModelScope.launch {
default.putValue(standardPreferenceProvider(), newState)
}
}
private fun onBack() = navigationRouter.back()
private fun booleanStateFlow(default: BooleanPreferenceDefault): StateFlow<Boolean?> =
flow<Boolean?> {
emitAll(default.observe(standardPreferenceProvider()))
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT), null)
}

View File

@ -6,7 +6,6 @@ import androidx.compose.runtime.Immutable
data class SettingsTroubleshootingState(
val backgroundSync: TroubleshootingItemState,
val keepScreenOnDuringSync: TroubleshootingItemState,
val analytics: TroubleshootingItemState,
val rescan: TroubleshootingItemState,
)

View File

@ -144,14 +144,6 @@ private fun TroubleshootingMenu(state: SettingsTroubleshootingState) {
},
leadingIcon = { AddIcon(state.keepScreenOnDuringSync.isEnabled) }
)
DropdownMenuItem(
text = { Text(stringResource(id = R.string.settings_troubleshooting_enable_analytics)) },
onClick = {
state.analytics.onClick()
expanded = false
},
leadingIcon = { AddIcon(state.analytics.isEnabled) }
)
// isRescanEnabled means if this feature should be visible, not whether it is enabled as in the case of
// the previous booleans
if (state.rescan.isEnabled) {

View File

@ -54,7 +54,6 @@ class SettingsViewModel(
) : ViewModel() {
private val versionInfo by lazy { getVersionInfo() }
private val isAnalyticsEnabled = booleanStateFlow(StandardPreferenceKeys.IS_ANALYTICS_ENABLED)
private val isBackgroundSyncEnabled = booleanStateFlow(StandardPreferenceKeys.IS_BACKGROUND_SYNC_ENABLED)
private val isKeepScreenOnWhileSyncingEnabled =
booleanStateFlow(StandardPreferenceKeys.IS_KEEP_SCREEN_ON_DURING_SYNC)
@ -63,12 +62,10 @@ class SettingsViewModel(
private val troubleshootingState =
combine(
observeConfiguration(),
isAnalyticsEnabled,
isBackgroundSyncEnabled,
isKeepScreenOnWhileSyncingEnabled,
) { configuration, isAnalyticsEnabled, isBackgroundSyncEnabled, isKeepScreenOnWhileSyncingEnabled ->
) { configuration, isBackgroundSyncEnabled, isKeepScreenOnWhileSyncingEnabled ->
if (configuration != null &&
isAnalyticsEnabled != null &&
isBackgroundSyncEnabled != null &&
isKeepScreenOnWhileSyncingEnabled != null &&
versionInfo.isDebuggable &&
@ -83,10 +80,6 @@ class SettingsViewModel(
TroubleshootingItemState(
isKeepScreenOnWhileSyncingEnabled
) { setKeepScreenOnWhileSyncing(isKeepScreenOnWhileSyncingEnabled.not()) },
analytics =
TroubleshootingItemState(
isAnalyticsEnabled
) { setAnalyticsEnabled(isAnalyticsEnabled.not()) },
rescan =
TroubleshootingItemState(
ConfigurationEntries.IS_RESCAN_ENABLED.getValue(configuration),
@ -195,10 +188,6 @@ class SettingsViewModel(
version = stringRes(R.string.settings_version, versionInfo.versionName)
)
private fun setAnalyticsEnabled(enabled: Boolean) {
setBooleanPreference(StandardPreferenceKeys.IS_ANALYTICS_ENABLED, enabled)
}
private fun setBackgroundSyncEnabled(enabled: Boolean) {
setBooleanPreference(StandardPreferenceKeys.IS_BACKGROUND_SYNC_ENABLED, enabled)
}

View File

@ -0,0 +1,16 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="40dp"
android:height="40dp"
android:viewportWidth="40"
android:viewportHeight="40">
<path
android:pathData="M0,20C0,8.954 8.954,0 20,0C31.046,0 40,8.954 40,20C40,31.046 31.046,40 20,40C8.954,40 0,31.046 0,20Z"
android:fillColor="#454243"/>
<path
android:pathData="M23.027,16.359C22.697,16.029 22.532,15.864 22.47,15.674C22.415,15.507 22.415,15.326 22.47,15.159C22.532,14.969 22.697,14.804 23.027,14.474L25.392,12.108C24.764,11.825 24.067,11.667 23.334,11.667C20.572,11.667 18.334,13.905 18.334,16.667C18.334,17.076 18.383,17.473 18.476,17.854C18.575,18.262 18.625,18.465 18.616,18.594C18.607,18.729 18.587,18.801 18.524,18.921C18.465,19.035 18.351,19.149 18.124,19.377L12.917,24.583C12.227,25.274 12.227,26.393 12.917,27.083C13.608,27.774 14.727,27.774 15.417,27.083L20.624,21.877C20.851,21.649 20.965,21.535 21.08,21.476C21.2,21.414 21.271,21.394 21.406,21.385C21.535,21.376 21.739,21.425 22.146,21.525C22.527,21.617 22.925,21.667 23.334,21.667C26.095,21.667 28.334,19.428 28.334,16.667C28.334,15.933 28.176,15.236 27.892,14.608L25.527,16.974C25.197,17.304 25.032,17.469 24.841,17.531C24.674,17.585 24.494,17.585 24.326,17.531C24.136,17.469 23.971,17.304 23.641,16.974L23.027,16.359Z"
android:strokeLineJoin="round"
android:strokeWidth="1.66667"
android:fillColor="#00000000"
android:strokeColor="#E8E8E8"
android:strokeLineCap="round"/>
</vector>

View File

@ -0,0 +1,16 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="40dp"
android:height="40dp"
android:viewportWidth="40"
android:viewportHeight="40">
<path
android:pathData="M0,20C0,8.954 8.954,0 20,0C31.046,0 40,8.954 40,20C40,31.046 31.046,40 20,40C8.954,40 0,31.046 0,20Z"
android:fillColor="#EBEBE6"/>
<path
android:pathData="M23.027,16.359C22.697,16.029 22.532,15.864 22.47,15.674C22.415,15.507 22.415,15.326 22.47,15.159C22.532,14.969 22.697,14.804 23.027,14.474L25.392,12.108C24.764,11.825 24.067,11.667 23.334,11.667C20.572,11.667 18.334,13.905 18.334,16.667C18.334,17.076 18.383,17.473 18.476,17.854C18.575,18.262 18.625,18.465 18.616,18.594C18.607,18.729 18.587,18.801 18.524,18.921C18.465,19.035 18.351,19.149 18.124,19.377L12.917,24.583C12.227,25.274 12.227,26.393 12.917,27.083C13.608,27.774 14.727,27.774 15.417,27.083L20.624,21.877C20.851,21.649 20.965,21.535 21.08,21.476C21.2,21.414 21.271,21.394 21.406,21.385C21.535,21.376 21.739,21.425 22.146,21.525C22.527,21.617 22.925,21.667 23.334,21.667C26.095,21.667 28.334,19.428 28.334,16.667C28.334,15.933 28.176,15.236 27.892,14.608L25.527,16.974C25.197,17.304 25.032,17.469 24.841,17.531C24.674,17.585 24.494,17.585 24.326,17.531C24.136,17.469 23.971,17.304 23.641,16.974L23.027,16.359Z"
android:strokeLineJoin="round"
android:strokeWidth="1.66667"
android:fillColor="#00000000"
android:strokeColor="#231F20"
android:strokeLineCap="round"/>
</vector>

View File

@ -5,6 +5,7 @@
<string name="advanced_settings_export">Exportar Datos Privados</string>
<string name="advanced_settings_choose_server">Elegir un Servidor</string>
<string name="advanced_settings_currency_conversion">Conversión de Moneda</string>
<string name="advanced_settings_crash_reporting">Crash Reporting</string>
<string name="advanced_settings_info">Se te pedirá confirmación en la siguiente pantalla</string>
<string name="advanced_settings_delete_button">Restablecer Zashi</string>
<string name="advanced_settings_tax">Exportar Archivo de Impuestos</string>

View File

@ -5,6 +5,7 @@
<string name="advanced_settings_export">Export Private Data</string>
<string name="advanced_settings_choose_server">Choose a Server</string>
<string name="advanced_settings_currency_conversion">Currency Conversion</string>
<string name="advanced_settings_crash_reporting">Crash Reporting</string>
<string name="advanced_settings_info">You will be asked to confirm on the next screen</string>
<string name="advanced_settings_delete_button">Reset Zashi</string>
<string name="advanced_settings_tax">Export Tax File</string>

View File

@ -0,0 +1,37 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="132dp"
android:height="72dp"
android:viewportWidth="132"
android:viewportHeight="72">
<path
android:pathData="M36,2C17.22,2 2,17.22 2,36C2,54.78 17.22,70 36,70C54.78,70 70,54.78 70,36C70,17.22 54.78,2 36,2Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M36,2C17.22,2 2,17.22 2,36C2,54.78 17.22,70 36,70C54.78,70 70,54.78 70,36C70,17.22 54.78,2 36,2Z"
android:strokeWidth="4"
android:fillColor="#00000000"
android:strokeColor="#231F20"/>
<path
android:pathData="M39.32,40.11L21.3,16L48.92,31.82L50.7,38.48L37.81,45.58L42.71,56L29.13,49.58M30.85,49.42L40.91,54.18L37.06,45.99L30.85,49.42ZM48.34,32.91L32.77,47.36L49.71,38.03L48.34,32.91ZM24.48,18.83L39.95,39.52L47.84,32.2L24.48,18.83Z"
android:fillColor="#231F20"/>
<path
android:pathData="M39.32,40.11L21.3,16L48.92,31.82L50.7,38.48L37.81,45.58L42.71,56L29.13,49.58M30.85,49.42L40.91,54.18L37.06,45.99L30.85,49.42ZM48.34,32.91L32.77,47.36L49.71,38.03L48.34,32.91ZM24.48,18.83L39.95,39.52L47.84,32.2L24.48,18.83Z"
android:strokeWidth="0.8"
android:fillColor="#00000000"
android:strokeColor="#231F20"/>
<path
android:pathData="M96,2C77.22,2 62,17.22 62,36C62,54.78 77.22,70 96,70C114.78,70 130,54.78 130,36C130,17.22 114.78,2 96,2Z"
android:fillColor="#5925DC"/>
<path
android:pathData="M96,2C77.22,2 62,17.22 62,36C62,54.78 77.22,70 96,70C114.78,70 130,54.78 130,36C130,17.22 114.78,2 96,2Z"
android:strokeWidth="4"
android:fillColor="#00000000"
android:strokeColor="#231F20"/>
<path
android:pathData="M100.84,30.17C100.31,29.65 100.05,29.38 99.95,29.08C99.86,28.81 99.86,28.52 99.95,28.25C100.05,27.95 100.31,27.69 100.84,27.16L104.63,23.37C103.62,22.92 102.51,22.67 101.33,22.67C96.92,22.67 93.33,26.25 93.33,30.67C93.33,31.32 93.41,31.96 93.56,32.57C93.72,33.22 93.8,33.54 93.79,33.75C93.77,33.97 93.74,34.08 93.64,34.27C93.54,34.46 93.36,34.64 93,35L84.67,43.33C83.56,44.44 83.56,46.23 84.67,47.33C85.77,48.44 87.56,48.44 88.67,47.33L97,39C97.36,38.64 97.54,38.46 97.73,38.36C97.92,38.26 98.03,38.23 98.25,38.22C98.46,38.2 98.78,38.28 99.43,38.44C100.04,38.59 100.68,38.67 101.33,38.67C105.75,38.67 109.33,35.08 109.33,30.67C109.33,29.49 109.08,28.38 108.63,27.37L104.84,31.16C104.31,31.69 104.05,31.95 103.75,32.05C103.48,32.14 103.19,32.14 102.92,32.05C102.62,31.95 102.35,31.69 101.82,31.16L100.84,30.17Z"
android:strokeLineJoin="round"
android:strokeWidth="2.66667"
android:fillColor="#00000000"
android:strokeColor="#D9D6FE"
android:strokeLineCap="round"/>
</vector>

View File

@ -0,0 +1,43 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="132dp"
android:height="72dp"
android:viewportWidth="132"
android:viewportHeight="72">
<group>
<clip-path
android:pathData="M4,36C4,18.33 18.33,4 36,4C53.67,4 68,18.33 68,36C68,53.67 53.67,68 36,68C18.33,68 4,53.67 4,36Z"/>
<path
android:pathData="M36,4L36,4A32,32 0,0 1,68 36L68,36A32,32 0,0 1,36 68L36,68A32,32 0,0 1,4 36L4,36A32,32 0,0 1,36 4z"
android:fillColor="#ffffff"/>
<path
android:pathData="M4,36C4,18.35 18.35,4 36,4C53.65,4 68,18.35 68,36C68,53.65 53.65,68 36,68C18.35,68 4,53.65 4,36ZM47.41,21.15V26.02L33.87,44.39H47.41V50.85H38.68V56.2H33.32V50.85H24.59V45.98L38.12,27.61H24.59V21.15H33.32V15.78H38.68V21.15H47.41Z"
android:fillColor="#FCBB1A"
android:fillType="evenOdd"/>
<path
android:pathData="M4,36C4,18.33 18.33,4 36,4C53.67,4 68,18.33 68,36C68,53.67 53.67,68 36,68C18.33,68 4,53.67 4,36Z"
android:fillColor="#282622"/>
<path
android:pathData="M39.32,40.11L21.3,16L48.92,31.82L50.7,38.48L37.81,45.58L42.71,56L29.13,49.58M30.85,49.42L40.91,54.18L37.06,45.99L30.85,49.42ZM48.34,32.91L32.77,47.36L49.71,38.03L48.34,32.91ZM24.48,18.83L39.95,39.52L47.84,32.2L24.48,18.83Z"
android:fillColor="#ffffff"/>
</group>
<path
android:pathData="M36,2C17.22,2 2,17.22 2,36C2,54.78 17.22,70 36,70C54.78,70 70,54.78 70,36C70,17.22 54.78,2 36,2Z"
android:strokeWidth="4"
android:fillColor="#00000000"
android:strokeColor="#ffffff"/>
<path
android:pathData="M96,2C77.22,2 62,17.22 62,36C62,54.78 77.22,70 96,70C114.78,70 130,54.78 130,36C130,17.22 114.78,2 96,2Z"
android:fillColor="#BDB4FE"/>
<path
android:pathData="M96,2C77.22,2 62,17.22 62,36C62,54.78 77.22,70 96,70C114.78,70 130,54.78 130,36C130,17.22 114.78,2 96,2Z"
android:strokeWidth="4"
android:fillColor="#00000000"
android:strokeColor="#ffffff"/>
<path
android:pathData="M100.84,30.17C100.31,29.65 100.05,29.38 99.95,29.08C99.86,28.81 99.86,28.52 99.95,28.25C100.05,27.95 100.31,27.69 100.84,27.16L104.63,23.37C103.62,22.92 102.51,22.67 101.33,22.67C96.92,22.67 93.33,26.25 93.33,30.67C93.33,31.32 93.41,31.96 93.56,32.57C93.72,33.22 93.8,33.54 93.79,33.75C93.77,33.97 93.74,34.08 93.64,34.27C93.54,34.46 93.36,34.64 93,35L84.67,43.33C83.56,44.44 83.56,46.23 84.67,47.33C85.77,48.44 87.56,48.44 88.67,47.33L97,39C97.36,38.64 97.54,38.46 97.73,38.36C97.92,38.26 98.03,38.23 98.25,38.22C98.46,38.2 98.78,38.28 99.43,38.44C100.04,38.59 100.68,38.67 101.33,38.67C105.75,38.67 109.33,35.08 109.33,30.67C109.33,29.49 109.08,28.38 108.63,27.37L104.84,31.16C104.31,31.69 104.05,31.95 103.75,32.05C103.48,32.14 103.19,32.14 102.92,32.05C102.62,31.95 102.35,31.69 101.82,31.16L100.84,30.17Z"
android:strokeLineJoin="round"
android:strokeWidth="2.66667"
android:fillColor="#00000000"
android:strokeColor="#4A1FB8"
android:strokeLineCap="round"/>
</vector>

View File

@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="14dp"
android:height="14dp"
android:viewportWidth="14"
android:viewportHeight="14">
<path
android:pathData="M13,1L1,13M1,1L13,13"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#D2D1D2"
android:strokeLineCap="round"/>
</vector>

View File

@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="21dp"
android:height="20dp"
android:viewportWidth="21"
android:viewportHeight="20">
<path
android:pathData="M10.5,13.333V10M10.5,6.667H10.508M18.833,10C18.833,14.602 15.102,18.333 10.5,18.333C5.897,18.333 2.167,14.602 2.167,10C2.167,5.398 5.897,1.667 10.5,1.667C15.102,1.667 18.833,5.398 18.833,10Z"
android:strokeLineJoin="round"
android:strokeWidth="1.66667"
android:fillColor="#00000000"
android:strokeColor="#ffffff"
android:strokeLineCap="round"/>
</vector>

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="crash_reporting_opt_in_title">
Help Improve <xliff:g id="app_name" example="Zashi">%1$s</xliff:g>
</string>
<string name="crash_reporting_opt_in_subtitle">Crash reports are an essential tool for developers to improve the
quality and reliability of apps. They are very useful in practice to help with fixing bugs that cause
crashes.</string>
<string name="crash_reporting_opt_in_info">Note: The crash reports include information about app version, time
when the crash happened, and stack trace so it\s clear where in the app the crash happened. The data
collected is completely anonymized.</string>
<string name="crash_reporting_opt_in_positive">Opt in</string>
<string name="crash_reporting_opt_in_positive_desc">Share crash reports with Zashi developers.</string>
<string name="crash_reporting_opt_in_negative">Opt out</string>
<string name="crash_reporting_opt_in_negative_desc">Dont share crash reports with Zashi developers.</string>
<string name="crash_reporting_opt_in_save">Save changes</string>
</resources>

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="crash_reporting_opt_in_title">
Help Improve <xliff:g id="app_name" example="Zashi">%1$s</xliff:g>
</string>
<string name="crash_reporting_opt_in_subtitle">Crash reports are an essential tool for developers to improve the
quality and reliability of apps. They are very useful in practice to help with fixing bugs that cause
crashes.</string>
<string name="crash_reporting_opt_in_info">Note: The crash reports include information about app version, time
when the crash happened, and stack trace so it\s clear where in the app the crash happened. The data
collected is completely anonymized.</string>
<string name="crash_reporting_opt_in_positive">Opt in</string>
<string name="crash_reporting_opt_in_positive_desc">Share crash reports with Zashi developers.</string>
<string name="crash_reporting_opt_in_negative">Opt out</string>
<string name="crash_reporting_opt_in_negative_desc">Dont share crash reports with Zashi developers.</string>
<string name="crash_reporting_opt_in_save">Save changes</string>
</resources>

View File

@ -14,6 +14,4 @@
<string name="settings_troubleshooting_rescan">Volver a escanear la blockchain</string>
<string name="settings_troubleshooting_enable_background_sync">Sincronización en segundo plano</string>
<string name="settings_troubleshooting_enable_keep_screen_on">Mantener la pantalla encendida durante la sincronización</string>
<string name="settings_troubleshooting_enable_analytics">Reportar fallos</string>
</resources>

View File

@ -14,6 +14,4 @@
<string name="settings_troubleshooting_rescan">Rescan blockchain</string>
<string name="settings_troubleshooting_enable_background_sync">Background sync</string>
<string name="settings_troubleshooting_enable_keep_screen_on">Keep screen on during sync</string>
<string name="settings_troubleshooting_enable_analytics">Report crashes</string>
</resources>