diff --git a/.github/scripts/gradlew_recursive.sh b/.github/scripts/gradlew_recursive.sh
deleted file mode 100644
index d575c49..0000000
--- a/.github/scripts/gradlew_recursive.sh
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/bin/bash
-
-# Copyright (C) 2020 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-set -xe
-
-# Default Gradle settings are not optimal for Android builds, override them
-# here to make the most out of the GitHub Actions build servers
-GRADLE_OPTS="$GRADLE_OPTS -Xms4g -Xmx4g"
-GRADLE_OPTS="$GRADLE_OPTS -XX:+HeapDumpOnOutOfMemoryError"
-GRADLE_OPTS="$GRADLE_OPTS -Dorg.gradle.daemon=false"
-GRADLE_OPTS="$GRADLE_OPTS -Dorg.gradle.workers.max=2"
-GRADLE_OPTS="$GRADLE_OPTS -Dkotlin.incremental=false"
-GRADLE_OPTS="$GRADLE_OPTS -Dkotlin.compiler.execution.strategy=in-process"
-GRADLE_OPTS="$GRADLE_OPTS -Dfile.encoding=UTF-8"
-export GRADLE_OPTS
-
-# Crawl all gradlew files which indicate an Android project
-# You may edit this if your repo has a different project structure
-for GRADLEW in `find . -name "gradlew"` ; do
- SAMPLE=$(dirname "${GRADLEW}")
- # Tell Gradle that this is a CI environment and disable parallel compilation
- bash "$GRADLEW" -p "$SAMPLE" -Pci --no-parallel --stacktrace $@
-done
-
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index ef1c8c4..068c5a5 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -27,16 +27,14 @@ jobs:
steps:
- uses: actions/checkout@v3
- - name: set up JDK 11
+ - name: set up JDK 17
uses: actions/setup-java@v3
with:
- java-version: '11'
- distribution: 'adopt'
+ java-version: '17'
+ distribution: 'temurin'
cache: gradle
- name: Build project
- run: |
- chmod +x .github/scripts/gradlew_recursive.sh
- .github/scripts/gradlew_recursive.sh assembleDebug
+ run: ./gradlew assembleDebug
- name: Zip artifacts
run: zip -r assemble.zip . -i '**/build/*.apk' '**/build/*.aab' '**/build/*.aar' '**/build/*.so'
- name: Upload artifacts
diff --git a/README.md b/README.md
index 5f95cdb..42370c2 100644
--- a/README.md
+++ b/README.md
@@ -24,7 +24,7 @@ Lightweight & customizable crash android crash handler library
Add to you dependencies (check the latest release for the version):
- (Gradle Kotlin DSL) Add `implementation("com.dzeio:crashhandler:1.0.2")`
-- (Gradle Groovy DSL) Add `implementation "com.dzeio:crashhandler:1.0.2" `
+- (Gradle Groovy DSL) Add `implementation "com.dzeio:crashhandler:1.0.2"`
## Usage
@@ -33,25 +33,32 @@ _note: full featured example in the `sample` app_
Create and add this to your Application.{kt,java}
```kotlin
+// create the Crash Handler
CrashHandler.Builder()
- // need the application context to run
- .withContext(this)
- // every other items below are optionnal
- // define a custom activity to use
- .withActivity(ErrorActivity::class.java)
+ // need the application context to run
+ .withContext(this)
+
+ // every other items below are optionnal
+ // define a custom activity to use
+ .withActivity(ErrorActivity::class.java)
- // define the preferenceManager to be able to handle crash in a custom Activity and to have the previous crash date in the logs
- .withPrefs(prefs)
- .withPrefsKey("com.dzeio.crashhandler.key")
+ // define the preferenceManager to have the previous crash date in the logs
+ .withPrefs(prefs)
+ .withPrefsKey("com.dzeio.crashhandler.key")
- // a Prefix to add at the beggining the crash message
- .withPrefix("Pouet :D")
+ // a Prefix to add at the beginning the crash message
+ .withPrefix("Prefix")
- // a Suffic to add at the end of the crash message
- .withSuffix("WHYYYYY")
+ // a Suffix to add at the end of the crash message
+ .withSuffix("Suffix")
+
+ // add a location where the crash logs are also exported (can be recovered as a zip ByteArray by calling {CrashHandler.getInstance().export()})
+ .withExportLocation(
+ File(this.getExternalFilesDir(null) ?: this.filesDir, "crash-logs")
+ )
- // build & start the module
- .build().setup()
+ // build & start the module
+ .build().setup()
```
## Build
diff --git a/build.gradle.kts b/build.gradle.kts
index 4394ff2..1a28748 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -4,7 +4,7 @@ buildscript {
mavenCentral()
}
dependencies {
- classpath("com.android.tools.build:gradle:7.4.2")
+ classpath("com.android.tools.build:gradle:8.1.1")
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
@@ -12,8 +12,8 @@ buildscript {
}
plugins {
- id("com.android.application") version "7.4.2" apply false
- id("com.android.library") version "7.4.2" apply false
+ id("com.android.application") version "8.1.1" apply false
+ id("com.android.library") version "8.1.1" apply false
id("org.jetbrains.kotlin.android") version "1.7.0" apply false
}
diff --git a/gradle.properties b/gradle.properties
index b81a080..d4699b7 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -4,17 +4,39 @@
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx1024m -XX:MaxPermSize=256m
-org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
+org.gradle.jvmargs=-Xmx2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
#
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
org.gradle.parallel=true
-#Tue Jul 19 18:42:00 CEST 2022
+
+# enable non transitive R Classes
android.nonTransitiveRClass=true
+
+# use official kotlin style
kotlin.code.style=official
+
+# use androidX
android.useAndroidX=true
+
+# Disable Jetifier
android.enableJetifier=false
-org.gradle.unsafe.configuration-cache=true
+
+# Disable configuration cache
+org.gradle.unsafe.configuration-cache=false
+
+# Enable Gradle Daemon
+org.gradle.daemon=true
+
+# Enable Configure on demand
+org.gradle.configureondemand=true
+
+# Enable gradle caching
org.gradle.caching=true
-org.gradle.configureondemand=true
\ No newline at end of file
+
+# Enabled Build config
+android.defaults.buildfeatures.buildconfig=true
+
+# BREAK EVERYTHING
+android.nonFinalResIds=false
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 4cbbcaa..fa344aa 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
#Sun Aug 07 22:38:24 CEST 2022
distributionBase=GRADLE_USER_HOME
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.1-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
diff --git a/gradlew b/gradlew
old mode 100644
new mode 100755
diff --git a/gradlew.bat b/gradlew.bat
old mode 100644
new mode 100755
diff --git a/library/build.gradle.kts b/library/build.gradle.kts
index 8ece8ad..71a4c7d 100644
--- a/library/build.gradle.kts
+++ b/library/build.gradle.kts
@@ -6,7 +6,7 @@ plugins {
val artifact = "crashhandler"
group = "com.dzeio"
-val projectVersion = project.findProperty("version") as String? ?: "1.0.0"
+val projectVersion = project.findProperty("version") as String? ?: "1.1.0"
version = projectVersion
publishing {
@@ -37,6 +37,7 @@ android {
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
+ buildConfigField("String", "VERSION", "\"$projectVersion\"")
}
testFixtures {
diff --git a/library/src/main/java/com/dzeio/crashhandler/CrashHandler.kt b/library/src/main/java/com/dzeio/crashhandler/CrashHandler.kt
index ab45f96..6a3c2e5 100644
--- a/library/src/main/java/com/dzeio/crashhandler/CrashHandler.kt
+++ b/library/src/main/java/com/dzeio/crashhandler/CrashHandler.kt
@@ -1,5 +1,6 @@
package com.dzeio.crashhandler
+import android.annotation.SuppressLint
import android.app.Application
import android.content.Context
import android.content.Intent
@@ -9,10 +10,16 @@ import android.os.Process
import android.util.Log
import android.widget.Toast
import androidx.annotation.StringRes
+import androidx.core.content.edit
import com.dzeio.crashhandler.CrashHandler.Builder
import com.dzeio.crashhandler.ui.ErrorActivity
+import com.dzeio.crashhandler.utils.ZipFile
+import java.io.File
+import java.io.IOException
+import java.lang.Exception
+import java.text.SimpleDateFormat
import java.util.Date
-import kotlin.system.exitProcess
+import java.util.TimeZone
/**
* the Crash Handler class, you can get an instance by using it's [Builder]
@@ -25,11 +32,24 @@ class CrashHandler private constructor(
@StringRes
private val errorReporterCrashKey: Int?,
private val prefix: String? = null,
- private val suffix: String? = null
+ private val suffix: String? = null,
+ private val exportFolder: File? = null
) {
- private companion object {
+ companion object {
private const val TAG = "CrashHandler"
+ private var instance: CrashHandler? = null
+
+ /**
+ * get the instance of the CrashHandler it will crash if it was not initialized previously
+ */
+ fun getInstance(): CrashHandler {
+ if (this.instance == null) {
+ throw Exception("can't get CrashHandler instance as its not initialized")
+ }
+
+ return this.instance!!
+ }
}
/**
@@ -43,6 +63,7 @@ class CrashHandler private constructor(
private var activity: Class<*>? = ErrorActivity::class.java
private var prefix: String? = null
private var suffix: String? = null
+ private var exportLocation: File? = null
/**
* Change the Crash activity to with your own
@@ -124,6 +145,16 @@ class CrashHandler private constructor(
return this
}
+ /**
+ * Add a crash log export folder
+ *
+ * @param exportLocation the folder in which you want to export crash logs, it will be created if it does not exists
+ */
+ fun withExportLocation(exportLocation: File): Builder {
+ this.exportLocation = exportLocation
+ return this
+ }
+
/**
* build the Crash Handler
*/
@@ -135,11 +166,16 @@ class CrashHandler private constructor(
prefsKey,
errorReporterCrashKey,
prefix,
- suffix
+ suffix,
+ exportLocation
)
}
}
+ init {
+ instance = this
+ }
+
private var oldHandler: Thread.UncaughtExceptionHandler? = null
fun setup() {
@@ -179,47 +215,15 @@ class CrashHandler private constructor(
// get current time an date
val now = Date().time
-
- // prepare to build debug string
- var data = "${application.getString(R.string.crash_handler_crash_report)}\n\n"
-
- data += prefix ?: ""
-
- // add device informations
- val deviceToReport =
- if (Build.DEVICE.contains(Build.MANUFACTURER)) {
- Build.DEVICE
- } else {
- "${Build.MANUFACTURER} ${Build.DEVICE}"
- }
-
- data += "\n\n${application.getString(
- R.string.crash_handler_hard_soft_infos,
- deviceToReport,
- Build.MODEL,
- Build.VERSION.RELEASE,
- Build.VERSION.SDK_INT
- )}"
-
- // add the current time to it
- data += "\n\n${application.getString(
- R.string.crash_handler_crash_happened,
- Date(now).toString()
- )}"
+ var previousCrash: Long? = null
// if lib as access to the preferences store
if (prefs != null && prefsKey != null) {
// get the last Crash
- val lastCrash = prefs.getLong(prefsKey, 0L)
-
- // then add it to the logs :D
- data += "\n${application.getString(
- R.string.crash_handler_previous_crash,
- Date(lastCrash).toString()
- )}"
+ previousCrash = prefs.getLong(prefsKey, 0L)
// if a crash already happened just before it means the Error Activity crashed lul
- if (lastCrash >= now - 1000) {
+ if (previousCrash >= now - 1000) {
// log it :D
Log.e(
TAG,
@@ -239,22 +243,23 @@ class CrashHandler private constructor(
}
// update the store
- prefs.edit().putLong(prefsKey, now).commit()
+ prefs.edit(true) { putLong(prefsKey, now) }
}
Log.i(TAG, "Collecting Error")
- // get Thread name and ID
- data += "\n\n${application.getString(
- R.string.crash_handler_thread_infos,
- paramThread.name,
- paramThread.id
- )}"
+ val data = this.buildData(
+ now,
+ previousCrash,
+ paramThread,
+ paramThrowable
+ )
- // print exception backtrace
- data += "\n\n${application.getString(R.string.crash_handler_error)}\n${paramThrowable.stackTraceToString()}\n\n"
-
- data += suffix ?: ""
+ try {
+ exportData(data, now)
+ } catch (e: IOException) {
+ Log.e(TAG, "Could not export the data to file", e)
+ }
Log.i(TAG, "Starting ${activity.name}")
@@ -278,7 +283,127 @@ class CrashHandler private constructor(
// Kill self
Process.killProcess(Process.myPid())
- exitProcess(10)
}
}
+
+ fun export(): ByteArray? {
+ if (exportFolder == null) {
+ return null
+ }
+ val output = ZipFile()
+ val files = exportFolder.listFiles()
+ for (file in files!!) {
+ output.addFile(file.name, file)
+ }
+ return output.toByteArray()
+ }
+
+ fun clearExports() {
+ if (exportFolder == null) {
+ return
+ }
+ val files = exportFolder.listFiles()
+ for (file in files!!) {
+ file.delete()
+ }
+ }
+
+ private fun exportData(data: String, now: Long) {
+ if (exportFolder == null) {
+ return
+ }
+ @SuppressLint("SimpleDateFormat")
+ val sdf = SimpleDateFormat("yyyy-MM-dd'T'HH-mm-ss.SSS")
+ sdf.timeZone = TimeZone.getTimeZone("CET")
+ val filename = sdf.format(Date(now))
+ if (!exportFolder.exists()) {
+ exportFolder.mkdirs()
+ }
+ if (!exportFolder.isDirectory) {
+ Log.e(
+ "CrashHandler",
+ "Cannot export the crash logs to a file due to the folder not being a folder"
+ )
+ return
+ }
+ val out = File(exportFolder, "$filename.log")
+ out.writeText(data, Charsets.UTF_8)
+ Log.d("CrashHandler", "Saving file to ${out.absolutePath}")
+ }
+
+ /**
+ * build the data text
+ * @param now the date as of right now
+ * @param previousCrash the previous crash date
+ * @param thread the thread that crashed
+ * @param throwable the exception thrown
+ *
+ * @return the string that contains a nicely formatted list of informations about the device
+ */
+ private fun buildData(
+ now: Long,
+ previousCrash: Long?,
+ thread: Thread,
+ throwable: Throwable
+ ): String {
+ val app = application
+
+ if (app == null) {
+ return "Could not build data because the library is missing the context"
+ }
+
+ // prepare to build debug string
+ var data = "${app.getString(R.string.crash_handler_crash_report)}\n\n"
+
+ // add the user submitted prefix
+ data += prefix ?: ""
+
+ // add device informations
+ val deviceToReport =
+ if (Build.DEVICE.contains(Build.MANUFACTURER)) {
+ Build.DEVICE
+ } else {
+ "${Build.MANUFACTURER} ${Build.DEVICE}"
+ }
+
+ // add the device informations
+ data += "\n\n${app.getString(
+ R.string.crash_handler_hard_soft_infos,
+ deviceToReport,
+ Build.MODEL,
+ Build.VERSION.RELEASE,
+ Build.VERSION.SDK_INT
+ )}"
+
+ // add the current time to it
+ data += "\n\n${app.getString(
+ R.string.crash_handler_crash_happened,
+ Date(now).toString()
+ )}"
+
+ // add the previous crash date if available
+ if (previousCrash != null) {
+ data += "\n${app.getString(
+ R.string.crash_handler_previous_crash,
+ Date(previousCrash).toString()
+ )}"
+ }
+
+ // get Thread name and ID
+ data += "\n\n${app.getString(
+ R.string.crash_handler_thread_infos,
+ thread.name,
+ thread.id
+ )}"
+
+ // print exception backtrace
+ data += "\n\n${app.getString(R.string.crash_handler_error)}\n${throwable.stackTraceToString()}\n\n"
+
+ data += "Generated by Dzeio Crash Handler Version ${BuildConfig.VERSION}\n\n"
+
+ // add the user submitted suffix
+ data += suffix ?: ""
+
+ return data
+ }
}
diff --git a/library/src/main/java/com/dzeio/crashhandler/utils/ZipFile.kt b/library/src/main/java/com/dzeio/crashhandler/utils/ZipFile.kt
new file mode 100644
index 0000000..2c94fef
--- /dev/null
+++ b/library/src/main/java/com/dzeio/crashhandler/utils/ZipFile.kt
@@ -0,0 +1,71 @@
+package com.dzeio.crashhandler.utils
+
+import java.io.BufferedOutputStream
+import java.io.ByteArrayOutputStream
+import java.io.File
+import java.io.IOException
+import java.util.zip.ZipEntry
+import java.util.zip.ZipOutputStream
+
+/**
+ * Simple Wrapper around the Java zip implementation to make it easier to use
+ */
+class ZipFile {
+
+ private val stream = ByteArrayOutputStream()
+ private val output = ZipOutputStream(BufferedOutputStream(stream))
+
+ /**
+ * add a file to the zip with the [content] to the specified [path]
+ *
+ * @param path the path in the Zip
+ * @param content the content as a String
+ */
+ fun addFile(path: String, content: String) = addFile(path, content.toByteArray())
+
+ /**
+ * add the [file] to the zip with at the specified [path]
+ *
+ * @param path the path in the Zip
+ * @param file the file to add to the zip
+ */
+ fun addFile(path: String, file: File) {
+ // Read file
+ val data = file.inputStream()
+ val bytes = data.readBytes()
+ data.close()
+
+ return addFile(path, bytes)
+ }
+
+ /**
+ * add the [content] to the zip with at the specified [path]
+ *
+ * @param path the path in the Zip
+ * @param content the content of the file to add to the zip
+ */
+ fun addFile(path: String, content: ByteArray) {
+ val entry = ZipEntry(path)
+ try {
+ output.putNextEntry(entry)
+
+ output.write(content)
+ } catch (e: IOException) {
+ e.printStackTrace()
+ }
+
+ output.closeEntry()
+ }
+
+ /**
+ * Export the Zip file to a ByteArray
+ *
+ * **note: You can't write to the ZipFile after running this function**
+ *
+ * @return the Zip File as a [ByteArray]
+ */
+ fun toByteArray(): ByteArray {
+ output.close()
+ return stream.toByteArray()
+ }
+}
diff --git a/sample/src/main/java/com/dzeio/crashhandlertest/Application.kt b/sample/src/main/java/com/dzeio/crashhandlertest/Application.kt
index b6f9c45..182baca 100644
--- a/sample/src/main/java/com/dzeio/crashhandlertest/Application.kt
+++ b/sample/src/main/java/com/dzeio/crashhandlertest/Application.kt
@@ -3,6 +3,7 @@ package com.dzeio.crashhandlertest
import androidx.preference.PreferenceManager
import com.dzeio.crashhandler.CrashHandler
import com.dzeio.crashhandlertest.ui.ErrorActivity
+import java.io.File
class Application : android.app.Application() {
override fun onCreate() {
@@ -15,17 +16,20 @@ class Application : android.app.Application() {
CrashHandler.Builder()
// need the application context to run
.withContext(this)
+ // every other items below are optionnal
// define a custom activity to use
.withActivity(ErrorActivity::class.java)
- // define the preferenceManager to be able to handle crash in a custom Activity and to have the previous crash date in the logs
+ // define the preferenceManager to have the previous crash date in the logs
.withPrefs(prefs)
.withPrefsKey("com.dzeio.crashhandler.key")
// a Prefix to add at the beginning the crash message
- .withPrefix(
- "Pokémon"
- )
+ .withPrefix("Prefix")
// a Suffix to add at the end of the crash message
.withSuffix("Suffix")
+ // add a location where the crash logs are also exported (can be recovered as a zip ByteArray by calling {CrashHandler.getInstance().export()})
+ .withExportLocation(
+ File(this.getExternalFilesDir(null) ?: this.filesDir, "crash-logs")
+ )
// build & start the module
.build().setup()
}
diff --git a/sample/src/main/java/com/dzeio/crashhandlertest/ui/MainActivity.kt b/sample/src/main/java/com/dzeio/crashhandlertest/ui/MainActivity.kt
index 3a30160..065cf92 100644
--- a/sample/src/main/java/com/dzeio/crashhandlertest/ui/MainActivity.kt
+++ b/sample/src/main/java/com/dzeio/crashhandlertest/ui/MainActivity.kt
@@ -1,16 +1,41 @@
package com.dzeio.crashhandlertest.ui
import android.os.Bundle
+import android.widget.Toast
+import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.WindowCompat
+import com.dzeio.crashhandler.CrashHandler
import com.dzeio.crashhandlertest.R
import com.dzeio.crashhandlertest.databinding.ActivityMainBinding
-/**
- *
- */
class MainActivity : AppCompatActivity() {
+ /**
+ * Response to the export button event
+ */
+ private val writeResult = registerForActivityResult(
+ ActivityResultContracts.CreateDocument("application/zip")
+ ) {
+
+ val zipFile = CrashHandler.getInstance().export()
+ if (zipFile == null) {
+ return@registerForActivityResult
+ }
+
+ // write file to location
+ this.contentResolver.openOutputStream(it!!)?.apply {
+ write(zipFile)
+ close()
+ }
+
+ Toast.makeText(
+ this,
+ R.string.export_complete,
+ Toast.LENGTH_LONG
+ ).show()
+ }
+
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
@@ -24,5 +49,15 @@ class MainActivity : AppCompatActivity() {
// DIE
throw Exception(getString(R.string.error_message))
}
+
+ binding.buttonExport.setOnClickListener {
+ // launch the popin to select where to save the file
+ writeResult.launch("output.zip")
+ }
+
+ binding.buttonClearExports.setOnClickListener {
+ // clear the handler exports
+ CrashHandler.getInstance().clearExports()
+ }
}
}
diff --git a/sample/src/main/res/layout/activity_main.xml b/sample/src/main/res/layout/activity_main.xml
index c4d5449..8df1670 100644
--- a/sample/src/main/res/layout/activity_main.xml
+++ b/sample/src/main/res/layout/activity_main.xml
@@ -8,14 +8,35 @@
tools:context=".ui.MainActivity"
android:padding="16dp">
-
+ app:layout_constraintTop_toTopOf="parent">
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sample/src/main/res/values-fr/strings.xml b/sample/src/main/res/values-fr/strings.xml
index 80af28a..96022ed 100644
--- a/sample/src/main/res/values-fr/strings.xml
+++ b/sample/src/main/res/values-fr/strings.xml
@@ -9,4 +9,7 @@
Aucun client E-Mail trouvé!Envoyer le rapport de crash par E-MailRapport d\'erreur pour le crash d\'application
+ Exporter les logs de crash sauvegardé
+ Logs de crash exporté!
+ Nettoyer les crashs sauvegardé
\ No newline at end of file
diff --git a/sample/src/main/res/values/strings.xml b/sample/src/main/res/values/strings.xml
index 833b5ac..79d6425 100644
--- a/sample/src/main/res/values/strings.xml
+++ b/sample/src/main/res/values/strings.xml
@@ -10,4 +10,7 @@
No E-Mail client found!Send a Report E-MailError report for the application crash
+ Export saved crash logs
+ Exported crash logs!
+ Cleanup saved crash logs
\ No newline at end of file