[#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:
parent
7cc98528e9
commit
312a974b1c
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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())
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -6,7 +6,6 @@ import androidx.compose.runtime.Immutable
|
|||
data class SettingsTroubleshootingState(
|
||||
val backgroundSync: TroubleshootingItemState,
|
||||
val keepScreenOnDuringSync: TroubleshootingItemState,
|
||||
val analytics: TroubleshootingItemState,
|
||||
val rescan: TroubleshootingItemState,
|
||||
)
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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">Don’t share crash reports with Zashi developers.</string>
|
||||
<string name="crash_reporting_opt_in_save">Save changes</string>
|
||||
</resources>
|
|
@ -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">Don’t share crash reports with Zashi developers.</string>
|
||||
<string name="crash_reporting_opt_in_save">Save changes</string>
|
||||
</resources>
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue