Explicitly disable all feedback, dev info and crash reporting.

Addresses https://github.com/zcash/zcash-android-wallet/issues/143 by placing everything behind a user setting that can be enabled in the future by users who want to continue helping us improve the user experience. For the most part, this will just be turned on for internal company releases in order to continue learning and improving the app.
This commit is contained in:
Kevin Gorham 2020-06-10 10:01:58 -04:00
parent 340fb8c993
commit de69567812
No known key found for this signature in database
GPG Key ID: CCA55602DF49FC38
14 changed files with 96 additions and 88 deletions

View File

@ -5,8 +5,7 @@ apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
apply plugin: 'com.google.gms.google-services'
apply plugin: 'io.fabric'
apply plugin: 'com.google.firebase.firebase-perf'
apply plugin: 'com.google.firebase.crashlytics'
//apply plugin: 'com.github.ben-manes.versions'
archivesBaseName = 'zcash-android-wallet'
@ -102,10 +101,6 @@ android {
}
}
crashlytics {
enableNdk true
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(':qrecycler')
@ -161,9 +156,6 @@ dependencies {
// Analytics (for dogfooding/crash-reporting/feedback only on internal team builds)
implementation Deps.Analytics.CRASHLYTICS
implementation Deps.Analytics.CRASHLYTICS_NDK
implementation Deps.Analytics.FIREBASE
implementation Deps.Analytics.FIREBASE_PERF
implementation Deps.Analytics.MIXPANEL
// Misc.

View File

@ -33,6 +33,8 @@
<!-- Firebase options -->
<meta-data android:name="com.google.firebase.ml.vision.DEPENDENCIES" android:value="barcode" />
<!-- All reporting is opt-in, only -->
<meta-data android:name="firebase_crashlytics_collection_enabled" android:value="false" />
<!-- Mixpanel options -->
<meta-data android:name="com.mixpanel.android.MPConfig.AutoShowMixpanelUpdates" android:value="false" />

View File

@ -37,7 +37,6 @@ class ZcashWalletApp : Application(), CameraXConfig.Provider {
override fun onCreate() {
Thread.setDefaultUncaughtExceptionHandler(ExceptionReporter(Thread.getDefaultUncaughtExceptionHandler()))
Twig.plant(TroubleshootingTwig())
creationTime = System.currentTimeMillis()
instance = this
// Setup handler for uncaught exceptions.

View File

@ -2,9 +2,14 @@ package cash.z.ecc.android.di.module
import android.content.ClipboardManager
import android.content.Context
import android.content.SharedPreferences
import cash.z.ecc.android.ZcashWalletApp
import cash.z.ecc.android.di.component.MainActivitySubcomponent
import cash.z.ecc.android.feedback.*
import cash.z.ecc.android.sdk.ext.SilentTwig
import cash.z.ecc.android.sdk.ext.TroubleshootingTwig
import cash.z.ecc.android.sdk.ext.Twig
import com.google.firebase.crashlytics.FirebaseCrashlytics
import dagger.Module
import dagger.Provides
import dagger.multibindings.IntoSet
@ -27,6 +32,11 @@ class AppModule {
// Feedback
//
@Provides
@Singleton
fun providePreferences(context: Context): SharedPreferences
= context.getSharedPreferences("Application", Context.MODE_PRIVATE)
@Provides
@Singleton
fun provideFeedback(): Feedback = Feedback()
@ -35,8 +45,16 @@ class AppModule {
@Singleton
fun provideFeedbackCoordinator(
feedback: Feedback,
preferences: SharedPreferences,
defaultObservers: Set<@JvmSuppressWildcards FeedbackCoordinator.FeedbackObserver>
): FeedbackCoordinator = FeedbackCoordinator(feedback, defaultObservers)
): FeedbackCoordinator {
return preferences.getBoolean(FeedbackCoordinator.ENABLED, false).let { isEnabled ->
// observe nothing unless feedback is enabled
FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(isEnabled)
Twig.plant(if (isEnabled) TroubleshootingTwig() else SilentTwig())
FeedbackCoordinator(feedback, if (isEnabled) defaultObservers else setOf())
}
}
//

View File

@ -1,6 +1,6 @@
package cash.z.ecc.android.feedback
import com.crashlytics.android.Crashlytics
import com.google.firebase.crashlytics.FirebaseCrashlytics
class FeedbackCrashlytics : FeedbackCoordinator.FeedbackObserver {
/**
@ -18,7 +18,7 @@ class FeedbackCrashlytics : FeedbackCoordinator.FeedbackObserver {
)
else -> null
}
exception?.let { Crashlytics.logException(it) }
exception?.let { FirebaseCrashlytics.getInstance().recordException(it) }
}
private class ReorgException(errorHeight: Int, rewindHeight: Int, reorgMesssage: String) :

View File

@ -63,6 +63,7 @@ object Report {
val errorHeight: Int by propertyMap
val rewindHeight: Int by propertyMap
}
class TxUpdateFailed(t: Throwable) : Feedback.AppError("txupdate", t, false)
}
}

View File

@ -29,73 +29,75 @@ class TransactionViewHolder<T : ConfirmedTransaction>(itemView: View) : Recycler
private val formatter = SimpleDateFormat("M/d h:mma", Locale.getDefault())
private val addressRegex = """zs\d\w{65,}""".toRegex()
fun bindTo(transaction: T?) = (itemView.context as MainActivity).lifecycleScope.launch {
// update view
var lineOne: String = ""
var lineTwo: String = ""
var amountZec: String = ""
var amountDisplay: String = ""
var amountColor: Int = R.color.text_light_dimmed
var lineOneColor: Int = R.color.text_light
var lineTwoColor: Int = R.color.text_light_dimmed
var indicatorBackground: Int = R.drawable.background_indicator_unknown
fun bindTo(transaction: T?) {
(itemView.context as MainActivity).lifecycleScope.launch {
// update view
var lineOne: String = ""
var lineTwo: String = ""
var amountZec: String = ""
var amountDisplay: String = ""
var amountColor: Int = R.color.text_light_dimmed
var lineOneColor: Int = R.color.text_light
var lineTwoColor: Int = R.color.text_light_dimmed
var indicatorBackground: Int = R.drawable.background_indicator_unknown
transaction?.apply {
itemView.setOnClickListener {
onTransactionClicked(this)
}
itemView.setOnLongClickListener {
onTransactionLongPressed(this)
true
}
amountZec = value.convertZatoshiToZecString()
// TODO: these might be good extension functions
val timestamp = formatter.format(blockTimeInSeconds * 1000L)
val isMined = blockTimeInSeconds != 0L
when {
!toAddress.isNullOrEmpty() -> {
lineOne = "You paid ${toAddress?.toAbbreviatedAddress()}"
lineTwo = if (isMined) "Sent $timestamp" else "Pending confirmation"
amountDisplay = "- $amountZec"
if (isMined) {
amountColor = R.color.zcashRed
indicatorBackground = R.drawable.background_indicator_outbound
} else {
lineOneColor = R.color.text_light_dimmed
lineTwoColor = R.color.text_light
transaction?.apply {
itemView.setOnClickListener {
onTransactionClicked(this)
}
itemView.setOnLongClickListener {
onTransactionLongPressed(this)
true
}
amountZec = value.convertZatoshiToZecString()
// TODO: these might be good extension functions
val timestamp = formatter.format(blockTimeInSeconds * 1000L)
val isMined = blockTimeInSeconds != 0L
when {
!toAddress.isNullOrEmpty() -> {
lineOne = "You paid ${toAddress?.toAbbreviatedAddress()}"
lineTwo = if (isMined) "Sent $timestamp" else "Pending confirmation"
amountDisplay = "- $amountZec"
if (isMined) {
amountColor = R.color.zcashRed
indicatorBackground = R.drawable.background_indicator_outbound
} else {
lineOneColor = R.color.text_light_dimmed
lineTwoColor = R.color.text_light
}
}
toAddress.isNullOrEmpty() && value > 0L && minedHeight > 0 -> {
lineOne = getSender(transaction)
lineTwo = "Received $timestamp"
amountDisplay = "+ $amountZec"
amountColor = R.color.zcashGreen
indicatorBackground = R.drawable.background_indicator_inbound
}
else -> {
lineOne = "Unknown"
lineTwo = "Unknown"
amountDisplay = "$amountZec"
amountColor = R.color.text_light
}
}
toAddress.isNullOrEmpty() && value > 0L && minedHeight > 0 -> {
lineOne = getSender(transaction)
lineTwo = "Received $timestamp"
amountDisplay = "+ $amountZec"
amountColor = R.color.zcashGreen
indicatorBackground = R.drawable.background_indicator_inbound
}
else -> {
lineOne = "Unknown"
lineTwo = "Unknown"
amountDisplay = "$amountZec"
amountColor = R.color.text_light
// sanitize amount
if (value < ZcashSdk.MINERS_FEE_ZATOSHI) amountDisplay = "< 0.001"
else if (amountZec.length > 10) { // 10 allows 3 digits to the left and 6 to the right of the decimal
amountDisplay = "tap to view"
}
}
// sanitize amount
if (value < ZcashSdk.MINERS_FEE_ZATOSHI) amountDisplay = "< 0.001"
else if (amountZec.length > 10) { // 10 allows 3 digits to the left and 6 to the right of the decimal
amountDisplay = "tap to view"
}
topText.text = lineOne
bottomText.text = lineTwo
amountText.text = amountDisplay
amountText.setTextColor(amountColor.toAppColor())
topText.setTextColor(lineOneColor.toAppColor())
bottomText.setTextColor(lineTwoColor.toAppColor())
val context = itemView.context
indicator.background = context.resources.getDrawable(indicatorBackground)
shieldIcon.goneIf((transaction?.raw != null || transaction?.expiryHeight != null) && !transaction?.toAddress.isShielded())
}
topText.text = lineOne
bottomText.text = lineTwo
amountText.text = amountDisplay
amountText.setTextColor(amountColor.toAppColor())
topText.setTextColor(lineOneColor.toAppColor())
bottomText.setTextColor(lineTwoColor.toAppColor())
val context = itemView.context
indicator.background = context.resources.getDrawable(indicatorBackground)
shieldIcon.goneIf((transaction?.raw != null || transaction?.expiryHeight != null) && !transaction?.toAddress.isShielded())
}
private suspend fun getSender(transaction: ConfirmedTransaction): String {

View File

@ -21,8 +21,8 @@ import cash.z.ecc.android.feedback.Report.Tap.SCAN_RECEIVE
import cash.z.ecc.android.sdk.ext.twig
import cash.z.ecc.android.ui.base.BaseFragment
import cash.z.ecc.android.ui.send.SendViewModel
import com.crashlytics.android.Crashlytics
import com.google.common.util.concurrent.ListenableFuture
import com.google.firebase.crashlytics.FirebaseCrashlytics
import kotlinx.coroutines.launch
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
@ -102,7 +102,7 @@ class ScanFragment : BaseFragment<FragmentScanBinding>() {
preview.setSurfaceProvider(binding.preview.createSurfaceProvider())
} catch (t: Throwable) {
// TODO: consider bubbling this up to the user
Crashlytics.logException(t)
mainActivity?.feedback?.report(t)
twig("Error while opening the camera: $t")
}

View File

@ -16,7 +16,6 @@ import cash.z.ecc.android.sdk.db.entity.*
import cash.z.ecc.android.sdk.ext.convertZatoshiToZecString
import cash.z.ecc.android.sdk.ext.toAbbreviatedAddress
import cash.z.ecc.android.sdk.ext.twig
import com.crashlytics.android.Crashlytics
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.launchIn
@ -112,8 +111,8 @@ class SendFinalFragment : BaseFragment<FragmentSendFinalBinding>() {
} catch(t: Throwable) {
val message = "ERROR: error while handling pending transaction update! $t"
twig(message)
Crashlytics.log(message)
Crashlytics.logException(t)
mainActivity?.feedback?.report(Report.Error.NonFatal.TxUpdateFailed(t))
mainActivity?.feedback?.report(t)
}
}

View File

@ -21,7 +21,6 @@ import cash.z.ecc.android.sdk.ext.ZcashSdk
import cash.z.ecc.android.sdk.ext.convertZatoshiToZecString
import cash.z.ecc.android.sdk.ext.twig
import cash.z.ecc.android.sdk.validate.AddressType
import com.crashlytics.android.Crashlytics
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
@ -144,7 +143,7 @@ class SendViewModel @Inject constructor() : ViewModel() {
report(metricId)
}
} catch (t: Throwable) {
Crashlytics.logException(RuntimeException("Error while updating Metrics", t))
feedback.report(t)
}
}

View File

@ -10,11 +10,9 @@ import android.widget.TextView
import android.widget.Toast
import androidx.activity.addCallback
import androidx.lifecycle.lifecycleScope
import cash.z.ecc.android.R
import cash.z.ecc.android.ZcashWalletApp
import cash.z.ecc.android.databinding.FragmentBackupBinding
import cash.z.ecc.android.di.viewmodel.activityViewModel
import cash.z.ecc.android.di.viewmodel.viewModel
import cash.z.ecc.android.feedback.Report
import cash.z.ecc.android.feedback.Report.MetricType.SEED_PHRASE_LOADED
import cash.z.ecc.android.feedback.Report.Tap.BACKUP_DONE

View File

@ -13,7 +13,7 @@ buildscript {
classpath 'com.google.gms:google-services:4.3.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${Deps.kotlinVersion}"
classpath 'io.fabric.tools:gradle:1.31.2'
classpath 'com.google.firebase:perf-plugin:1.3.1'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.1.1'
}
}

View File

@ -61,10 +61,7 @@ object Deps {
val STUB = "io.grpc:grpc-stub:$version"
}
object Analytics { // for dogfooding/crash-reporting/feedback only on internal team builds
val CRASHLYTICS = "com.crashlytics.sdk.android:crashlytics:2.10.1"
val CRASHLYTICS_NDK = "com.crashlytics.sdk.android:crashlytics-ndk:2.1.1"
val FIREBASE = "com.google.firebase:firebase-analytics:17.4.3"
val FIREBASE_PERF = "com.google.firebase:firebase-perf:19.0.7"
val CRASHLYTICS = "com.google.firebase:firebase-crashlytics:17.0.1"
val MIXPANEL = "com.mixpanel.android:mixpanel-android:5.6.3"
}
object JavaX {

View File

@ -125,6 +125,7 @@ class FeedbackCoordinator(val feedback: Feedback, defaultObservers: Set<Feedback
}
companion object {
const val ENABLED = "setting.feedbackcoordinater.enabled"
private val mutex: Mutex = Mutex()
}
}