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.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.
 

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)
}
}
}
}