feat: crash report | various small fixes
This commit is contained in:
parent
31c509f88c
commit
8a0224e6b0
12 changed files with 250 additions and 88 deletions
|
@ -227,6 +227,11 @@
|
|||
android:windowSoftInputMode="adjustResize|stateHidden" />
|
||||
<activity android:name=".media.CharacterDetailsActivity" />
|
||||
<activity android:name=".home.NoInternet" />
|
||||
<activity android:name=".others.CrashActivity"
|
||||
android:excludeFromRecents="true"
|
||||
android:exported="true"
|
||||
android:process=":error_process"
|
||||
android:launchMode="singleTask" />
|
||||
<activity
|
||||
android:name=".media.anime.ExoplayerView"
|
||||
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|keyboard|keyboardHidden|navigation"
|
||||
|
|
|
@ -38,6 +38,7 @@ import logcat.LogPriority
|
|||
import logcat.LogcatLogger
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.lang.IllegalStateException
|
||||
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
|
@ -124,11 +125,16 @@ class App : MultiDexApplication() {
|
|||
downloadAddonManager = Injekt.get()
|
||||
torrentAddonManager.init()
|
||||
downloadAddonManager.init()
|
||||
CommentsAPI.fetchAuthToken()
|
||||
CommentsAPI.fetchAuthToken(this@App)
|
||||
|
||||
val useAlarmManager = PrefManager.getVal<Boolean>(PrefName.UseAlarmManager)
|
||||
val scheduler = TaskScheduler.create(this@App, useAlarmManager)
|
||||
scheduler.scheduleAllTasks(this@App)
|
||||
try {
|
||||
scheduler.scheduleAllTasks(this@App)
|
||||
} catch (e: IllegalStateException) {
|
||||
Logger.log("Failed to schedule tasks")
|
||||
Logger.log(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -159,7 +165,7 @@ class App : MultiDexApplication() {
|
|||
}
|
||||
|
||||
companion object {
|
||||
private var instance: App? = null
|
||||
var instance: App? = null
|
||||
|
||||
/** Reference to the application context.
|
||||
*
|
||||
|
|
|
@ -77,7 +77,7 @@ class AnilistQueries {
|
|||
"""{Media(id:${media.id}){id favourites popularity episodes chapters mediaListEntry{id status score(format:POINT_100)progress private notes repeat customLists updatedAt startedAt{year month day}completedAt{year month day}}isFavourite siteUrl idMal nextAiringEpisode{episode airingAt}source countryOfOrigin format duration season seasonYear startDate{year month day}endDate{year month day}genres studios(isMain:true){nodes{id name siteUrl}}description trailer{site id}synonyms tags{name rank isMediaSpoiler}characters(sort:[ROLE,FAVOURITES_DESC],perPage:25,page:1){edges{role voiceActors { id name { first middle last full native userPreferred } image { large medium } languageV2 } node{id image{medium}name{userPreferred}isFavourite}}}relations{edges{relationType(version:2)node{id idMal mediaListEntry{progress private score(format:POINT_100)status}episodes chapters nextAiringEpisode{episode}popularity meanScore isAdult isFavourite format title{english romaji userPreferred}type status(version:2)bannerImage coverImage{large}}}}staffPreview:staff(perPage:8,sort:[RELEVANCE,ID]){edges{role node{id image{large medium}name{userPreferred}}}}recommendations(sort:RATING_DESC){nodes{mediaRecommendation{id idMal mediaListEntry{progress private score(format:POINT_100)status}episodes chapters nextAiringEpisode{episode}meanScore isAdult isFavourite format title{english romaji userPreferred}type status(version:2)bannerImage coverImage{large}}}}externalLinks{url site}}Page(page:1){pageInfo{total perPage currentPage lastPage hasNextPage}mediaList(isFollowing:true,sort:[STATUS],mediaId:${media.id}){id status score(format: POINT_100) progress progressVolumes user{id name avatar{large medium}}}}}"""
|
||||
runBlocking {
|
||||
val anilist = async {
|
||||
var response = executeQuery<Query.Media>(query, force = true, show = true)
|
||||
var response = executeQuery<Query.Media>(query, force = true)
|
||||
if (response != null) {
|
||||
fun parse() {
|
||||
val fetchedMedia = response?.data?.media ?: return
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
package ani.dantotsu.connections.comments
|
||||
|
||||
import android.content.Context
|
||||
import ani.dantotsu.connections.anilist.Anilist
|
||||
import ani.dantotsu.isOnline
|
||||
import ani.dantotsu.settings.saving.PrefManager
|
||||
import ani.dantotsu.settings.saving.PrefName
|
||||
import ani.dantotsu.snackString
|
||||
import ani.dantotsu.toast
|
||||
import ani.dantotsu.util.Logger
|
||||
import com.lagradost.nicehttp.NiceResponse
|
||||
import com.lagradost.nicehttp.Requests
|
||||
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||
|
@ -25,6 +28,7 @@ import uy.kohesive.injekt.api.get
|
|||
|
||||
object CommentsAPI {
|
||||
private const val ADDRESS: String = "https://1224665.xyz:443"
|
||||
private var isOnline: Boolean = true
|
||||
var authToken: String? = null
|
||||
var userId: String? = null
|
||||
var isBanned: Boolean = false
|
||||
|
@ -49,7 +53,8 @@ object CommentsAPI {
|
|||
val json = try {
|
||||
request.get(url)
|
||||
} catch (e: IOException) {
|
||||
snackString("Failed to fetch comments")
|
||||
Logger.log(e)
|
||||
errorMessage("Failed to fetch comments")
|
||||
return null
|
||||
}
|
||||
if (!json.text.startsWith("{")) return null
|
||||
|
@ -71,7 +76,8 @@ object CommentsAPI {
|
|||
val json = try {
|
||||
request.get(url)
|
||||
} catch (e: IOException) {
|
||||
snackString("Failed to fetch comments")
|
||||
Logger.log(e)
|
||||
errorMessage("Failed to fetch comments")
|
||||
return null
|
||||
}
|
||||
if (!json.text.startsWith("{")) return null
|
||||
|
@ -93,7 +99,8 @@ object CommentsAPI {
|
|||
val json = try {
|
||||
request.get(url)
|
||||
} catch (e: IOException) {
|
||||
snackString("Failed to fetch comment")
|
||||
Logger.log(e)
|
||||
errorMessage("Failed to fetch comment")
|
||||
return null
|
||||
}
|
||||
if (!json.text.startsWith("{")) return null
|
||||
|
@ -115,7 +122,8 @@ object CommentsAPI {
|
|||
val json = try {
|
||||
request.post(url)
|
||||
} catch (e: IOException) {
|
||||
snackString("Failed to vote")
|
||||
Logger.log(e)
|
||||
errorMessage("Failed to vote")
|
||||
return false
|
||||
}
|
||||
val res = json.code == 200
|
||||
|
@ -141,7 +149,8 @@ object CommentsAPI {
|
|||
val json = try {
|
||||
request.post(url, requestBody = body.build())
|
||||
} catch (e: IOException) {
|
||||
snackString("Failed to comment")
|
||||
Logger.log(e)
|
||||
errorMessage("Failed to comment")
|
||||
return null
|
||||
}
|
||||
val res = json.code == 200
|
||||
|
@ -152,7 +161,8 @@ object CommentsAPI {
|
|||
val parsed = try {
|
||||
Json.decodeFromString<ReturnedComment>(json.text)
|
||||
} catch (e: Exception) {
|
||||
snackString("Failed to parse comment")
|
||||
Logger.log(e)
|
||||
errorMessage("Failed to parse comment")
|
||||
return null
|
||||
}
|
||||
return Comment(
|
||||
|
@ -179,7 +189,8 @@ object CommentsAPI {
|
|||
val json = try {
|
||||
request.delete(url)
|
||||
} catch (e: IOException) {
|
||||
snackString("Failed to delete comment")
|
||||
Logger.log(e)
|
||||
errorMessage("Failed to delete comment")
|
||||
return false
|
||||
}
|
||||
val res = json.code == 200
|
||||
|
@ -198,7 +209,8 @@ object CommentsAPI {
|
|||
val json = try {
|
||||
request.put(url, requestBody = body)
|
||||
} catch (e: IOException) {
|
||||
snackString("Failed to edit comment")
|
||||
Logger.log(e)
|
||||
errorMessage("Failed to edit comment")
|
||||
return false
|
||||
}
|
||||
val res = json.code == 200
|
||||
|
@ -214,7 +226,8 @@ object CommentsAPI {
|
|||
val json = try {
|
||||
request.post(url)
|
||||
} catch (e: IOException) {
|
||||
snackString("Failed to ban user")
|
||||
Logger.log(e)
|
||||
errorMessage("Failed to ban user")
|
||||
return false
|
||||
}
|
||||
val res = json.code == 200
|
||||
|
@ -241,7 +254,8 @@ object CommentsAPI {
|
|||
val json = try {
|
||||
request.post(url, requestBody = body)
|
||||
} catch (e: IOException) {
|
||||
snackString("Failed to report comment")
|
||||
Logger.log(e)
|
||||
errorMessage("Failed to report comment")
|
||||
return false
|
||||
}
|
||||
val res = json.code == 200
|
||||
|
@ -296,7 +310,8 @@ object CommentsAPI {
|
|||
return null
|
||||
}
|
||||
|
||||
suspend fun fetchAuthToken(client: OkHttpClient? = null) {
|
||||
suspend fun fetchAuthToken(context: Context, client: OkHttpClient? = null) {
|
||||
isOnline = isOnline(context)
|
||||
if (authToken != null) return
|
||||
val MAX_RETRIES = 5
|
||||
val tokenLifetime: Long = 1000 * 60 * 60 * 24 * 6 // 6 days
|
||||
|
@ -325,7 +340,8 @@ object CommentsAPI {
|
|||
val parsed = try {
|
||||
Json.decodeFromString<AuthResponse>(json.text)
|
||||
} catch (e: Exception) {
|
||||
snackString("Failed to login to comments API: ${e.printStackTrace()}")
|
||||
Logger.log(e)
|
||||
errorMessage("Failed to login to comments API: ${e.printStackTrace()}")
|
||||
return
|
||||
}
|
||||
PrefManager.setVal(PrefName.CommentAuthResponse, parsed)
|
||||
|
@ -345,12 +361,18 @@ object CommentsAPI {
|
|||
return
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
snackString("Failed to login to comments API")
|
||||
Logger.log(e)
|
||||
errorMessage("Failed to login to comments API")
|
||||
return
|
||||
}
|
||||
kotlinx.coroutines.delay(60000)
|
||||
}
|
||||
snackString("Failed to login after multiple attempts")
|
||||
errorMessage("Failed to login after multiple attempts")
|
||||
}
|
||||
|
||||
private fun errorMessage(reason: String) {
|
||||
Logger.log(reason)
|
||||
if (isOnline) snackString(reason)
|
||||
}
|
||||
|
||||
fun logout() {
|
||||
|
|
|
@ -38,6 +38,7 @@ import ani.dantotsu.databinding.ItemTitleSearchBinding
|
|||
import ani.dantotsu.databinding.ItemTitleTextBinding
|
||||
import ani.dantotsu.databinding.ItemTitleTrailerBinding
|
||||
import ani.dantotsu.displayTimer
|
||||
import ani.dantotsu.isOnline
|
||||
import ani.dantotsu.loadImage
|
||||
import ani.dantotsu.navBarHeight
|
||||
import ani.dantotsu.profile.User
|
||||
|
@ -80,7 +81,7 @@ class MediaInfoFragment : Fragment() {
|
|||
@SuppressLint("SetJavaScriptEnabled")
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
val model: MediaDetailsViewModel by activityViewModels()
|
||||
val offline: Boolean = PrefManager.getVal(PrefName.OfflineMode)
|
||||
val offline: Boolean = PrefManager.getVal(PrefName.OfflineMode) || !isOnline(requireContext())
|
||||
binding.mediaInfoProgressBar.isGone = loaded
|
||||
binding.mediaInfoContainer.isVisible = loaded
|
||||
binding.mediaInfoContainer.updateLayoutParams<ViewGroup.MarginLayoutParams> { bottomMargin += 128f.px + navBarHeight }
|
||||
|
|
|
@ -27,16 +27,6 @@ class MediaListViewActivity: AppCompatActivity() {
|
|||
binding = ActivityMediaListViewBinding.inflate(layoutInflater)
|
||||
ThemeManager(this).applyTheme()
|
||||
initActivity(this)
|
||||
setContentView(binding.root)
|
||||
|
||||
val primaryColor = getThemeColor(com.google.android.material.R.attr.colorSurface)
|
||||
val primaryTextColor = getThemeColor(com.google.android.material.R.attr.colorPrimary)
|
||||
val secondaryTextColor = getThemeColor(com.google.android.material.R.attr.colorOutline)
|
||||
|
||||
window.statusBarColor = primaryColor
|
||||
window.navigationBarColor = primaryColor
|
||||
binding.listAppBar.setBackgroundColor(primaryColor)
|
||||
binding.listTitle.setTextColor(primaryTextColor)
|
||||
if (!PrefManager.getVal<Boolean>(PrefName.ImmersiveMode)) {
|
||||
this.window.statusBarColor =
|
||||
ContextCompat.getColor(this, R.color.nav_bg_inv)
|
||||
|
@ -50,6 +40,16 @@ class MediaListViewActivity: AppCompatActivity() {
|
|||
topMargin = statusBarHeight
|
||||
}
|
||||
}
|
||||
setContentView(binding.root)
|
||||
|
||||
val primaryColor = getThemeColor(com.google.android.material.R.attr.colorSurface)
|
||||
val primaryTextColor = getThemeColor(com.google.android.material.R.attr.colorPrimary)
|
||||
val secondaryTextColor = getThemeColor(com.google.android.material.R.attr.colorOutline)
|
||||
|
||||
window.statusBarColor = primaryColor
|
||||
window.navigationBarColor = primaryColor
|
||||
binding.listAppBar.setBackgroundColor(primaryColor)
|
||||
binding.listTitle.setTextColor(primaryTextColor)
|
||||
val screenWidth = resources.displayMetrics.run { widthPixels / density }
|
||||
binding.listTitle.text = intent.getStringExtra("title")
|
||||
binding.mediaRecyclerView.adapter = MediaAdaptor(0, mediaList, this)
|
||||
|
|
|
@ -30,7 +30,7 @@ class CommentNotificationTask : Task {
|
|||
withContext(Dispatchers.IO) {
|
||||
PrefManager.init(context) //make sure prefs are initialized
|
||||
val client = OkHttpClient()
|
||||
CommentsAPI.fetchAuthToken(client)
|
||||
CommentsAPI.fetchAuthToken(context, client)
|
||||
val notificationResponse = CommentsAPI.getNotifications(client)
|
||||
var notifications = notificationResponse?.notifications?.toMutableList()
|
||||
//if we have at least one reply notification, we need to fetch the media titles
|
||||
|
|
30
app/src/main/java/ani/dantotsu/others/CrashActivity.kt
Normal file
30
app/src/main/java/ani/dantotsu/others/CrashActivity.kt
Normal file
|
@ -0,0 +1,30 @@
|
|||
package ani.dantotsu.others
|
||||
|
||||
import android.os.Bundle
|
||||
import android.text.InputType
|
||||
import android.view.View
|
||||
import android.widget.Button
|
||||
import android.widget.EditText
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import ani.dantotsu.R
|
||||
import eu.kanade.tachiyomi.util.system.copyToClipboard
|
||||
|
||||
|
||||
class CrashActivity : AppCompatActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_crash)
|
||||
|
||||
val stackTrace = intent.getStringExtra("stackTrace") ?: "No stack trace available"
|
||||
val reportView = findViewById<EditText>(R.id.crashReportView)
|
||||
reportView.setText(stackTrace)
|
||||
reportView.setOnKeyListener(View.OnKeyListener { _, _, _ ->
|
||||
true // Blocks input from hardware keyboards.
|
||||
})
|
||||
|
||||
val copyButton = findViewById<Button>(R.id.copyButton)
|
||||
copyButton.setOnClickListener {
|
||||
copyToClipboard("Crash log", stackTrace)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,16 +5,20 @@ import android.content.Intent
|
|||
import android.os.Build
|
||||
import android.util.Log
|
||||
import androidx.core.content.FileProvider
|
||||
import ani.dantotsu.App
|
||||
import ani.dantotsu.BuildConfig
|
||||
import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
|
||||
import ani.dantotsu.others.CrashActivity
|
||||
import ani.dantotsu.settings.saving.PrefManager
|
||||
import ani.dantotsu.settings.saving.PrefName
|
||||
import ani.dantotsu.snackString
|
||||
import ani.dantotsu.util.Logger.getDeviceAndAppInfo
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.io.File
|
||||
import java.util.Date
|
||||
import java.util.concurrent.Executors
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
object Logger {
|
||||
var file: File? = null
|
||||
|
@ -25,7 +29,7 @@ object Logger {
|
|||
if (!PrefManager.getVal<Boolean>(PrefName.LogToFile) || file != null) return
|
||||
file = File(context.getExternalFilesDir(null), "log.txt")
|
||||
if (file?.exists() == true) {
|
||||
if (file!!.length() > 1024 * 1024 * 10) { // 10MB
|
||||
if (file!!.length() > 1024 * 1024 * 5) { // 5 MB
|
||||
file?.delete()
|
||||
file?.createNewFile()
|
||||
}
|
||||
|
@ -33,52 +37,8 @@ object Logger {
|
|||
file?.createNewFile()
|
||||
}
|
||||
file?.appendText("log started\n")
|
||||
file?.appendText("date/time: ${Date()}\n")
|
||||
file?.appendText("device: ${Build.MODEL}\n")
|
||||
file?.appendText("os version: ${Build.VERSION.RELEASE}\n")
|
||||
file?.appendText(
|
||||
"app version: ${
|
||||
context.packageManager.getPackageInfo(
|
||||
context.packageName,
|
||||
0
|
||||
).versionName
|
||||
}\n"
|
||||
)
|
||||
file?.appendText(
|
||||
"app version code: ${
|
||||
context.packageManager.getPackageInfo(
|
||||
context.packageName,
|
||||
0
|
||||
).run {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
|
||||
longVersionCode
|
||||
else
|
||||
@Suppress("DEPRECATION") versionCode
|
||||
file?.appendText(getDeviceAndAppInfo(context))
|
||||
|
||||
}
|
||||
}\n"
|
||||
)
|
||||
file?.appendText("sdk version: ${Build.VERSION.SDK_INT}\n")
|
||||
file?.appendText("manufacturer: ${Build.MANUFACTURER}\n")
|
||||
file?.appendText("brand: ${Build.BRAND}\n")
|
||||
file?.appendText("product: ${Build.PRODUCT}\n")
|
||||
file?.appendText("device: ${Build.DEVICE}\n")
|
||||
file?.appendText("hardware: ${Build.HARDWARE}\n")
|
||||
file?.appendText("host: ${Build.HOST}\n")
|
||||
file?.appendText("id: ${Build.ID}\n")
|
||||
file?.appendText("type: ${Build.TYPE}\n")
|
||||
file?.appendText("user: ${Build.USER}\n")
|
||||
file?.appendText("tags: ${Build.TAGS}\n")
|
||||
file?.appendText("time: ${Build.TIME}\n")
|
||||
file?.appendText("radio: ${Build.getRadioVersion()}\n")
|
||||
file?.appendText("bootloader: ${Build.BOOTLOADER}\n")
|
||||
file?.appendText("board: ${Build.BOARD}\n")
|
||||
file?.appendText("fingerprint: ${Build.FINGERPRINT}\n")
|
||||
file?.appendText("supported_abis: ${Build.SUPPORTED_ABIS.joinToString()}\n")
|
||||
file?.appendText("supported_32_bit_abis: ${Build.SUPPORTED_32_BIT_ABIS.joinToString()}\n")
|
||||
file?.appendText("supported_64_bit_abis: ${Build.SUPPORTED_64_BIT_ABIS.joinToString()}\n")
|
||||
file?.appendText("is emulator: ${Build.FINGERPRINT.contains("generic")}\n")
|
||||
file?.appendText("--------------------------------\n")
|
||||
} catch (e: Exception) {
|
||||
Injekt.get<CrashlyticsInterface>().logException(e)
|
||||
file = null
|
||||
|
@ -155,15 +115,81 @@ object Logger {
|
|||
file?.delete()
|
||||
file = null
|
||||
}
|
||||
fun getDeviceAndAppInfo(context: Context): String {
|
||||
val pm = context.packageManager
|
||||
val pkgInfo = pm.getPackageInfo(context.packageName, 0)
|
||||
val versionName = pkgInfo.versionName
|
||||
val versionCode = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
pkgInfo.longVersionCode
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
pkgInfo.versionCode
|
||||
}
|
||||
|
||||
return buildString {
|
||||
append("Date/time: ${Date()}\n")
|
||||
append("Device: ${Build.MODEL}\n")
|
||||
append("OS version: ${Build.VERSION.RELEASE}\n")
|
||||
append("App version: $versionName\n")
|
||||
append("App version code: $versionCode\n")
|
||||
append("SDK version: ${Build.VERSION.SDK_INT}\n")
|
||||
append("Manufacturer: ${Build.MANUFACTURER}\n")
|
||||
append("Brand: ${Build.BRAND}\n")
|
||||
append("Product: ${Build.PRODUCT}\n")
|
||||
append("Device: ${Build.DEVICE}\n")
|
||||
append("Hardware: ${Build.HARDWARE}\n")
|
||||
append("Host: ${Build.HOST}\n")
|
||||
append("ID: ${Build.ID}\n")
|
||||
append("Type: ${Build.TYPE}\n")
|
||||
append("User: ${Build.USER}\n")
|
||||
append("Tags: ${Build.TAGS}\n")
|
||||
append("Time: ${Build.TIME}\n")
|
||||
append("Radio: ${Build.getRadioVersion()}\n")
|
||||
append("Bootloader: ${Build.BOOTLOADER}\n")
|
||||
append("Board: ${Build.BOARD}\n")
|
||||
append("Fingerprint: ${Build.FINGERPRINT}\n")
|
||||
append("Supported ABIs: ${Build.SUPPORTED_ABIS.joinToString()}\n")
|
||||
append("Supported 32 bit ABIs: ${Build.SUPPORTED_32_BIT_ABIS.joinToString()}\n")
|
||||
append("Supported 64 bit ABIs: ${Build.SUPPORTED_64_BIT_ABIS.joinToString()}\n")
|
||||
append("Is emulator: ${Build.FINGERPRINT.contains("generic")}\n")
|
||||
append("--------------------------------\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class FinalExceptionHandler : Thread.UncaughtExceptionHandler {
|
||||
private val defaultUEH: Thread.UncaughtExceptionHandler? =
|
||||
Thread.getDefaultUncaughtExceptionHandler()
|
||||
private val defaultUEH = Thread.getDefaultUncaughtExceptionHandler()
|
||||
private val MAX_STACK_TRACE_SIZE = 131071 //128 KB - 1
|
||||
|
||||
override fun uncaughtException(t: Thread, e: Throwable) {
|
||||
Logger.uncaughtException(t, e)
|
||||
val stackTraceString = Log.getStackTraceString(e)
|
||||
Injekt.get<CrashlyticsInterface>().logException(e)
|
||||
|
||||
if (App.instance?.applicationContext != null) {
|
||||
App.instance?.applicationContext?.let {
|
||||
val report = StringBuilder()
|
||||
report.append(getDeviceAndAppInfo(it))
|
||||
report.append("Thread: ${t.name}\n")
|
||||
report.append("Exception: ${e.message}\n")
|
||||
report.append("Stack trace:\n")
|
||||
report.append(stackTraceString)
|
||||
val reportString = report.toString()
|
||||
Logger.uncaughtException(t, Error(reportString))
|
||||
val intent = Intent(it, CrashActivity::class.java)
|
||||
if (reportString.length > MAX_STACK_TRACE_SIZE) {
|
||||
val subStr = reportString.substring(0, MAX_STACK_TRACE_SIZE)
|
||||
intent.putExtra("stackTrace", subStr)
|
||||
} else intent.putExtra("stackTrace", reportString)
|
||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
it.startActivity(intent)
|
||||
}
|
||||
} else {
|
||||
Logger.log("App context is null")
|
||||
Logger.uncaughtException(t, e)
|
||||
}
|
||||
|
||||
defaultUEH?.uncaughtException(t, e)
|
||||
android.os.Process.killProcess(android.os.Process.myPid())
|
||||
exitProcess(10)
|
||||
}
|
||||
}
|
75
app/src/main/res/layout/activity_crash.xml
Normal file
75
app/src/main/res/layout/activity_crash.xml
Normal file
|
@ -0,0 +1,75 @@
|
|||
<?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:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:gravity="top|center_horizontal"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="48dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/copyButton"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.0">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/poppins_thin"
|
||||
android:text="@string/sad"
|
||||
android:textColor="?attr/colorPrimary"
|
||||
android:textSize="64sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="200dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/poppins_bold"
|
||||
android:text="@string/app_died"
|
||||
android:textAlignment="center"
|
||||
android:textColor="?attr/colorPrimary"
|
||||
android:textSize="32sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="200dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/poppins"
|
||||
android:text="@string/an_unexpected_error_occurred"
|
||||
android:textAlignment="center"
|
||||
android:textColor="?attr/colorOnBackground" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/crashReportView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:fontFamily="@font/poppins"
|
||||
android:inputType="textMultiLine|none"
|
||||
android:textIsSelectable="true"
|
||||
android:focusable="false"
|
||||
android:focusableInTouchMode="false"
|
||||
android:padding="16dp"
|
||||
android:textColor="?attr/colorOnBackground" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/copyButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="64dp"
|
||||
android:layout_gravity="bottom|center_horizontal"
|
||||
android:layout_margin="64dp"
|
||||
android:fontFamily="@font/poppins_bold"
|
||||
android:maxLines="1"
|
||||
android:text="@string/copy_report"
|
||||
app:cornerRadius="16dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -44,11 +44,7 @@
|
|||
android:fontFamily="@font/poppins_thin"
|
||||
android:text="@string/sad"
|
||||
android:textColor="?attr/colorPrimary"
|
||||
android:textSize="64sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
android:textSize="64sp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/no_internet"
|
||||
|
@ -56,10 +52,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:text="@string/no_internet"
|
||||
android:textAlignment="center"
|
||||
android:textColor="?attr/colorOnBackground"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/noInternetSad" />
|
||||
android:textColor="?attr/colorOnBackground"/>
|
||||
</LinearLayout>
|
||||
|
||||
<Button
|
||||
|
@ -69,6 +62,7 @@
|
|||
android:layout_gravity="bottom|center_horizontal"
|
||||
android:layout_margin="128dp"
|
||||
android:fontFamily="@font/poppins_bold"
|
||||
android:maxLines="1"
|
||||
android:text="@string/refresh"
|
||||
app:cornerRadius="16dp"
|
||||
app:icon="@drawable/ic_round_refresh_24" />
|
||||
|
|
|
@ -967,4 +967,7 @@ Non quae tempore quo provident laudantium qui illo dolor vel quia dolor et exerc
|
|||
<string name="your_story">Your Story</string>
|
||||
<string name="your_progress">Your Progress</string>
|
||||
<string name="logout_confirm">Are you sure you want to logout?</string>
|
||||
<string name="app_died">APP DIED</string>
|
||||
<string name="an_unexpected_error_occurred">An unexpected error occurred.\nPlease send a crash report to the developer :)</string>
|
||||
<string name="copy_report">Copy Report</string>
|
||||
</resources>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue