Hush lite wallet for Android
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

117 lines
4.8 KiB

package cash.z.ecc.android
import android.app.Application
import android.content.Context
import androidx.camera.camera2.Camera2Config
import androidx.camera.core.CameraXConfig
import cash.z.ecc.android.di.DependenciesHolder
import cash.z.ecc.android.ext.tryWithWarning
import cash.z.ecc.android.feedback.FeedbackCoordinator
import cash.z.ecc.android.sdk.model.Zatoshi
import cash.z.ecc.android.sdk.model.ZcashNetwork
import cash.z.ecc.android.util.twig
import kotlinx.coroutines.*
class ZcashWalletApp : Application(), CameraXConfig.Provider {
private val coordinator: FeedbackCoordinator
get() = DependenciesHolder.feedbackCoordinator
lateinit var defaultNetwork: ZcashNetwork
var creationTime: Long = 0
private set
var creationMeasured: Boolean = false
/** The amount of transparent funds that need to accumulate before autoshielding is triggered */
val autoshieldThreshold: Long = Zatoshi.ZATOSHI_PER_ZEC // 1 ZEC
/**
* Intentionally private Scope for use with launching Feedback jobs. The feedback object has the
* longest scope in the app because it needs to be around early in order to measure launch times
* and stick around late in order to catch crashes. We intentionally don't expose this because
* application objects can have odd lifecycles, given that there is no clear onDestroy moment in
* many cases.
*/
private var feedbackScope: CoroutineScope = CoroutineScope(Dispatchers.Main + SupervisorJob())
override fun attachBaseContext(base: Context?) {
super.attachBaseContext(base)
// Setting a global reference to the application object is icky; we should try to refactor
// this away if possible. Doing this in attachBaseContext instead of onCreate()
// to avoid any lifecycle issues, as certain components can run before Application.onCreate()
// (like ContentProvider initialization), but attachBaseContext will still run before that.
instance = this
}
override fun onCreate() {
super.onCreate()
// Register this before the uncaught exception handler, because we want to make sure the
// exception handler also doesn't do disk IO. Since StrictMode only applies for debug builds,
// we'll also see the crashes during development right away and won't miss them if they aren't
// reported by the crash reporting.
if (BuildConfig.DEBUG) {
StrictModeHelper.enableStrictMode()
cash.z.ecc.android.sdk.internal.Twig.enabled(true)
cash.z.ecc.android.util.Twig.enabled(true)
}
// Setup handler for uncaught exceptions.
Thread.getDefaultUncaughtExceptionHandler()?.let {
Thread.setDefaultUncaughtExceptionHandler(ExceptionReporter(it))
}
creationTime = System.currentTimeMillis()
defaultNetwork = ZcashNetwork.from(resources.getInteger(R.integer.zcash_network_id))
feedbackScope.launch {
coordinator.feedback.start()
}
}
override fun getCameraXConfig(): CameraXConfig {
return Camera2Config.defaultConfig()
}
companion object {
lateinit var instance: ZcashWalletApp
}
/**
* @param feedbackCoordinator inject a provider so that if a crash happens before configuration
* is complete, we can lazily initialize all the feedback objects at this moment so that we
* don't have to add any time to startup.
*/
inner class ExceptionReporter(private val ogHandler: Thread.UncaughtExceptionHandler) :
Thread.UncaughtExceptionHandler {
override fun uncaughtException(t: Thread?, e: Throwable?) {
twig("Uncaught Exception: $e caused by: ${e?.cause}")
// Things can get pretty crazy during a fatal exception
// so be cautious here to avoid freezing the app
tryWithWarning("Unable to report fatal crash") {
// note: these are the only reported crashes that set isFatal=true
coordinator.feedback.report(e, true)
}
tryWithWarning("Unable to flush the feedback coordinator") {
coordinator.flush()
}
try {
// can do this if necessary but first verify that we need it
runBlocking {
coordinator.await()
coordinator.feedback.stop()
}
} catch (t: Throwable) {
twig("WARNING: failed to wait for the feedback observers to complete.")
} finally {
// it's important that this always runs so we use the finally clause here
// rather than another tryWithWarning block
ogHandler.uncaughtException(t, e)
Thread.sleep(2000L)
}
}
}
}