zircles-android/app/src/main/java/cash/z/ecc/android/ZcashWalletApp.kt

95 lines
3.3 KiB
Kotlin

package cash.z.ecc.android
import android.app.Application
import android.content.Context
import android.os.Build
import androidx.camera.camera2.Camera2Config
import androidx.camera.core.CameraXConfig
import cash.z.ecc.android.di.component.AppComponent
import cash.z.ecc.android.di.component.DaggerAppComponent
import cash.z.ecc.android.feedback.FeedbackCoordinator
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 cash.z.ecc.android.sdk.ext.twig
import kotlinx.coroutines.*
import javax.inject.Inject
class ZcashWalletApp : Application(), CameraXConfig.Provider {
@Inject
lateinit var coordinator: FeedbackCoordinator
var creationTime: Long = 0
private set
var creationMeasured: Boolean = false
/**
* 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 onCreate() {
Thread.setDefaultUncaughtExceptionHandler(ExceptionReporter(Thread.getDefaultUncaughtExceptionHandler()))
creationTime = System.currentTimeMillis()
instance = this
// Setup handler for uncaught exceptions.
super.onCreate()
component = DaggerAppComponent.factory().create(this)
component.inject(this)
feedbackScope.launch {
coordinator.feedback.start()
}
}
override fun attachBaseContext(base: Context) {
super.attachBaseContext(base)
// MultiDex.install(this)
}
override fun getCameraXConfig(): CameraXConfig {
return Camera2Config.defaultConfig()
}
companion object {
lateinit var instance: ZcashWalletApp
lateinit var component: AppComponent
}
/**
* @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}")
// these are the only reported crashes that are considered fatal
coordinator.feedback.report(e, true)
coordinator.flush()
// can do this if necessary but first verify that we need it
runBlocking {
coordinator.await()
coordinator.feedback.stop()
}
ogHandler.uncaughtException(t, e)
Thread.sleep(2000L)
}
}
}
fun ZcashWalletApp.isEmulator(): Boolean {
val goldfish = Build.HARDWARE.contains("goldfish");
val emu = (System.getProperty("ro.kernel.qemu", "")?.length ?: 0) > 0;
val sdk = Build.MODEL.toLowerCase().contains("sdk")
return goldfish || emu || sdk;
}