fix: Missing required view (#1)

* misc: Renamed `app` to `sample`

* misc: Renamed crashhandler to library

* fix: Missing required view
This commit is contained in:
Florian Bouillon 2022-08-09 18:09:30 +02:00 committed by GitHub
parent 7dfb454e72
commit ef361a4eaa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 233 additions and 21 deletions

View File

@ -1,6 +1,7 @@
package com.dzeio.crashhandler package com.dzeio.crashhandler
import android.app.Application import android.app.Application
import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences import android.content.SharedPreferences
import android.os.Build import android.os.Build
@ -17,13 +18,14 @@ import kotlin.system.exitProcess
* the Crash Handler class, you can get an instance by using it's [Builder] * the Crash Handler class, you can get an instance by using it's [Builder]
*/ */
class CrashHandler private constructor( class CrashHandler private constructor(
private val activity: Any, private val application: Application?,
private val activity: Class<*>,
private val prefs: SharedPreferences?, private val prefs: SharedPreferences?,
private val prefsKey: String?, private val prefsKey: String?,
@StringRes @StringRes
private val errorReporterCrashKey: Int?, private val errorReporterCrashKey: Int?,
private var prefix: String? = null, private val prefix: String? = null,
private var suffix: String? = null private val suffix: String? = null
) { ) {
private companion object { private companion object {
@ -34,10 +36,11 @@ class CrashHandler private constructor(
* Builder for the crash handler * Builder for the crash handler
*/ */
class Builder() { class Builder() {
private var application: Application? = null
private var prefs: SharedPreferences? = null private var prefs: SharedPreferences? = null
private var prefsKey: String? = null private var prefsKey: String? = null
private var errorReporterCrashKey: Int? = null private var errorReporterCrashKey: Int? = null
private var activity: Any? = ErrorActivity::class.java private var activity: Class<*>? = ErrorActivity::class.java
private var prefix: String? = null private var prefix: String? = null
private var suffix: String? = null private var suffix: String? = null
@ -48,7 +51,19 @@ class CrashHandler private constructor(
* *
* @param activity the activity class to use * @param activity the activity class to use
*/ */
fun withActivity(activity: Any): Builder { fun withContext(context: Context): Builder {
this.application = context.applicationContext as Application?
return this
}
/**
* Change the Crash activity to with your own
*
* note: you can get the backtrace text by using `intent.getStringExtra("error")`
*
* @param activity the activity class to use
*/
fun withActivity(activity: Class<*>): Builder {
this.activity = activity this.activity = activity
return this return this
} }
@ -114,7 +129,24 @@ class CrashHandler private constructor(
* build the Crash Handler * build the Crash Handler
*/ */
fun build(): CrashHandler { fun build(): CrashHandler {
return CrashHandler(activity!!, prefs, prefsKey, errorReporterCrashKey, prefix, suffix) return CrashHandler(application, activity!!, prefs, prefsKey, errorReporterCrashKey, prefix, suffix)
}
}
private var oldHandler: Thread.UncaughtExceptionHandler? = null
fun setup() {
if (application != null) {
this.setup(application)
}
}
/**
* Destroy the handler
*/
fun destroy() {
if (oldHandler != null) {
Thread.setDefaultUncaughtExceptionHandler(oldHandler)
} }
} }
@ -126,7 +158,7 @@ class CrashHandler private constructor(
*/ */
fun setup(application: Application) { fun setup(application: Application) {
// Application Error Handling // Application Error Handling
val oldHandler = Thread.getDefaultUncaughtExceptionHandler() oldHandler = Thread.getDefaultUncaughtExceptionHandler()
Thread.setDefaultUncaughtExceptionHandler { paramThread, paramThrowable -> Thread.setDefaultUncaughtExceptionHandler { paramThread, paramThrowable ->
// Log error to logcat if it wasn't done before has it can not be logged depending on the version // Log error to logcat if it wasn't done before has it can not be logged depending on the version
@ -199,13 +231,13 @@ class CrashHandler private constructor(
data += suffix ?: "" data += suffix ?: ""
Log.i(TAG, "Starting ${(activity as Class<*>).name}") Log.i(TAG, "Starting ${activity.name}")
// prepare the activity // prepare the activity
val intent = Intent(application.applicationContext, activity) val intent = Intent(application, activity)
// add flags so that it don't use the current Application context // add flags so that it don't use the current Application context
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
// add the Data String // add the Data String
intent.putExtra("error", data) intent.putExtra("error", data)

View File

@ -5,24 +5,26 @@ import android.content.ClipboardManager
import android.content.Context import android.content.Context
import android.os.Bundle import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.dzeio.crashhandler.databinding.ActivityErrorBinding import com.dzeio.crashhandler.databinding.CrashHandlerActivityErrorBinding
class ErrorActivity : AppCompatActivity() { class ErrorActivity : AppCompatActivity() {
private lateinit var binding: ActivityErrorBinding private lateinit var binding: CrashHandlerActivityErrorBinding
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = ActivityErrorBinding.inflate(layoutInflater) binding = CrashHandlerActivityErrorBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
val data = intent.getStringExtra("error") val data = intent.getStringExtra("error")
// put it in the textView // put it in the textView
binding.errorText.text = data binding.errorText.apply {
binding.errorText.setTextIsSelectable(true) text = data
setTextIsSelectable(true)
}
// Handle the Quit button // Handle the Quit button
binding.errorQuit.setOnClickListener { binding.errorQuit.setOnClickListener {

View File

@ -2,7 +2,8 @@
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
tools:context=".ui.ErrorActivity"
android:fitsSystemWindows="true"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">

View File

@ -39,7 +39,7 @@ android {
dependencies { dependencies {
implementation(project(":crashhandler")) implementation(project(":library"))
// Material Design // Material Design
implementation("com.google.android.material:material:1.6.1") implementation("com.google.android.material:material:1.6.1")

View File

@ -15,6 +15,10 @@
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name=".ui.ErrorActivity"
android:theme="@style/Theme.CrashHandler"
android:exported="false" />
</application> </application>
</manifest> </manifest>

View File

@ -1,14 +1,21 @@
package com.dzeio.crashhandlertest package com.dzeio.crashhandlertest
import com.dzeio.crashhandler.CrashHandler import com.dzeio.crashhandler.CrashHandler
import com.dzeio.crashhandlertest.ui.ErrorActivity
class Application : android.app.Application() { class Application : android.app.Application() {
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
CrashHandler.Builder() CrashHandler.Builder()
.withActivity(ErrorActivity::class.java)
.withContext(this)
.withPrefix("Pouet :D") .withPrefix("Pouet :D")
.withSuffix("WHYYYYY") .withSuffix("WHYYYYY")
.build().setup(this) .build().setup()
}
companion object {
const val TAG = "CrashHandlerTest"
} }
} }

View File

@ -0,0 +1,86 @@
package com.dzeio.crashhandlertest.ui
import android.content.ActivityNotFoundException
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Process
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.WindowCompat
import com.dzeio.crashhandlertest.Application
import com.dzeio.crashhandlertest.databinding.ActivityErrorBinding
import kotlin.system.exitProcess
class ErrorActivity : AppCompatActivity() {
companion object {
const val TAG = "${Application.TAG}/ErrorActivity"
}
private lateinit var binding: ActivityErrorBinding
override fun onCreate(savedInstanceState: Bundle?) {
WindowCompat.setDecorFitsSystemWindows(window, false)
super.onCreate(savedInstanceState)
binding = ActivityErrorBinding.inflate(layoutInflater)
setContentView(binding.root)
val data = intent.getStringExtra("error")
// Get Application datas
val deviceToReport = if (Build.DEVICE.contains(Build.MANUFACTURER)) Build.DEVICE else "${Build.MANUFACTURER} ${Build.DEVICE}"
val reportText = """
Crash Report (Thread: ${intent?.getLongExtra("threadId", -1) ?: "unknown"})
on $deviceToReport (${Build.MODEL}) running Android ${Build.VERSION.RELEASE} (${Build.VERSION.SDK_INT})
backtrace:
""".trimIndent() + data
// put it in the textView
binding.errorText.text = reportText
// Handle the Quit button
binding.errorQuit.setOnClickListener {
Process.killProcess(Process.myPid())
exitProcess(10)
}
// Handle the Email Button
binding.errorSubmitEmail.setOnClickListener {
// Create Intent
val intent = Intent(Intent.ACTION_SEND)
intent.data = Uri.parse("mailto:")
intent.type = "text/plain"
intent.putExtra(Intent.EXTRA_EMAIL, arrayOf("report.openhealth@dzeio.com"))
intent.putExtra(Intent.EXTRA_SUBJECT, "Error report for application crash")
intent.putExtra(Intent.EXTRA_TEXT, "Send Report Email\n$reportText")
try {
startActivity(Intent.createChooser(intent, "Send Report Email..."))
} catch (e: ActivityNotFoundException) {
Toast.makeText(this, "Not Email client found :(", Toast.LENGTH_LONG).show()
}
}
// Handle the GitHub Button
binding.errorSubmitGithub.setOnClickListener {
// Build URL
val url = "https://github.com/dzeiocom/OpenHealth/issues/new?title=Application Error&body=$reportText"
try {
startActivity(
Intent(Intent.ACTION_VIEW, Uri.parse(url))
)
} catch (e: ActivityNotFoundException) {
Toast.makeText(this, "No Web Browser found :(", Toast.LENGTH_LONG).show()
}
}
}
}

View File

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:fitsSystemWindows="true"
android:id="@+id/linearLayout2"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/textView3"
style="?textAppearanceHeadline5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="blablabla"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textView4"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="blablabla2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView3" />
<ScrollView
android:id="@+id/scrollView2"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
app:layout_constraintBottom_toTopOf="@+id/error_submit_email"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView4">
<TextView
android:id="@+id/error_text"
android:layout_width="match_parent"
style="?textAppearanceCaption"
android:layout_height="wrap_content" />
</ScrollView>
<Button
android:id="@+id/error_submit_github"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Github"
android:layout_marginStart="16dp"
app:layout_constraintBaseline_toBaselineOf="@+id/error_submit_email"
app:layout_constraintStart_toStartOf="parent" />
<Button
android:id="@+id/error_submit_email"
android:layout_marginBottom="16dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:text="E-Mail"
app:layout_constraintBottom_toTopOf="@+id/error_quit"
app:layout_constraintEnd_toEndOf="parent" />
<Button
android:id="@+id/error_quit"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:text="Quit"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

Before

Width:  |  Height:  |  Size: 982 B

After

Width:  |  Height:  |  Size: 982 B

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@ -15,5 +15,5 @@ dependencyResolutionManagement {
rootProject.name = "Crash Handler" rootProject.name = "Crash Handler"
include ':app' include ':sample'
include ':crashhandler' include ':library'