Compare commits
No commits in common. "master" and "1.0.0" have entirely different histories.
3
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -1,3 +0,0 @@
|
|||||||
<!--
|
|
||||||
Thanks for your Pull Request, Please provide the related Issue using "Fix #0" or describe the change(s) you made.
|
|
||||||
-->
|
|
45
.github/dependabot.yml
vendored
@ -1,45 +0,0 @@
|
|||||||
# To get started with Dependabot version updates, you'll need to specify which
|
|
||||||
# package ecosystems to update and where the package manifests are located.
|
|
||||||
# Please see the documentation for all configuration options:
|
|
||||||
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
|
||||||
|
|
||||||
version: 2
|
|
||||||
updates:
|
|
||||||
- package-ecosystem: gradle
|
|
||||||
directory: "/"
|
|
||||||
schedule:
|
|
||||||
interval: daily
|
|
||||||
ignore:
|
|
||||||
- dependency-name: "*"
|
|
||||||
update-types: ["version-update:semver-minor", "version-update:semver-patch"]
|
|
||||||
|
|
||||||
- package-ecosystem: gradle
|
|
||||||
directory: "/library"
|
|
||||||
schedule:
|
|
||||||
interval: daily
|
|
||||||
commit-message:
|
|
||||||
prefix: build
|
|
||||||
ignore:
|
|
||||||
- dependency-name: "*"
|
|
||||||
update-types: ["version-update:semver-minor", "version-update:semver-patch"]
|
|
||||||
|
|
||||||
|
|
||||||
- package-ecosystem: gradle
|
|
||||||
directory: "/sample"
|
|
||||||
schedule:
|
|
||||||
interval: daily
|
|
||||||
commit-message:
|
|
||||||
prefix: build
|
|
||||||
ignore:
|
|
||||||
- dependency-name: "*"
|
|
||||||
update-types: ["version-update:semver-minor", "version-update:semver-patch"]
|
|
||||||
|
|
||||||
- package-ecosystem: "github-actions"
|
|
||||||
directory: "/"
|
|
||||||
schedule:
|
|
||||||
interval: "daily"
|
|
||||||
commit-message:
|
|
||||||
prefix: build
|
|
||||||
ignore:
|
|
||||||
- dependency-name: "*"
|
|
||||||
update-types: ["version-update:semver-minor", "version-update:semver-patch"]
|
|
50
.github/workflows/build.yml
vendored
@ -1,50 +0,0 @@
|
|||||||
# 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.
|
|
||||||
|
|
||||||
name: Android CI
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ master ]
|
|
||||||
pull_request:
|
|
||||||
branches: [ master ]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- name: set up JDK 17
|
|
||||||
uses: actions/setup-java@v3
|
|
||||||
with:
|
|
||||||
java-version: '17'
|
|
||||||
distribution: 'temurin'
|
|
||||||
cache: gradle
|
|
||||||
- name: Build project
|
|
||||||
run: ./gradlew assembleDebug
|
|
||||||
- name: Zip artifacts
|
|
||||||
run: zip -r assemble.zip . -i '**/build/*.apk' '**/build/*.aab' '**/build/*.aar' '**/build/*.so'
|
|
||||||
- name: Upload artifacts
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: assemble
|
|
||||||
path: assemble.zip
|
|
||||||
- name: ktlint
|
|
||||||
uses: ScaCap/action-ktlint@master
|
|
||||||
with:
|
|
||||||
github_token: ${{ secrets.github_token }}
|
|
||||||
android: true
|
|
||||||
reporter: github-pr-review # Change reporter
|
|
55
README.md
@ -1,19 +1,3 @@
|
|||||||
<p align="center">
|
|
||||||
<img alt="Dzeio Charts logo" width="30%" src="sample/src/main/ic_launcher-playstore.png">
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p align="center">
|
|
||||||
<a href="https://discord.gg/d3QeWKBmBD">
|
|
||||||
<img src="https://img.shields.io/discord/1143555541004726272?color=%235865F2&label=Discord" alt="Discord Link">
|
|
||||||
</a>
|
|
||||||
<a href="https://github.com/dzeiocom/crashhandler/stargazers">
|
|
||||||
<img src="https://img.shields.io/github/stars/dzeiocom/crashhandler?style=flat-square" alt="Github stars">
|
|
||||||
</a>
|
|
||||||
<a href="https://github.com/dzeiocom/crashhandler/actions/workflows/build.yml">
|
|
||||||
<img src="https://img.shields.io/github/actions/workflow/status/dzeiocom/crashhandler/build.yml?style=flat-square" alt="Build passing" />
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
# Crash Handler
|
# Crash Handler
|
||||||
|
|
||||||
Lightweight & customizable crash android crash handler library
|
Lightweight & customizable crash android crash handler library
|
||||||
@ -23,43 +7,8 @@ Lightweight & customizable crash android crash handler library
|
|||||||
- Add Jitpack.io to your `settings.gradle` file `maven { url 'https://jitpack.io' }`
|
- Add Jitpack.io to your `settings.gradle` file `maven { url 'https://jitpack.io' }`
|
||||||
|
|
||||||
Add to you dependencies (check the latest release for the version):
|
Add to you dependencies (check the latest release for the version):
|
||||||
- (Gradle Kotlin DSL) Add `implementation("com.dzeio:crashhandler:1.0.2")`
|
- (Gradle Kotlin DSL) Add `implementation("com.dzeio:crashhandler:1.0.0")`
|
||||||
- (Gradle Groovy DSL) Add `implementation "com.dzeio:crashhandler:1.0.2"`
|
- (Gradle Groovy DSL) Add `implementation "com.dzeio:crashhandler:1.0.0" `
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
_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)
|
|
||||||
|
|
||||||
// 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("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()
|
|
||||||
```
|
|
||||||
|
|
||||||
## Build
|
## Build
|
||||||
|
|
||||||
|
0
library/.gitignore → app/.gitignore
vendored
@ -20,10 +20,7 @@ android {
|
|||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
isMinifyEnabled = true
|
isMinifyEnabled = true
|
||||||
proguardFiles(
|
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
|
||||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
|
||||||
"proguard-rules.pro"
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,7 +31,7 @@ android {
|
|||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
jvmTarget = "11"
|
jvmTarget = "11"
|
||||||
}
|
}
|
||||||
|
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
viewBinding = true
|
viewBinding = true
|
||||||
}
|
}
|
||||||
@ -42,14 +39,11 @@ android {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
implementation(project(":library"))
|
implementation(project(":crashhandler"))
|
||||||
|
|
||||||
// Material Design
|
// Material Design
|
||||||
implementation("com.google.android.material:material:1.8.0")
|
implementation("com.google.android.material:material:1.6.1")
|
||||||
|
|
||||||
// Navigation because I don't want to maintain basic transactions and shit
|
// Navigation because I don't want to maintain basic transactions and shit
|
||||||
implementation("androidx.navigation:navigation-fragment-ktx:2.5.3")
|
implementation("androidx.navigation:navigation-fragment-ktx:2.5.1")
|
||||||
|
}
|
||||||
// preferences
|
|
||||||
implementation("androidx.preference:preference-ktx:1.2.0")
|
|
||||||
}
|
|
@ -15,10 +15,6 @@
|
|||||||
<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>
|
14
app/src/main/java/com/dzeio/crashhandlertest/Application.kt
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package com.dzeio.crashhandlertest
|
||||||
|
|
||||||
|
import com.dzeio.crashhandler.CrashHandler
|
||||||
|
|
||||||
|
class Application : android.app.Application() {
|
||||||
|
override fun onCreate() {
|
||||||
|
super.onCreate()
|
||||||
|
|
||||||
|
CrashHandler.Builder()
|
||||||
|
.withPrefix("Pouet :D")
|
||||||
|
.withSuffix("WHYYYYY")
|
||||||
|
.build().setup(this)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package com.dzeio.crashhandlertest.ui
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.core.view.WindowCompat
|
||||||
|
import com.dzeio.crashhandlertest.databinding.ActivityMainBinding
|
||||||
|
|
||||||
|
class MainActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
private lateinit var binding: ActivityMainBinding
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
package com.dzeio.crashhandlertest.ui
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import com.dzeio.crashhandlertest.databinding.FragmentMainBinding
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple [Fragment] subclass as the default destination in the navigation.
|
||||||
|
*/
|
||||||
|
class MainFragment : Fragment() {
|
||||||
|
|
||||||
|
private var _binding: FragmentMainBinding? = null
|
||||||
|
|
||||||
|
// This property is only valid between onCreateView and
|
||||||
|
// onDestroyView.
|
||||||
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater, container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View? {
|
||||||
|
|
||||||
|
_binding = FragmentMainBinding.inflate(inflater, container, false)
|
||||||
|
return binding.root
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
binding.buttonFirst.setOnClickListener {
|
||||||
|
// DIE
|
||||||
|
throw Exception("POKÉMON :D")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
_binding = null
|
||||||
|
}
|
||||||
|
}
|
30
app/src/main/res/drawable-v24/ic_launcher_foreground.xml
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:aapt="http://schemas.android.com/aapt"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportWidth="108"
|
||||||
|
android:viewportHeight="108">
|
||||||
|
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient
|
||||||
|
android:endX="85.84757"
|
||||||
|
android:endY="92.4963"
|
||||||
|
android:startX="42.9492"
|
||||||
|
android:startY="49.59793"
|
||||||
|
android:type="linear">
|
||||||
|
<item
|
||||||
|
android:color="#44000000"
|
||||||
|
android:offset="0.0" />
|
||||||
|
<item
|
||||||
|
android:color="#00000000"
|
||||||
|
android:offset="1.0" />
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFF"
|
||||||
|
android:fillType="nonZero"
|
||||||
|
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:strokeColor="#00000000" />
|
||||||
|
</vector>
|
170
app/src/main/res/drawable/ic_launcher_background.xml
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportWidth="108"
|
||||||
|
android:viewportHeight="108">
|
||||||
|
<path
|
||||||
|
android:fillColor="#3DDC84"
|
||||||
|
android:pathData="M0,0h108v108h-108z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M9,0L9,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,0L19,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M29,0L29,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M39,0L39,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M49,0L49,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M59,0L59,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M69,0L69,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M79,0L79,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M89,0L89,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M99,0L99,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,9L108,9"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,19L108,19"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,29L108,29"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,39L108,39"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,49L108,49"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,59L108,59"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,69L108,69"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,79L108,79"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,89L108,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,99L108,99"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,29L89,29"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,39L89,39"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,49L89,49"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,59L89,59"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,69L89,69"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,79L89,79"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M29,19L29,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M39,19L39,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M49,19L49,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M59,19L59,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M69,19L69,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M79,19L79,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
</vector>
|
23
app/src/main/res/layout/activity_main.xml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.coordinatorlayout.widget.CoordinatorLayout 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:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
|
tools:context=".ui.MainActivity">
|
||||||
|
|
||||||
|
<androidx.fragment.app.FragmentContainerView
|
||||||
|
android:id="@+id/fragment"
|
||||||
|
android:name="androidx.navigation.fragment.NavHostFragment"
|
||||||
|
android:layout_marginTop="?attr/actionBarSize"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:defaultNavHost="true"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:navGraph="@navigation/nav_graph" />
|
||||||
|
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
21
app/src/main/res/layout/fragment_main.xml
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?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:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:context=".ui.MainFragment"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/button_first"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/crash_app"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
/>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -1,6 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<background android:drawable="@color/ic_launcher_background"/>
|
<background android:drawable="@drawable/ic_launcher_background" />
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||||
<monochrome android:drawable="@drawable/ic_launcher_foreground"/>
|
|
||||||
</adaptive-icon>
|
</adaptive-icon>
|
BIN
app/src/main/res/mipmap-hdpi/ic_launcher.webp
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher.webp
Normal file
After Width: | Height: | Size: 982 B |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher.webp
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
Normal file
After Width: | Height: | Size: 3.8 KiB |
13
app/src/main/res/navigation/nav_graph.xml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<navigation 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:id="@+id/nav_graph"
|
||||||
|
app:startDestination="@id/MainFragment">
|
||||||
|
|
||||||
|
<fragment
|
||||||
|
android:id="@+id/MainFragment"
|
||||||
|
android:name="com.dzeio.crashhandlertest.ui.MainFragment"
|
||||||
|
android:label="@string/first_fragment_label"
|
||||||
|
tools:layout="@layout/fragment_main"/>
|
||||||
|
</navigation>
|
9
app/src/main/res/values/strings.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<resources>
|
||||||
|
<string name="app_name" translatable="false">Crash Handler</string>
|
||||||
|
<string name="title_activity_main" translatable="false">MainActivity</string>
|
||||||
|
<!-- Strings used for fragments for navigation -->
|
||||||
|
<string name="first_fragment_label" translatable="false">First Fragment</string>
|
||||||
|
<string name="second_fragment_label" translatable="false">Second Fragment</string>
|
||||||
|
|
||||||
|
<string name="crash_app" translatable="false">Crash app</string>
|
||||||
|
</resources>
|
@ -4,7 +4,7 @@ buildscript {
|
|||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath("com.android.tools.build:gradle:8.1.1")
|
classpath("com.android.tools.build:gradle:7.2.2")
|
||||||
|
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
// in the individual module build.gradle files
|
// in the individual module build.gradle files
|
||||||
@ -12,8 +12,8 @@ buildscript {
|
|||||||
}
|
}
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("com.android.application") version "8.1.1" apply false
|
id("com.android.application") version "7.2.2" apply false
|
||||||
id("com.android.library") version "8.1.1" apply false
|
id("com.android.library") version "7.2.2" apply false
|
||||||
id("org.jetbrains.kotlin.android") version "1.7.0" apply false
|
id("org.jetbrains.kotlin.android") version "1.7.0" apply false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ plugins {
|
|||||||
|
|
||||||
val artifact = "crashhandler"
|
val artifact = "crashhandler"
|
||||||
group = "com.dzeio"
|
group = "com.dzeio"
|
||||||
val projectVersion = project.findProperty("version") as String? ?: "1.1.0"
|
val projectVersion = project.findProperty("version") as String? ?: "1.0.0"
|
||||||
version = projectVersion
|
version = projectVersion
|
||||||
|
|
||||||
publishing {
|
publishing {
|
||||||
@ -24,7 +24,7 @@ publishing {
|
|||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "$group.$artifact"
|
namespace = "com.dzeio.crashhandler"
|
||||||
compileSdk = 33
|
compileSdk = 33
|
||||||
buildToolsVersion = "33.0.0"
|
buildToolsVersion = "33.0.0"
|
||||||
|
|
||||||
@ -35,9 +35,9 @@ android {
|
|||||||
minCompileSdk = 21
|
minCompileSdk = 21
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
consumerProguardFiles("consumer-rules.pro")
|
consumerProguardFiles("consumer-rules.pro")
|
||||||
buildConfigField("String", "VERSION", "\"$projectVersion\"")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
testFixtures {
|
testFixtures {
|
||||||
@ -77,5 +77,5 @@ android {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
// Necessary for the Activity (well to make it pretty :D)
|
// Necessary for the Activity (well to make it pretty :D)
|
||||||
implementation("com.google.android.material:material:1.8.0")
|
implementation("com.google.android.material:material:1.6.1")
|
||||||
}
|
}
|
@ -0,0 +1,224 @@
|
|||||||
|
package com.dzeio.crashhandler
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Process
|
||||||
|
import android.util.Log
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import com.dzeio.crashhandler.CrashHandler.Builder
|
||||||
|
import com.dzeio.crashhandler.ui.ErrorActivity
|
||||||
|
import java.util.Date
|
||||||
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the Crash Handler class, you can get an instance by using it's [Builder]
|
||||||
|
*/
|
||||||
|
class CrashHandler private constructor(
|
||||||
|
private val activity: Any,
|
||||||
|
private val prefs: SharedPreferences?,
|
||||||
|
private val prefsKey: String?,
|
||||||
|
@StringRes
|
||||||
|
private val errorReporterCrashKey: Int?,
|
||||||
|
private var prefix: String? = null,
|
||||||
|
private var suffix: String? = null
|
||||||
|
) {
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
private const val TAG = "CrashHandler"
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builder for the crash handler
|
||||||
|
*/
|
||||||
|
class Builder() {
|
||||||
|
private var prefs: SharedPreferences? = null
|
||||||
|
private var prefsKey: String? = null
|
||||||
|
private var errorReporterCrashKey: Int? = null
|
||||||
|
private var activity: Any? = ErrorActivity::class.java
|
||||||
|
private var prefix: String? = null
|
||||||
|
private var suffix: String? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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: Any): Builder {
|
||||||
|
this.activity = activity
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [SharedPreferences] of your app to be able to handle ErrorActivity crashes
|
||||||
|
*
|
||||||
|
* note: you also need to use [withPrefsKey]
|
||||||
|
*
|
||||||
|
* @param prefs instance of [SharedPreferences] to use
|
||||||
|
*/
|
||||||
|
fun withPrefs(prefs: SharedPreferences?): Builder {
|
||||||
|
this.prefs = prefs
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the key of the [SharedPreferences] you want to let the library handle
|
||||||
|
*
|
||||||
|
* note: you also need to use [withPrefs]
|
||||||
|
*
|
||||||
|
* @param prefsKey the key to use
|
||||||
|
*/
|
||||||
|
fun withPrefsKey(prefsKey: String?): Builder {
|
||||||
|
this.prefsKey = prefsKey
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the resource key to use for the [Toast] if ErrorActivity crashed
|
||||||
|
*
|
||||||
|
* @param errorReporterCrashKey the string key to use
|
||||||
|
*/
|
||||||
|
fun witheErrorReporterCrashKey(@StringRes errorReporterCrashKey: Int): Builder {
|
||||||
|
this.errorReporterCrashKey = errorReporterCrashKey
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* text to add after the "Crash report:" text and before the rest
|
||||||
|
*
|
||||||
|
* ex: "${BuildConfig.APPLICATION_ID} v${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})"
|
||||||
|
*
|
||||||
|
* @param prefix the text you add
|
||||||
|
*/
|
||||||
|
fun withPrefix(prefix: String): Builder {
|
||||||
|
this.prefix = prefix
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* text to add after the content generated by the handler
|
||||||
|
*
|
||||||
|
* @param suffix the text
|
||||||
|
*/
|
||||||
|
fun withSuffix(suffix: String): Builder {
|
||||||
|
this.suffix = suffix
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* build the Crash Handler
|
||||||
|
*/
|
||||||
|
fun build(): CrashHandler {
|
||||||
|
return CrashHandler(activity!!, prefs, prefsKey, errorReporterCrashKey, prefix, suffix)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup the crash handler, after this method is executed crashes should be handled through your
|
||||||
|
* activity
|
||||||
|
*
|
||||||
|
* @param application the application instance to make sure everything is setup right
|
||||||
|
*/
|
||||||
|
fun setup(application: Application) {
|
||||||
|
// Application Error Handling
|
||||||
|
val oldHandler = Thread.getDefaultUncaughtExceptionHandler()
|
||||||
|
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.e(TAG, "En error was detected", paramThrowable)
|
||||||
|
|
||||||
|
// mostly unusable data but also log Thread Stacktrace
|
||||||
|
Log.i(TAG, "Thread StackTrace:")
|
||||||
|
for (item in paramThread.stackTrace) {
|
||||||
|
Log.i(TAG, item.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
// get current time an date
|
||||||
|
val now = Date().time
|
||||||
|
|
||||||
|
// prepare to build debug string
|
||||||
|
var data = "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\non $deviceToReport (${Build.MODEL}) running Android ${Build.VERSION.RELEASE} (${Build.VERSION.SDK_INT})"
|
||||||
|
|
||||||
|
|
||||||
|
// add the current time to it
|
||||||
|
data += "\n\nCrash happened at ${Date(now)}"
|
||||||
|
|
||||||
|
// 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 += "\nLast crash happened at ${Date(lastCrash)}"
|
||||||
|
|
||||||
|
// if a crash already happened just before it means the Error Activity crashed lul
|
||||||
|
if (lastCrash >= now - 1000) {
|
||||||
|
|
||||||
|
// log it :D
|
||||||
|
Log.e(
|
||||||
|
TAG,
|
||||||
|
"Seems like the ErrorActivity also crashed, letting the OS handle it"
|
||||||
|
)
|
||||||
|
|
||||||
|
// try to send a toast indicating it
|
||||||
|
Toast.makeText(
|
||||||
|
application,
|
||||||
|
errorReporterCrashKey ?: R.string.error_reporter_crash,
|
||||||
|
Toast.LENGTH_LONG
|
||||||
|
).show()
|
||||||
|
|
||||||
|
// Use the default exception handler
|
||||||
|
oldHandler?.uncaughtException(paramThread, paramThrowable)
|
||||||
|
return@setDefaultUncaughtExceptionHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the store
|
||||||
|
prefs.edit().putLong(prefsKey, now).apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.i(TAG, "Collecting Error")
|
||||||
|
|
||||||
|
// get Thread name and ID
|
||||||
|
data += "\n\nHappened on Thread \"${paramThread.name}\" (${paramThread.id})"
|
||||||
|
|
||||||
|
// print exception backtrace
|
||||||
|
data += "\n\nException:\n${paramThrowable.stackTraceToString()}\n\n"
|
||||||
|
|
||||||
|
data += suffix ?: ""
|
||||||
|
|
||||||
|
Log.i(TAG, "Starting ${(activity as Class<*>).name}")
|
||||||
|
|
||||||
|
// prepare the activity
|
||||||
|
val intent = Intent(application.applicationContext, activity)
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
|
||||||
|
// add the Data String
|
||||||
|
intent.putExtra("error", data)
|
||||||
|
|
||||||
|
// Start the activity
|
||||||
|
application.startActivity(intent)
|
||||||
|
Log.i(TAG, "Activity should have started")
|
||||||
|
|
||||||
|
// Kill self
|
||||||
|
Process.killProcess(Process.myPid())
|
||||||
|
exitProcess(10)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -5,26 +5,24 @@ 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.CrashHandlerActivityErrorBinding
|
import com.dzeio.crashhandler.databinding.ActivityErrorBinding
|
||||||
|
|
||||||
class ErrorActivity : AppCompatActivity() {
|
class ErrorActivity : AppCompatActivity() {
|
||||||
|
|
||||||
private lateinit var binding: CrashHandlerActivityErrorBinding
|
private lateinit var binding: ActivityErrorBinding
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
binding = CrashHandlerActivityErrorBinding.inflate(layoutInflater)
|
binding = ActivityErrorBinding.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.apply {
|
binding.errorText.text = data
|
||||||
text = data
|
binding.errorText.setTextIsSelectable(true)
|
||||||
setTextIsSelectable(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle the Quit button
|
// Handle the Quit button
|
||||||
binding.errorQuit.setOnClickListener {
|
binding.errorQuit.setOnClickListener {
|
@ -2,8 +2,7 @@
|
|||||||
<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">
|
||||||
|
|
||||||
@ -11,12 +10,11 @@
|
|||||||
android:id="@+id/title"
|
android:id="@+id/title"
|
||||||
style="?textAppearanceHeadline5"
|
style="?textAppearanceHeadline5"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:textAlignment="center"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="8dp"
|
android:layout_marginStart="8dp"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
android:layout_marginEnd="8dp"
|
android:layout_marginEnd="8dp"
|
||||||
android:text="@string/crash_handler_error_app_crash"
|
android:text="@string/error_app_crash"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
@ -28,8 +26,7 @@
|
|||||||
android:layout_marginStart="8dp"
|
android:layout_marginStart="8dp"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
android:layout_marginEnd="8dp"
|
android:layout_marginEnd="8dp"
|
||||||
android:textAlignment="center"
|
android:text="@string/error_app_report"
|
||||||
android:text="@string/crash_handler_error_app_report"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/title" />
|
app:layout_constraintTop_toBottomOf="@+id/title" />
|
||||||
@ -50,8 +47,7 @@
|
|||||||
android:id="@+id/error_text"
|
android:id="@+id/error_text"
|
||||||
style="?textAppearanceCaption"
|
style="?textAppearanceCaption"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content" />
|
||||||
android:textIsSelectable="true" />
|
|
||||||
|
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
||||||
@ -62,7 +58,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_marginStart="16dp"
|
||||||
android:layout_marginBottom="16dp"
|
android:layout_marginBottom="16dp"
|
||||||
android:text="@string/crash_handler_copy_to_clipboard"
|
android:text="@string/copy_to_clipboard"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/error_quit"
|
app:layout_constraintBottom_toTopOf="@+id/error_quit"
|
||||||
app:layout_constraintEnd_toEndOf="@+id/error_quit"
|
app:layout_constraintEnd_toEndOf="@+id/error_quit"
|
||||||
app:layout_constraintStart_toStartOf="@+id/error_quit" />
|
app:layout_constraintStart_toStartOf="@+id/error_quit" />
|
||||||
@ -72,7 +68,7 @@
|
|||||||
android:id="@+id/error_quit"
|
android:id="@+id/error_quit"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
android:text="@string/crash_handler_quit"
|
android:text="@string/quit"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent" />
|
app:layout_constraintStart_toStartOf="parent" />
|
9
crashhandler/src/main/res/values-fr/strings.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<!-- Error Activity Translations -->
|
||||||
|
<string name="error_app_crash">Une Erreur est survenu lors de l\'utilisation de l\'application</string>
|
||||||
|
<string name="error_app_report">Merci d\'envoyer le rapport d\'erreur afin que nous puissions améliorer votre experience</string>
|
||||||
|
<string name="copy_to_clipboard">Copier dans le presse papier</string>
|
||||||
|
<string name="quit">Quitter</string>
|
||||||
|
<string name="error_reporter_crash">Erreur lors de la géneration d\'un rapport d\'erreur</string>
|
||||||
|
</resources>
|
11
crashhandler/src/main/res/values/strings.xml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
<!-- Error Activity Translations -->
|
||||||
|
<string name="error_app_crash">An Error Occurred in the application forcing it to close down</string>
|
||||||
|
<string name="error_app_report">Please report it so we can enhance you\'re Android OpenHealth experience</string>
|
||||||
|
<string name="copy_to_clipboard">Copy to clipboard</string>
|
||||||
|
<string name="quit">Quit</string>
|
||||||
|
<string name="error_reporter_crash">An error occurred while making the error report</string>
|
||||||
|
|
||||||
|
</resources>
|
@ -4,39 +4,15 @@
|
|||||||
# Specifies the JVM arguments used for the daemon process.
|
# Specifies the JVM arguments used for the daemon process.
|
||||||
# The setting is particularly useful for tweaking memory settings.
|
# The setting is particularly useful for tweaking memory settings.
|
||||||
# Default value: -Xmx1024m -XX:MaxPermSize=256m
|
# Default value: -Xmx1024m -XX:MaxPermSize=256m
|
||||||
org.gradle.jvmargs=-Xmx2048m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
||||||
#
|
#
|
||||||
# When configured, Gradle will run in incubating parallel mode.
|
# When configured, Gradle will run in incubating parallel mode.
|
||||||
# This option should only be used with decoupled projects. More details, visit
|
# 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
|
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||||
org.gradle.parallel=true
|
org.gradle.parallel=true
|
||||||
|
#Tue Jul 19 18:42:00 CEST 2022
|
||||||
# enable non transitive R Classes
|
|
||||||
android.nonTransitiveRClass=true
|
android.nonTransitiveRClass=true
|
||||||
|
|
||||||
# use official kotlin style
|
|
||||||
kotlin.code.style=official
|
kotlin.code.style=official
|
||||||
|
|
||||||
# use androidX
|
|
||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
|
|
||||||
# Disable Jetifier
|
|
||||||
android.enableJetifier=false
|
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
|
|
||||||
|
|
||||||
# Enabled Build config
|
|
||||||
android.defaults.buildfeatures.buildconfig=true
|
|
||||||
|
|
||||||
# BREAK EVERYTHING
|
|
||||||
android.nonFinalResIds=false
|
|
||||||
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,6 +1,6 @@
|
|||||||
#Sun Aug 07 22:38:24 CEST 2022
|
#Sun Aug 07 22:38:24 CEST 2022
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
0
gradlew.bat
vendored
Executable file → Normal file
@ -1,5 +0,0 @@
|
|||||||
before_install:
|
|
||||||
- export SDKMAN_DIR="/home/jitpack/.sdkman/"
|
|
||||||
- source "/home/jitpack/.sdkman/bin/sdkman-init.sh"
|
|
||||||
- sdk install java 17.0.1-open
|
|
||||||
- sdk use java 17.0.1-open
|
|
@ -1,409 +0,0 @@
|
|||||||
package com.dzeio.crashhandler
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.app.Application
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import android.content.SharedPreferences
|
|
||||||
import android.os.Build
|
|
||||||
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 java.util.TimeZone
|
|
||||||
|
|
||||||
/**
|
|
||||||
* the Crash Handler class, you can get an instance by using it's [Builder]
|
|
||||||
*/
|
|
||||||
class CrashHandler private constructor(
|
|
||||||
private val application: Application?,
|
|
||||||
private val activity: Class<*>,
|
|
||||||
private val prefs: SharedPreferences?,
|
|
||||||
private val prefsKey: String?,
|
|
||||||
@StringRes
|
|
||||||
private val errorReporterCrashKey: Int?,
|
|
||||||
private val prefix: String? = null,
|
|
||||||
private val suffix: String? = null,
|
|
||||||
private val exportFolder: File? = null
|
|
||||||
) {
|
|
||||||
|
|
||||||
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!!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Builder for the crash handler
|
|
||||||
*/
|
|
||||||
class Builder {
|
|
||||||
private var application: Application? = null
|
|
||||||
private var prefs: SharedPreferences? = null
|
|
||||||
private var prefsKey: String? = null
|
|
||||||
private var errorReporterCrashKey: Int? = null
|
|
||||||
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
|
|
||||||
*
|
|
||||||
* note: you can get the backtrace text by using `intent.getStringExtra("error")`
|
|
||||||
*
|
|
||||||
* @param context the context class to use
|
|
||||||
*/
|
|
||||||
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
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* [SharedPreferences] of your app to be able to handle ErrorActivity crashes
|
|
||||||
*
|
|
||||||
* note: you also need to use [withPrefsKey]
|
|
||||||
*
|
|
||||||
* @param prefs instance of [SharedPreferences] to use
|
|
||||||
*/
|
|
||||||
fun withPrefs(prefs: SharedPreferences?): Builder {
|
|
||||||
this.prefs = prefs
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* the key of the [SharedPreferences] you want to let the library handle
|
|
||||||
*
|
|
||||||
* note: you also need to use [withPrefs]
|
|
||||||
*
|
|
||||||
* @param prefsKey the key to use
|
|
||||||
*/
|
|
||||||
fun withPrefsKey(prefsKey: String?): Builder {
|
|
||||||
this.prefsKey = prefsKey
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* the resource key to use for the [Toast] if ErrorActivity crashed
|
|
||||||
*
|
|
||||||
* @param errorReporterCrashKey the string key to use
|
|
||||||
*/
|
|
||||||
fun witheErrorReporterCrashKey(@StringRes errorReporterCrashKey: Int): Builder {
|
|
||||||
this.errorReporterCrashKey = errorReporterCrashKey
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* text to add after the "Crash report:" text and before the rest
|
|
||||||
*
|
|
||||||
* ex: "${BuildConfig.APPLICATION_ID} v${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})"
|
|
||||||
*
|
|
||||||
* @param prefix the text you add
|
|
||||||
*/
|
|
||||||
fun withPrefix(prefix: String): Builder {
|
|
||||||
this.prefix = prefix
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* text to add after the content generated by the handler
|
|
||||||
*
|
|
||||||
* @param suffix the text
|
|
||||||
*/
|
|
||||||
fun withSuffix(suffix: String): Builder {
|
|
||||||
this.suffix = suffix
|
|
||||||
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
|
|
||||||
*/
|
|
||||||
fun build(): CrashHandler {
|
|
||||||
return CrashHandler(
|
|
||||||
application,
|
|
||||||
activity!!,
|
|
||||||
prefs,
|
|
||||||
prefsKey,
|
|
||||||
errorReporterCrashKey,
|
|
||||||
prefix,
|
|
||||||
suffix,
|
|
||||||
exportLocation
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
instance = this
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Setup the crash handler, after this method is executed crashes should be handled through your
|
|
||||||
* activity
|
|
||||||
*
|
|
||||||
* @param application the application instance to make sure everything is setup right
|
|
||||||
*/
|
|
||||||
fun setup(application: Application) {
|
|
||||||
// Application Error Handling
|
|
||||||
oldHandler = Thread.getDefaultUncaughtExceptionHandler()
|
|
||||||
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.e(TAG, "En error was detected", paramThrowable)
|
|
||||||
|
|
||||||
// mostly unusable data but also log Thread Stacktrace
|
|
||||||
Log.i(TAG, "Thread StackTrace:")
|
|
||||||
for (item in paramThread.stackTrace) {
|
|
||||||
Log.i(TAG, item.toString())
|
|
||||||
}
|
|
||||||
|
|
||||||
// get current time an date
|
|
||||||
val now = Date().time
|
|
||||||
var previousCrash: Long? = null
|
|
||||||
|
|
||||||
// if lib as access to the preferences store
|
|
||||||
if (prefs != null && prefsKey != null) {
|
|
||||||
// get the last Crash
|
|
||||||
previousCrash = prefs.getLong(prefsKey, 0L)
|
|
||||||
|
|
||||||
// if a crash already happened just before it means the Error Activity crashed lul
|
|
||||||
if (previousCrash >= now - 1000) {
|
|
||||||
// log it :D
|
|
||||||
Log.e(
|
|
||||||
TAG,
|
|
||||||
"Seems like the ErrorActivity also crashed, letting the OS handle it"
|
|
||||||
)
|
|
||||||
|
|
||||||
// try to send a toast indicating it
|
|
||||||
Toast.makeText(
|
|
||||||
application,
|
|
||||||
errorReporterCrashKey ?: R.string.crash_handler_reporter_crash,
|
|
||||||
Toast.LENGTH_LONG
|
|
||||||
).show()
|
|
||||||
|
|
||||||
// Use the default exception handler
|
|
||||||
oldHandler?.uncaughtException(paramThread, paramThrowable)
|
|
||||||
return@setDefaultUncaughtExceptionHandler
|
|
||||||
}
|
|
||||||
|
|
||||||
// update the store
|
|
||||||
prefs.edit(true) { putLong(prefsKey, now) }
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.i(TAG, "Collecting Error")
|
|
||||||
|
|
||||||
val data = this.buildData(
|
|
||||||
now,
|
|
||||||
previousCrash,
|
|
||||||
paramThread,
|
|
||||||
paramThrowable
|
|
||||||
)
|
|
||||||
|
|
||||||
try {
|
|
||||||
exportData(data, now)
|
|
||||||
} catch (e: IOException) {
|
|
||||||
Log.e(TAG, "Could not export the data to file", e)
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.i(TAG, "Starting ${activity.name}")
|
|
||||||
|
|
||||||
// prepare the activity
|
|
||||||
val intent = Intent(application, activity)
|
|
||||||
|
|
||||||
// add flags so that it don't use the current Application context
|
|
||||||
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
|
|
||||||
intent.putExtra("error", data)
|
|
||||||
|
|
||||||
// Start the activity
|
|
||||||
application.startActivity(intent)
|
|
||||||
Log.i(TAG, "Activity should have started")
|
|
||||||
|
|
||||||
// Kill self
|
|
||||||
Process.killProcess(Process.myPid())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,71 +0,0 @@
|
|||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
<!-- Error Activity Translations -->
|
|
||||||
<string name="crash_handler_error_app_crash">Une Erreur est survenu lors de l\'utilisation de l\'application</string>
|
|
||||||
<string name="crash_handler_error_app_report">Merci d\'envoyer le rapport d\'erreur afin que nous puissions améliorer votre experience</string>
|
|
||||||
<string name="crash_handler_copy_to_clipboard">Copier dans le presse papier</string>
|
|
||||||
<string name="crash_handler_quit">Quitter</string>
|
|
||||||
<string name="crash_handler_reporter_crash">Erreur lors de la géneration d\'un rapport d\'erreur</string>
|
|
||||||
|
|
||||||
<string name="crash_handler_hard_soft_infos">sur %1$s (%2$s) avec Android %3$s (%4$d)</string>
|
|
||||||
<string name="crash_handler_crash_happened">Plantage arrivé à %1$s</string>
|
|
||||||
<string name="crash_handler_previous_crash">Plantage précédent arrivé à %1$s</string>
|
|
||||||
<string name="crash_handler_thread_infos">Arrivé sur le thread "%1$s" (%2$d)</string>
|
|
||||||
<string name="crash_handler_error">Erreur :</string>
|
|
||||||
<string name="crash_handler_crash_report">Rapport d\'erreur :</string>
|
|
||||||
</resources>
|
|
@ -1,17 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
|
|
||||||
<!-- Error Activity Translations -->
|
|
||||||
<string name="crash_handler_error_app_crash">An Error Occurred in the application forcing it to close down</string>
|
|
||||||
<string name="crash_handler_error_app_report">Please report it so we can enhance you\'re Android OpenHealth experience</string>
|
|
||||||
<string name="crash_handler_copy_to_clipboard">Copy to clipboard</string>
|
|
||||||
<string name="crash_handler_quit">Quit</string>
|
|
||||||
<string name="crash_handler_reporter_crash">An error occurred while making the error report</string>
|
|
||||||
|
|
||||||
<string name="crash_handler_hard_soft_infos">on%1$s (%2$s) running Android %3$s (%4$d)</string>
|
|
||||||
<string name="crash_handler_crash_happened">Crash happened at %1$s</string>
|
|
||||||
<string name="crash_handler_previous_crash">Previous crash happened at %1$s</string>
|
|
||||||
<string name="crash_handler_thread_infos">Happened on Thread "%1$s" (%2$d)</string>
|
|
||||||
<string name="crash_handler_error">Exception:</string>
|
|
||||||
<string name="crash_handler_crash_report">Crash Report:</string>
|
|
||||||
</resources>
|
|
Before Width: | Height: | Size: 6.3 KiB |
@ -1,36 +0,0 @@
|
|||||||
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() {
|
|
||||||
super.onCreate()
|
|
||||||
|
|
||||||
// get the device Preference store
|
|
||||||
val prefs = PreferenceManager.getDefaultSharedPreferences(this)
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
// 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("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()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,91 +0,0 @@
|
|||||||
package com.dzeio.crashhandlertest.ui
|
|
||||||
|
|
||||||
import android.content.ActivityNotFoundException
|
|
||||||
import android.content.Intent
|
|
||||||
import android.net.Uri
|
|
||||||
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.R
|
|
||||||
import com.dzeio.crashhandlertest.databinding.ActivityErrorBinding
|
|
||||||
import kotlin.system.exitProcess
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Example Activity for a custom ErrorActivity
|
|
||||||
*
|
|
||||||
* note: try to keep the complexity of this class as low as possible
|
|
||||||
* to make sure this will always load
|
|
||||||
*/
|
|
||||||
class ErrorActivity : AppCompatActivity() {
|
|
||||||
|
|
||||||
// the view binding
|
|
||||||
private lateinit var binding: ActivityErrorBinding
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
|
|
||||||
// inflate the view
|
|
||||||
binding = ActivityErrorBinding.inflate(layoutInflater)
|
|
||||||
setContentView(binding.root)
|
|
||||||
|
|
||||||
// get the error string from the library
|
|
||||||
val data = intent.getStringExtra("error")
|
|
||||||
|
|
||||||
// put it in the textView
|
|
||||||
binding.errorText.text = data
|
|
||||||
|
|
||||||
// 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.setDataAndType(Uri.parse("mailto:"), "text/plain")
|
|
||||||
intent.putExtra(Intent.EXTRA_EMAIL, arrayOf("report.openhealth@dzeio.com"))
|
|
||||||
intent.putExtra(
|
|
||||||
Intent.EXTRA_SUBJECT,
|
|
||||||
getString(R.string.error_report_application_crash)
|
|
||||||
)
|
|
||||||
intent.putExtra(Intent.EXTRA_TEXT, "${getString(R.string.send_email_report)}\n$data")
|
|
||||||
|
|
||||||
// send intent
|
|
||||||
try {
|
|
||||||
startActivity(
|
|
||||||
Intent.createChooser(
|
|
||||||
intent,
|
|
||||||
getString(R.string.send_email_report)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
} catch (e: ActivityNotFoundException) {
|
|
||||||
Toast.makeText(
|
|
||||||
this,
|
|
||||||
getString(R.string.no_email_client_found),
|
|
||||||
Toast.LENGTH_LONG
|
|
||||||
).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle the GitHub Button
|
|
||||||
binding.errorSubmitGithub.setOnClickListener {
|
|
||||||
// Build URL
|
|
||||||
val title = "Application Error"
|
|
||||||
val url = "https://github.com/dzeiocom/OpenHealth/issues/new?title=$title&body=$data"
|
|
||||||
|
|
||||||
// send intent
|
|
||||||
try {
|
|
||||||
startActivity(
|
|
||||||
Intent(Intent.ACTION_VIEW, Uri.parse(url))
|
|
||||||
)
|
|
||||||
} catch (e: ActivityNotFoundException) {
|
|
||||||
Toast.makeText(this, "No Web Browser found :(", Toast.LENGTH_LONG).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,63 +0,0 @@
|
|||||||
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?) {
|
|
||||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
|
|
||||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
|
||||||
setContentView(binding.root)
|
|
||||||
|
|
||||||
binding.buttonFirst.setOnClickListener {
|
|
||||||
// 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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="108dp"
|
|
||||||
android:height="108dp"
|
|
||||||
android:viewportWidth="48"
|
|
||||||
android:viewportHeight="48"
|
|
||||||
android:autoMirrored="true">
|
|
||||||
<group android:scaleX="0.96130437"
|
|
||||||
android:scaleY="0.96130437"
|
|
||||||
android:translateX="0.9286957"
|
|
||||||
android:translateY="0.9286957">
|
|
||||||
<group android:scaleX="0.99"
|
|
||||||
android:scaleY="0.99"
|
|
||||||
android:translateX="0.24"
|
|
||||||
android:translateY="0.24">
|
|
||||||
<group android:scaleX="0.495"
|
|
||||||
android:scaleY="0.495"
|
|
||||||
android:translateX="12.12"
|
|
||||||
android:translateY="12.12">
|
|
||||||
<path android:fillColor="#FF4CAF50" android:pathData="M13.05,31.9q0.6,0 1.05,-0.45 0.45,-0.45 0.45,-1.05 0,-0.6 -0.45,-1.05 -0.45,-0.45 -1.05,-0.45 -0.6,0 -1.05,0.45 -0.45,0.45 -0.45,1.05 0,0.6 0.45,1.05 0.45,0.45 1.05,0.45ZM11.55,25.4h3v-9.55h-3ZM20.5,29.4h15.95v-3L20.5,26.4ZM20.5,20.85h15.95v-3L20.5,17.85ZM6.6,40q-1.2,0 -2.1,-0.9 -0.9,-0.9 -0.9,-2.1L3.6,11q0,-1.2 0.9,-2.1 0.9,-0.9 2.1,-0.9h34.8q1.2,0 2.1,0.9 0.9,0.9 0.9,2.1v26q0,1.2 -0.9,2.1 -0.9,0.9 -2.1,0.9Z"/>
|
|
||||||
</group>
|
|
||||||
</group>
|
|
||||||
</group>
|
|
||||||
</vector>
|
|
@ -1,82 +0,0 @@
|
|||||||
<?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"
|
|
||||||
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:textAlignment="center"
|
|
||||||
android:text="@string/crash_handler_page_title"
|
|
||||||
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:textAlignment="center"
|
|
||||||
android:text="@string/crash_handler_page_subtitle"
|
|
||||||
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"
|
|
||||||
android:textIsSelectable="true" />
|
|
||||||
|
|
||||||
</ScrollView>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/error_submit_github"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/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="@string/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="@string/quit"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -1,42 +0,0 @@
|
|||||||
<?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:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:fitsSystemWindows="true"
|
|
||||||
tools:context=".ui.MainActivity"
|
|
||||||
android:padding="16dp">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:gravity="center"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/button_first"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/crash_app"
|
|
||||||
android:layout_marginBottom="8dp" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/button_export"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/export_logs" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/button_clear_exports"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/clear_export_logs" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<background android:drawable="@color/ic_launcher_background"/>
|
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
|
||||||
<monochrome android:drawable="@drawable/ic_launcher_foreground"/>
|
|
||||||
</adaptive-icon>
|
|
Before Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 830 B |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 6.8 KiB |
Before Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 10 KiB |
@ -1,15 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
<string name="crash_app">Faire Planter l\'application</string>
|
|
||||||
<string name="error_message">Je suis une erreur</string>
|
|
||||||
<string name="crash_handler_page_title">Crash Handler Titre de page</string>
|
|
||||||
<string name="crash_handler_page_subtitle">Crash Handler sous-titre de page</string>
|
|
||||||
<string name="e_mail">E-Mail</string>
|
|
||||||
<string name="quit">Quitter</string>
|
|
||||||
<string name="no_email_client_found">Aucun client E-Mail trouvé!</string>
|
|
||||||
<string name="send_email_report">Envoyer le rapport de crash par E-Mail</string>
|
|
||||||
<string name="error_report_application_crash">Rapport d\'erreur pour le crash d\'application</string>
|
|
||||||
<string name="export_logs">Exporter les logs de crash sauvegardé</string>
|
|
||||||
<string name="export_complete">Logs de crash exporté!</string>
|
|
||||||
<string name="clear_export_logs">Nettoyer les crashs sauvegardé</string>
|
|
||||||
</resources>
|
|
@ -1,4 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
<color name="ic_launcher_background">#FFFFFF</color>
|
|
||||||
</resources>
|
|
@ -1,16 +0,0 @@
|
|||||||
<resources>
|
|
||||||
<string name="app_name" translatable="false">Crash Handler</string>
|
|
||||||
<string name="crash_app">Crash app</string>
|
|
||||||
<string name="error_message">I am an error</string>
|
|
||||||
<string name="crash_handler_page_title">Crash Handler page title</string>
|
|
||||||
<string name="crash_handler_page_subtitle">crash handler page subtitle</string>
|
|
||||||
<string name="github" translatable="false">Github</string>
|
|
||||||
<string name="e_mail">E-Mail</string>
|
|
||||||
<string name="quit">Quit</string>
|
|
||||||
<string name="no_email_client_found">No E-Mail client found!</string>
|
|
||||||
<string name="send_email_report">Send a Report E-Mail</string>
|
|
||||||
<string name="error_report_application_crash">Error report for the application crash</string>
|
|
||||||
<string name="export_logs">Export saved crash logs</string>
|
|
||||||
<string name="export_complete">Exported crash logs!</string>
|
|
||||||
<string name="clear_export_logs">Cleanup saved crash logs</string>
|
|
||||||
</resources>
|
|
@ -15,5 +15,5 @@ dependencyResolutionManagement {
|
|||||||
|
|
||||||
rootProject.name = "Crash Handler"
|
rootProject.name = "Crash Handler"
|
||||||
|
|
||||||
include ':sample'
|
include ':app'
|
||||||
include ':library'
|
include ':crashhandler'
|
||||||
|