mirror of
https://github.com/KitsunePie/AppErrorsTracking.git
synced 2025-09-01 16:55:18 +08:00
Added AppErrorsDetailActivity function and more function
This commit is contained in:
20
README.md
20
README.md
@@ -22,27 +22,33 @@ Added more features to app's crash dialog, fixed custom rom deleted dialog, the
|
||||
|
||||
## Feature
|
||||
|
||||
- 重新定制应用错误对话框
|
||||
- 重新定制应用异常错误对话框
|
||||
|
||||
- 记录每个应用的异常记录,直到重新启动前
|
||||
|
||||
- “错误详情”按钮功能,可查看具体的异常堆栈
|
||||
- 记录每个应用的异常,直到重新启动前持续保留
|
||||
|
||||
- “应用信息”按钮功能(原生功能),点击可打开当前出错的应用详情页面
|
||||
|
||||
- “重新打开”按钮功能(原生功能),在首次崩溃可点击按钮重新打开应用
|
||||
- “重新打开”按钮功能(原生功能),在首次错误可点击按钮重新打开应用
|
||||
|
||||
- “屡次停止运行”显示(原生功能)
|
||||
|
||||
- “忽略(直到设备重新解锁/重新启动)”显示(原生功能)
|
||||
|
||||
- 对话框支持 Android 10 及以上系统的深色模式
|
||||
- “错误详情”按钮功能,可查看具体的异常堆栈
|
||||
|
||||
- 导出异常堆栈到文件功能
|
||||
|
||||
- 复制异常堆栈功能
|
||||
|
||||
- 打印异常堆栈到控制台功能
|
||||
|
||||
- 支持 Android 10 及以上系统的深色模式
|
||||
|
||||
## Future
|
||||
|
||||
此项目依然在开发中,现在未解决的问题和包含的问题如下
|
||||
|
||||
- “错误详情”按钮现在是无效的,还在开发
|
||||
- 异常历史记录和排除列表正在开发
|
||||
|
||||
- 后台进程可能依然会弹出崩溃对话框且开发者选项里的设置无效,还在排查
|
||||
|
||||
|
@@ -1,7 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission
|
||||
android:name="android.permission.QUERY_ALL_PACKAGES"
|
||||
tools:ignore="QueryAllPackagesPermission" />
|
||||
|
||||
<application
|
||||
android:name=".application.AppErrorsApplication"
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
@@ -21,5 +27,10 @@
|
||||
<meta-data
|
||||
android:name="xposedscope"
|
||||
android:resource="@array/module_scope" />
|
||||
|
||||
<activity
|
||||
android:name=".ui.activity.AppErrorsDetailActivity"
|
||||
android:exported="true"
|
||||
android:screenOrientation="behind" />
|
||||
</application>
|
||||
</manifest>
|
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer.
|
||||
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/KitsunePie/AppErrorsTracking
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Affero General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/5/10.
|
||||
*/
|
||||
package com.fankes.apperrorstracking.application
|
||||
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import com.highcapable.yukihookapi.hook.xposed.application.ModuleApplication
|
||||
|
||||
class AppErrorsApplication : ModuleApplication() {
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
/** 跟随系统夜间模式 */
|
||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
|
||||
}
|
||||
}
|
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer.
|
||||
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/KitsunePie/AppErrorsTracking
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Affero General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/5/10.
|
||||
*/
|
||||
package com.fankes.apperrorstracking.bean
|
||||
|
||||
import java.io.Serializable
|
||||
|
||||
/**
|
||||
* 应用异常信息 bean
|
||||
* @param packageName 包名
|
||||
* @param isNativeCrash 是否为原生层异常
|
||||
* @param exceptionClassName 异常类名
|
||||
* @param exceptionMessage 异常信息
|
||||
* @param throwClassName 抛出异常的类名
|
||||
* @param throwFileName 抛出异常的文件名
|
||||
* @param throwMethodName 抛出异常的方法名
|
||||
* @param throwLineNumber 抛出异常的行号
|
||||
* @param stackTrace 异常堆栈
|
||||
* @param timestamp 记录时间戳
|
||||
*/
|
||||
data class AppErrorsInfoBean(
|
||||
var packageName: String,
|
||||
var isNativeCrash: Boolean,
|
||||
var exceptionClassName: String,
|
||||
var exceptionMessage: String,
|
||||
var throwFileName: String,
|
||||
var throwClassName: String,
|
||||
var throwMethodName: String,
|
||||
var throwLineNumber: Int,
|
||||
var stackTrace: String,
|
||||
var timestamp: Long,
|
||||
) : Serializable
|
@@ -24,6 +24,7 @@
|
||||
package com.fankes.apperrorstracking.hook.entity
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.app.ApplicationErrorReport
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
@@ -40,6 +41,8 @@ import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import com.fankes.apperrorstracking.R
|
||||
import com.fankes.apperrorstracking.bean.AppErrorsInfoBean
|
||||
import com.fankes.apperrorstracking.ui.activity.AppErrorsDetailActivity
|
||||
import com.fankes.apperrorstracking.utils.drawable.drawabletoolbox.DrawableBuilder
|
||||
import com.fankes.apperrorstracking.utils.factory.*
|
||||
import com.highcapable.yukihookapi.hook.bean.VariousClass
|
||||
@@ -52,6 +55,8 @@ import com.highcapable.yukihookapi.hook.type.android.MessageClass
|
||||
|
||||
object FrameworkHooker : YukiBaseHooker() {
|
||||
|
||||
const val APP_ERRORS_INFO = "app_errors_info"
|
||||
|
||||
private const val AppErrorsClass = "com.android.server.am.AppErrors"
|
||||
|
||||
private const val AppErrorResultClass = "com.android.server.am.AppErrorResult"
|
||||
@@ -79,6 +84,9 @@ object FrameworkHooker : YukiBaseHooker() {
|
||||
/** 已忽略错误的 APP 数组 - 直到重新启动 */
|
||||
private var ignoredErrorsIfRestartApps = HashSet<String>()
|
||||
|
||||
/** 已记录的 APP 异常信息数组 - 直到重新启动 */
|
||||
private val appErrorsRecords = arrayListOf<AppErrorsInfoBean>()
|
||||
|
||||
/** 是否已经注册广播 */
|
||||
private var isRegisterReceiver = false
|
||||
|
||||
@@ -113,6 +121,14 @@ object FrameworkHooker : YukiBaseHooker() {
|
||||
isRegisterReceiver = true
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最新的 APP 错误信息
|
||||
* @param packageName 包名
|
||||
* @return [AppErrorsInfoBean] or null
|
||||
*/
|
||||
private fun lastAppErrorsInfo(packageName: String) =
|
||||
appErrorsRecords.takeIf { it.isNotEmpty() }?.filter { it.packageName == packageName }?.get(0)
|
||||
|
||||
/**
|
||||
* 获取 I18n 字符串
|
||||
* @param resId 模块资源 Id
|
||||
@@ -203,7 +219,7 @@ object FrameworkHooker : YukiBaseHooker() {
|
||||
val packageName = appInfo?.packageName ?: processName
|
||||
|
||||
/** 当前 APP 名称 */
|
||||
val appName = appInfo?.let { context.packageManager.getApplicationLabel(it) } ?: packageName
|
||||
val appName = appInfo?.let { context.appName(it.packageName) } ?: packageName
|
||||
|
||||
/** 是否为 APP */
|
||||
val isApp = (PackageListClass.clazz.method {
|
||||
@@ -260,7 +276,9 @@ object FrameworkHooker : YukiBaseHooker() {
|
||||
/** 错误详情按钮 */
|
||||
val errorDetailButton =
|
||||
createButtonItem(context, R.drawable.ic_baseline_bug_report, string(R.string.error_detail)) {
|
||||
// TODO 待开发
|
||||
cancel()
|
||||
lastAppErrorsInfo(packageName)?.let { AppErrorsDetailActivity.start(context, it) }
|
||||
?: context.toast(msg = "Invalid AppErrorsInfo")
|
||||
}
|
||||
|
||||
/** 忽略按钮 - 直到解锁 */
|
||||
@@ -300,6 +318,34 @@ object FrameworkHooker : YukiBaseHooker() {
|
||||
}.show()
|
||||
}
|
||||
}
|
||||
injectMember {
|
||||
method {
|
||||
name = "crashApplication"
|
||||
paramCount = 2
|
||||
}
|
||||
afterHook {
|
||||
/** 当前 APP 信息 */
|
||||
val appInfo = ProcessRecordClass.clazz.field { name = "info" }.get(args().first().any()).cast<ApplicationInfo>()
|
||||
/** 当前异常信息 */
|
||||
args().last().cast<ApplicationErrorReport.CrashInfo>()?.also { crashInfo ->
|
||||
/** 添加到第一位 */
|
||||
appErrorsRecords.add(
|
||||
0, AppErrorsInfoBean(
|
||||
packageName = appInfo?.packageName ?: "",
|
||||
isNativeCrash = crashInfo.exceptionClassName.lowercase() == "native crash",
|
||||
exceptionClassName = crashInfo.exceptionClassName ?: "",
|
||||
exceptionMessage = crashInfo.exceptionMessage ?: "",
|
||||
throwFileName = crashInfo.throwFileName ?: "",
|
||||
throwClassName = crashInfo.throwClassName ?: "",
|
||||
throwMethodName = crashInfo.throwMethodName ?: "",
|
||||
throwLineNumber = crashInfo.throwLineNumber,
|
||||
stackTrace = crashInfo.stackTrace?.trim() ?: "",
|
||||
timestamp = System.currentTimeMillis()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer.
|
||||
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/KitsunePie/AppErrorsTracking
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Affero General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/5/7.
|
||||
*/
|
||||
@file:Suppress("DEPRECATION", "OVERRIDE_DEPRECATION")
|
||||
|
||||
package com.fankes.apperrorstracking.ui.activity
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.core.view.isGone
|
||||
import com.fankes.apperrorstracking.BuildConfig
|
||||
import com.fankes.apperrorstracking.R
|
||||
import com.fankes.apperrorstracking.bean.AppErrorsInfoBean
|
||||
import com.fankes.apperrorstracking.databinding.ActivityAppErrorsDetailBinding
|
||||
import com.fankes.apperrorstracking.hook.entity.FrameworkHooker
|
||||
import com.fankes.apperrorstracking.ui.activity.base.BaseActivity
|
||||
import com.fankes.apperrorstracking.utils.factory.*
|
||||
import com.highcapable.yukihookapi.hook.log.loggerE
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
class AppErrorsDetailActivity : BaseActivity<ActivityAppErrorsDetailBinding>() {
|
||||
|
||||
companion object {
|
||||
|
||||
/** 请求保存文件回调标识 */
|
||||
private const val WRITE_REQUEST_CODE = 0
|
||||
|
||||
/**
|
||||
* 启动 [AppErrorsDetailActivity]
|
||||
* @param context 实例
|
||||
* @param appErrorsInfo 应用异常信息
|
||||
*/
|
||||
fun start(context: Context, appErrorsInfo: AppErrorsInfoBean) {
|
||||
runCatching {
|
||||
context.startActivity(Intent().apply {
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
component = ComponentName(BuildConfig.APPLICATION_ID, AppErrorsDetailActivity::class.java.name)
|
||||
putExtra(FrameworkHooker.APP_ERRORS_INFO, appErrorsInfo)
|
||||
})
|
||||
}.onFailure { context.toast(msg = "Start AppErrorsDetailActivity failed") }
|
||||
}
|
||||
}
|
||||
|
||||
/** 预导出的异常堆栈 */
|
||||
private var stackTrace = ""
|
||||
|
||||
override fun onCreate() {
|
||||
val appErrorsInfo =
|
||||
intent?.getSerializableExtra(FrameworkHooker.APP_ERRORS_INFO) as? AppErrorsInfoBean ?: return toastAndFinish()
|
||||
|
||||
/** 创建异常堆栈模板 */
|
||||
fun createStack() =
|
||||
"package name: ${appErrorsInfo.packageName} timestamp: ${appErrorsInfo.timestamp}\n${appErrorsInfo.stackTrace}"
|
||||
binding.titleBackIcon.setOnClickListener { onBackPressed() }
|
||||
binding.printIcon.setOnClickListener {
|
||||
loggerE(msg = createStack())
|
||||
toast(getString(R.string.print_to_logcat_success))
|
||||
}
|
||||
binding.copyIcon.setOnClickListener { copyToClipboard(appErrorsInfo.stackTrace) }
|
||||
binding.exportIcon.setOnClickListener {
|
||||
stackTrace = createStack()
|
||||
runCatching {
|
||||
startActivityForResult(Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
type = "text/plain"
|
||||
putExtra(Intent.EXTRA_TITLE, "${appErrorsInfo.packageName}_${appErrorsInfo.timestamp}.log")
|
||||
}, WRITE_REQUEST_CODE)
|
||||
}.onFailure { toast(msg = "Start Android SAF failed") }
|
||||
}
|
||||
binding.appIcon.setImageDrawable(appIcon(appErrorsInfo.packageName))
|
||||
binding.appName.text = appName(appErrorsInfo.packageName)
|
||||
binding.appVersion.text = appVersion(appErrorsInfo.packageName)
|
||||
binding.jvmErrorPanel.isGone = appErrorsInfo.isNativeCrash
|
||||
binding.errorTypeIcon.setImageResource(if (appErrorsInfo.isNativeCrash) R.drawable.ic_cpp else R.drawable.ic_java)
|
||||
binding.errorInfoText.text = appErrorsInfo.exceptionMessage
|
||||
binding.errorTypeText.text = appErrorsInfo.exceptionClassName
|
||||
binding.errorFileNameText.text = appErrorsInfo.throwFileName
|
||||
binding.errorThrowClassText.text = appErrorsInfo.throwClassName
|
||||
binding.errorThrowMethodText.text = appErrorsInfo.throwMethodName
|
||||
binding.errorLineNumberText.text = appErrorsInfo.throwLineNumber.toString()
|
||||
binding.errorRecordTimeText.text = SimpleDateFormat.getDateTimeInstance().format(Date(appErrorsInfo.timestamp))
|
||||
binding.errorStackText.text = appErrorsInfo.stackTrace
|
||||
}
|
||||
|
||||
/** 弹出提示并退出 */
|
||||
private fun toastAndFinish() {
|
||||
toast(msg = "Invalid AppErrorsInfo, exit")
|
||||
finish()
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if (requestCode == WRITE_REQUEST_CODE && resultCode == Activity.RESULT_OK) runCatching {
|
||||
data?.data?.let {
|
||||
contentResolver?.openOutputStream(it)?.apply { write(stackTrace.toByteArray()) }?.close()
|
||||
toast(getString(R.string.output_stack_success))
|
||||
} ?: toast(getString(R.string.output_stack_fail))
|
||||
}.onFailure { toast(getString(R.string.output_stack_fail)) }
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
intent?.removeExtra(FrameworkHooker.APP_ERRORS_INFO)
|
||||
finish()
|
||||
}
|
||||
}
|
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer.
|
||||
* Copyright (C) 2019-2022 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/KitsunePie/AppErrorsTracking
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Affero General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is Created by fankes on 2022/5/7.
|
||||
*/
|
||||
@file:Suppress("UNCHECKED_CAST")
|
||||
|
||||
package com.fankes.apperrorstracking.ui.activity.base
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import com.fankes.apperrorstracking.R
|
||||
import com.fankes.apperrorstracking.utils.factory.isNotSystemInDarkMode
|
||||
import com.gyf.immersionbar.ktx.immersionBar
|
||||
import com.highcapable.yukihookapi.hook.factory.method
|
||||
import com.highcapable.yukihookapi.hook.type.android.LayoutInflaterClass
|
||||
import java.lang.reflect.ParameterizedType
|
||||
|
||||
abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
|
||||
|
||||
/** 获取绑定布局对象 */
|
||||
lateinit var binding: VB
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
javaClass.genericSuperclass.also { type ->
|
||||
if (type is ParameterizedType) {
|
||||
binding = (type.actualTypeArguments[0] as Class<VB>).method {
|
||||
name = "inflate"
|
||||
param(LayoutInflaterClass)
|
||||
}.get().invoke<VB>(layoutInflater) ?: error("binding failed")
|
||||
setContentView(binding.root)
|
||||
} else error("binding but got wrong type")
|
||||
}
|
||||
/** 隐藏系统的标题栏 */
|
||||
supportActionBar?.hide()
|
||||
/** 初始化沉浸状态栏 */
|
||||
immersionBar {
|
||||
statusBarColor(R.color.colorThemeBackground)
|
||||
autoDarkModeEnable(true)
|
||||
statusBarDarkFont(isNotSystemInDarkMode)
|
||||
navigationBarColor(R.color.colorThemeBackground)
|
||||
navigationBarDarkIcon(isNotSystemInDarkMode)
|
||||
fitsSystemWindows(true)
|
||||
}
|
||||
/** 装载子类 */
|
||||
onCreate()
|
||||
}
|
||||
|
||||
/** 回调 [onCreate] 方法 */
|
||||
abstract fun onCreate()
|
||||
}
|
@@ -23,12 +23,19 @@
|
||||
|
||||
package com.fankes.apperrorstracking.utils.factory
|
||||
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.Configuration
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.net.Uri
|
||||
import android.provider.Settings
|
||||
import android.widget.Toast
|
||||
import com.fankes.apperrorstracking.R
|
||||
|
||||
/**
|
||||
* 系统深色模式是否开启
|
||||
@@ -56,12 +63,57 @@ fun Number.dp(context: Context) = dpFloat(context).toInt()
|
||||
*/
|
||||
fun Number.dpFloat(context: Context) = toFloat() * context.resources.displayMetrics.density
|
||||
|
||||
/**
|
||||
* 获取 APP 名称
|
||||
* @param packageName 包名
|
||||
* @return [String]
|
||||
*/
|
||||
fun Context.appName(packageName: String) =
|
||||
runCatching {
|
||||
packageManager.getPackageInfo(packageName, PackageManager.GET_META_DATA)
|
||||
.applicationInfo.loadLabel(packageManager).toString()
|
||||
}.getOrNull() ?: packageName
|
||||
|
||||
/**
|
||||
* 获取 APP 完整版本
|
||||
* @param packageName 包名
|
||||
* @return [String]
|
||||
*/
|
||||
fun Context.appVersion(packageName: String) =
|
||||
runCatching {
|
||||
packageManager.getPackageInfo(packageName, PackageManager.GET_META_DATA)?.let { "${it.versionName} (${it.versionCode})" }
|
||||
}.getOrNull() ?: "unknown"
|
||||
|
||||
/**
|
||||
* 获取 APP 图标
|
||||
* @param packageName 包名
|
||||
* @return [Drawable]
|
||||
*/
|
||||
fun Context.appIcon(packageName: String) =
|
||||
runCatching {
|
||||
packageManager.getPackageInfo(packageName, PackageManager.GET_META_DATA)
|
||||
.applicationInfo.loadIcon(packageManager)
|
||||
}.getOrNull() ?: ColorDrawable(Color.WHITE)
|
||||
|
||||
/**
|
||||
* 弹出 [Toast]
|
||||
* @param msg 提示内容
|
||||
*/
|
||||
fun Context.toast(msg: String) = Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
|
||||
|
||||
/**
|
||||
* 复制到剪贴板
|
||||
* @param content 要复制的文本
|
||||
*/
|
||||
fun Context.copyToClipboard(content: String) = runCatching {
|
||||
(getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager).apply {
|
||||
setPrimaryClip(ClipData.newPlainText(null, content))
|
||||
(primaryClip?.getItemAt(0)?.text ?: "").also {
|
||||
if (it != content) toast(getString(R.string.copy_fail)) else toast(getString(R.string.copied))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转 APP 自身设置界面
|
||||
* @param packageName 包名
|
||||
|
6
app/src/main/res/drawable-night/bg_permotion_round.xml
Normal file
6
app/src/main/res/drawable-night/bg_permotion_round.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="#666E6E6E" />
|
||||
<corners android:radius="15dp" />
|
||||
</shape>
|
10
app/src/main/res/drawable/bg_button_round.xml
Normal file
10
app/src/main/res/drawable/bg_button_round.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="#777777">
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#66DAD9D9" />
|
||||
<corners android:radius="15dp" />
|
||||
</shape>
|
||||
</item>
|
||||
</ripple>
|
6
app/src/main/res/drawable/bg_permotion_round.xml
Normal file
6
app/src/main/res/drawable/bg_permotion_round.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="#66E4E4E4" />
|
||||
<corners android:radius="15dp" />
|
||||
</shape>
|
6
app/src/main/res/drawable/bg_stack_round.xml
Normal file
6
app/src/main/res/drawable/bg_stack_round.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="#FF14171F" />
|
||||
<corners android:radius="15dp" />
|
||||
</shape>
|
12
app/src/main/res/drawable/ic_copy.xml
Normal file
12
app/src/main/res/drawable/ic_copy.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="150dp"
|
||||
android:height="150dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M316.8,784h377.6v140.8c0,42.2 -34.6,76.8 -76.8,76.8h-518.4c-42.2,0 -76.8,-34.6 -76.8,-76.8v-518.4c0,-42.2 34.6,-76.8 76.8,-76.8h140.8v377.6c0,42.2 34.6,76.8 76.8,76.8z" />
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M1008,105.6v518.4c0,42.2 -34.6,76.8 -76.8,76.8h-518.4c-42.2,0 -76.8,-34.6 -76.8,-76.8v-518.4c0,-42.2 34.6,-76.8 76.8,-76.8h518.4c42.2,0 76.8,34.6 76.8,76.8z" />
|
||||
</vector>
|
14
app/src/main/res/drawable/ic_cpp.xml
Normal file
14
app/src/main/res/drawable/ic_cpp.xml
Normal file
@@ -0,0 +1,14 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:width="250dp"
|
||||
android:height="250dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024"
|
||||
tools:ignore="VectorRaster">
|
||||
<path
|
||||
android:fillColor="#1D88E5"
|
||||
android:pathData="M403.5,880.6c-202.8,0 -368.6,-165.9 -368.6,-368.6s165.9,-368.6 368.6,-368.6c116.7,0 223.2,54.3 292.9,145.4L563.2,367.6c-41,-44 -98.3,-70.7 -158.7,-70.7 -117.8,0 -215,96.3 -215,215s96.3,215 215,215c60.4,0 117.8,-26.6 158.7,-70.7l134.1,78.8C628.7,826.4 520.2,880.6 403.5,880.6z" />
|
||||
<path
|
||||
android:fillColor="#1D88E5"
|
||||
android:pathData="M772.1,542.7L711.7,542.7v61.4h-62.5v-61.4L588.8,542.7v-61.4h60.4v-61.4h62.5v61.4h60.4v61.4zM989.2,542.7L926.7,542.7v61.4h-60.4v-61.4L803.8,542.7v-61.4h62.5v-61.4h60.4v61.4h62.5v61.4z" />
|
||||
</vector>
|
9
app/src/main/res/drawable/ic_exception.xml
Normal file
9
app/src/main/res/drawable/ic_exception.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="250dp"
|
||||
android:height="250dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:pathData="M511,0a376.4,376.4 0,0 0,-265.1 106.8,365.1 365.1,0 0,0 -108,258.6v498.7h742L880,365.5a362.4,362.4 0,0 0,-108.9 -258.6A374.9,374.9 0,0 0,511 0zM480.3,744.6L469.5,502.9L286.9,502.9l239.9,-303.1 20.5,195.2 174.1,6.1 -243.2,343.3zM0.1,966.4a55.6,55.6 0,0 1,55.6 -56.2h910.6a57.6,57.6 0,0 1,39.3 16.3,58.7 58.7,0 0,1 16.4,39.9 55.7,55.7 0,0 1,-55.7 55.6L55.6,1022.1a55.6,55.6 0,0 1,-55.5 -55.6z"
|
||||
android:fillColor="#ffffff"/>
|
||||
</vector>
|
12
app/src/main/res/drawable/ic_export.xml
Normal file
12
app/src/main/res/drawable/ic_export.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="150dp"
|
||||
android:height="150dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M768,678.4v108.8H256v-108.8c0,-12.8 -6.4,-19.2 -19.2,-19.2H147.2c-12.8,0 -19.2,6.4 -19.2,19.2v172.8c0,38.4 25.6,64 64,64h640c38.4,0 64,-25.6 64,-64v-172.8c0,-12.8 -6.4,-19.2 -19.2,-19.2h-89.6c-12.8,0 -19.2,6.4 -19.2,19.2z" />
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M499.2,115.2L281.6,390.4c-12.8,12.8 0,32 12.8,32H448v268.8c0,6.4 6.4,12.8 19.2,12.8h89.6c12.8,0 19.2,-6.4 19.2,-19.2V416h153.6c19.2,0 25.6,-19.2 12.8,-32L524.8,115.2c-6.4,-6.4 -19.2,-6.4 -25.6,0z" />
|
||||
</vector>
|
26
app/src/main/res/drawable/ic_java.xml
Normal file
26
app/src/main/res/drawable/ic_java.xml
Normal file
@@ -0,0 +1,26 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:width="250dp"
|
||||
android:height="250dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024"
|
||||
tools:ignore="VectorRaster">
|
||||
<path
|
||||
android:fillColor="#FF1515"
|
||||
android:pathData="M747,175.6c-29.7,21 -58.4,38.9 -90.1,64.5 -24.1,19.5 -67.6,46.6 -69.6,82.4 -3.6,54.8 80.9,106 36.4,175.6 -16.9,26.6 -46.1,37.9 -82.4,54.3 -4.6,-8.2 9.7,-14.8 15.4,-23 56.3,-81.4 -58.4,-108.5 -44,-208.9 13.8,-97.8 128,-131.6 234.5,-144.9z" />
|
||||
<path
|
||||
android:fillColor="#2365C4"
|
||||
android:pathData="M388.1,511c-26.6,12.3 -71.2,14.8 -90.1,41.5 19.5,14.8 48.1,14.3 74.8,15.4 109.1,5.1 245.2,-4.6 337.9,-20.5 3.1,6.7 -12.8,18.4 -23,25.6 -58.4,43 -240.6,54.8 -366.6,46.6 -42,-3.1 -138.2,-13.8 -139.3,-51.7 -1.5,-45.6 116.7,-50.7 164.9,-54.3 9.7,-1 27.6,-4.6 41.5,-2.6zM333.8,626.7c12.3,1.5 -8.7,8.7 -5.1,17.9 44.5,44 182.3,31.2 250.4,17.9 14.3,-3.1 28.2,-11.3 38.9,-10.2 25.6,2.6 42.5,32.3 64.5,36.4 -78.3,35.3 -229.9,52.2 -340.5,30.7 -28.7,-5.6 -78.3,-21 -79.9,-44 -2.6,-30.7 49.2,-43.5 71.7,-48.6zM367.6,732.7c8.2,2.6 -3.1,7.2 -2.6,10.2 23.6,41 139.8,26.1 198.7,12.8 11.8,-3.1 23.6,-11.3 33.8,-10.2 29.7,2 41.5,33.3 67.1,38.9 -82.4,50.2 -281.6,70.7 -364,7.7 -4.1,-46.1 32.8,-51.2 67.1,-59.4z" />
|
||||
<path
|
||||
android:fillColor="#2365C4"
|
||||
android:pathData="M292.9,810c-24.6,6.1 -88.1,-2.6 -90.1,30.7 -1,12.8 21.5,28.2 36.4,33.8 84,31.7 252.9,36.9 392.2,20.5 64.5,-7.7 185.9,-29.2 170.5,-95.2 19.5,2.6 36.9,14.8 38.9,33.8 7.7,71.2 -155.6,101.4 -221.7,108.5 -143.9,15.9 -323.1,12.8 -433.7,-25.6 -35.8,-12.3 -79.4,-35.3 -77.3,-69.6 2,-58.4 141.3,-74.2 184.8,-36.9z" />
|
||||
<path
|
||||
android:fillColor="#2365C4"
|
||||
android:pathData="M512,1024c-96.8,-10.8 -190,-24.6 -268.3,-59.4 205.3,49.2 504.3,45.6 647.7,-59.4 7.7,-5.6 14.8,-16.9 25.6,-15.4 -36.4,108.5 -175.1,115.7 -293.9,134.1H512z" />
|
||||
<path
|
||||
android:fillColor="#FF1515"
|
||||
android:pathData="M579.1,0c17.9,16.9 30.7,48.6 30.7,82.4 0,99.8 -106,157.7 -157.2,224.3 -11.3,14.8 -26.1,37.9 -25.6,62 0.5,54.3 56.8,115.2 77.3,159.7 -36.4,-23.6 -79.9,-55.3 -111.1,-92.7 -30.7,-37.4 -62,-97.3 -33.8,-149.5 42.5,-78.3 169.5,-124.9 214,-208.9 11.3,-20.5 20,-51.7 5.6,-77.3z" />
|
||||
<path
|
||||
android:fillColor="#2365C4"
|
||||
android:pathData="M731.1,536.6c53.2,-45.6 143.4,-27.6 146.9,49.2 4.6,89.6 -93.7,140.3 -165.4,144.4 32.8,-31.2 120.3,-81.9 103.4,-154.6 -6.7,-29.7 -43,-47.6 -85,-38.9z" />
|
||||
</vector>
|
9
app/src/main/res/drawable/ic_print.xml
Normal file
9
app/src/main/res/drawable/ic_print.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="150dp"
|
||||
android:height="150dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:pathData="M851.2,957.9L179.2,957.9c-61.9,0 -110.9,-49.1 -110.9,-110.9L68.3,454.4c0,-32 25.6,-55.5 55.5,-55.5h55.5v224c0,32 25.6,55.5 55.5,55.5h558.9c32,0 55.5,-25.6 55.5,-55.5L849.1,398.9h55.5c32,0 55.5,25.6 55.5,55.5v390.4c2.1,64 -46.9,113.1 -108.8,113.1zM738.1,622.9h-448c-32,0 -55.5,-25.6 -55.5,-55.5v-448C234.7,89.6 260.3,64 290.1,64h448c32,0 55.5,25.6 55.5,55.5v448c0,29.9 -23.5,55.5 -55.5,55.5zM657.1,232.5L377.6,232.5c-19.2,0 -32,19.2 -32,32s12.8,25.6 32,32h279.5c19.2,0 32,-12.8 32,-32s-19.2,-32 -32,-32zM657.1,398.9L377.6,398.9c-19.2,0 -32,12.8 -32,32 0,12.8 19.2,25.6 32,25.6h279.5c19.2,0 32,-12.8 32,-25.6 0,-19.2 -19.2,-32 -32,-32z"
|
||||
android:fillColor="#ffffff"/>
|
||||
</vector>
|
402
app/src/main/res/layout/activity_app_errors_detail.xml
Normal file
402
app/src/main/res/layout/activity_app_errors_detail.xml
Normal file
@@ -0,0 +1,402 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout 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:background="@color/colorThemeBackground"
|
||||
android:orientation="vertical"
|
||||
tools:context=".ui.activity.AppErrorsDetailActivity"
|
||||
tools:ignore="UnusedAttribute,ContentDescription,UseCompoundDrawables">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:elevation="0dp"
|
||||
android:gravity="center|start"
|
||||
android:paddingLeft="15dp"
|
||||
android:paddingTop="15dp"
|
||||
android:paddingRight="15dp"
|
||||
android:paddingBottom="15dp">
|
||||
|
||||
<androidx.constraintlayout.utils.widget.ImageFilterView
|
||||
android:id="@+id/title_back_icon"
|
||||
style="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:src="@mipmap/ic_back"
|
||||
android:tint="@color/colorTextGray"
|
||||
android:tooltipText="@string/back" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="2.5dp"
|
||||
android:layout_weight="1"
|
||||
android:singleLine="true"
|
||||
android:text="@string/app_name"
|
||||
android:textColor="@color/colorTextGray"
|
||||
android:textSize="19sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<androidx.constraintlayout.utils.widget.ImageFilterView
|
||||
android:id="@+id/print_icon"
|
||||
android:layout_width="23dp"
|
||||
android:layout_height="23dp"
|
||||
android:layout_marginEnd="15dp"
|
||||
android:src="@drawable/ic_print"
|
||||
android:tint="@color/colorTextGray"
|
||||
android:tooltipText="@string/print_to_logcat" />
|
||||
|
||||
<androidx.constraintlayout.utils.widget.ImageFilterView
|
||||
android:id="@+id/copy_icon"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_marginEnd="15dp"
|
||||
android:src="@drawable/ic_copy"
|
||||
android:tint="@color/colorTextGray"
|
||||
android:tooltipText="@string/copy_error_stack" />
|
||||
|
||||
<androidx.constraintlayout.utils.widget.ImageFilterView
|
||||
android:id="@+id/export_icon"
|
||||
android:layout_width="25dp"
|
||||
android:layout_height="25dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:src="@drawable/ic_export"
|
||||
android:tint="@color/colorTextGray"
|
||||
android:tooltipText="@string/export_to_file" />
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:fadingEdgeLength="10dp"
|
||||
android:fillViewport="true"
|
||||
android:requiresFadingEdge="vertical"
|
||||
android:scrollbars="none">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_marginRight="10dp"
|
||||
android:background="@drawable/bg_permotion_round"
|
||||
android:gravity="center|start"
|
||||
android:padding="10dp">
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="45dp"
|
||||
android:layout_height="45dp"
|
||||
app:cardBackgroundColor="@color/trans"
|
||||
app:cardCornerRadius="10dp"
|
||||
app:cardElevation="0dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/app_icon"
|
||||
android:layout_width="45dp"
|
||||
android:layout_height="45dp" />
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center|start"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:gravity="center|start"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/app_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:textColor="@color/colorTextGray"
|
||||
android:textSize="15sp" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="13dp"
|
||||
android:layout_height="13dp"
|
||||
android:src="@drawable/ic_exception"
|
||||
app:tint="#FFEF5350" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/app_version"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:textColor="@color/colorTextDark"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/error_type_icon"
|
||||
android:layout_width="25dp"
|
||||
android:layout_height="25dp"
|
||||
android:layout_marginEnd="10dp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginRight="10dp"
|
||||
android:background="@drawable/bg_permotion_round"
|
||||
android:gravity="center|start"
|
||||
android:orientation="vertical"
|
||||
android:padding="15dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:gravity="center|start|top"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:text="@string/error_info"
|
||||
android:textColor="@color/colorTextDark"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/error_info_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:lineSpacingExtra="5dp"
|
||||
android:textColor="@color/colorTextGray"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/jvm_error_panel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:gravity="center|start"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:gravity="center|start"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:text="@string/error_type"
|
||||
android:textColor="@color/colorTextDark"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/error_type_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:textColor="#FFEF5350"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:gravity="center|start"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:text="@string/error_file_name"
|
||||
android:textColor="@color/colorTextDark"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/error_file_name_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:textColor="@color/colorTextGray"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:gravity="center|start"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:text="@string/error_throw_class"
|
||||
android:textColor="@color/colorTextDark"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/error_throw_class_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:textColor="@color/colorTextGray"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:gravity="center|start"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:text="@string/error_throw_method"
|
||||
android:textColor="@color/colorTextDark"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/error_throw_method_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:textColor="@color/colorTextGray"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center|start"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:text="@string/error_line_number"
|
||||
android:textColor="@color/colorTextDark"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/error_line_number_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:textColor="@color/colorTextGray"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center|start"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:text="@string/error_record_time"
|
||||
android:textColor="@color/colorTextDark"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/error_record_time_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:textColor="@color/colorTextGray"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/error_stack_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_marginRight="10dp"
|
||||
android:background="@drawable/bg_stack_round"
|
||||
android:lineSpacingExtra="5dp"
|
||||
android:padding="15dp"
|
||||
android:textColor="#B65B57"
|
||||
android:textIsSelectable="true"
|
||||
android:textSize="12sp"
|
||||
android:typeface="monospace" />
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
</LinearLayout>
|
BIN
app/src/main/res/mipmap-xxhdpi/ic_back.png
Normal file
BIN
app/src/main/res/mipmap-xxhdpi/ic_back.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.5 KiB |
@@ -13,4 +13,19 @@
|
||||
<string name="ignore_if_restart_tip">システイムが再起動するまで、「%1$s」のエラーを無視します</string>
|
||||
<string name="ignore_if_unlock_tip">ロックが解除されるまで、「%1$s」のエラーを無視します</string>
|
||||
<string name="back">戻る</string>
|
||||
<string name="copy_error_stack">例外スタックをコピーする</string>
|
||||
<string name="export_to_file">ファイルにエクスポート</string>
|
||||
<string name="error_info">例外情報</string>
|
||||
<string name="error_type">例外タイプ</string>
|
||||
<string name="error_file_name">ファイル名</string>
|
||||
<string name="error_throw_class">投擲クラス</string>
|
||||
<string name="error_throw_method">投擲メソッド</string>
|
||||
<string name="error_line_number">行番号</string>
|
||||
<string name="error_record_time">記録タイム</string>
|
||||
<string name="copied">コピーしました</string>
|
||||
<string name="copy_fail">コピーに失敗しました</string>
|
||||
<string name="print_to_logcat">コンソールに印刷</string>
|
||||
<string name="print_to_logcat_success">コンソールに印刷されていました</string>
|
||||
<string name="output_stack_success">エクスポートされた例外スタックされていました</string>
|
||||
<string name="output_stack_fail">例外スタックのエクスポートに失敗しました</string>
|
||||
</resources>
|
6
app/src/main/res/values-night/color.xml
Normal file
6
app/src/main/res/values-night/color.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorThemeBackground">#FF2D2D2D</color>
|
||||
<color name="colorTextDark">#FFCFCFCF</color>
|
||||
<color name="colorTextGray">#FFD3D3D3</color>
|
||||
</resources>
|
16
app/src/main/res/values-night/themes.xml
Normal file
16
app/src/main/res/values-night/themes.xml
Normal file
@@ -0,0 +1,16 @@
|
||||
<resources>
|
||||
<!-- Base application theme. -->
|
||||
<style name="Theme.AppErrorsTracking" parent="Theme.Material3.DayNight">
|
||||
<!-- Primary brand color. -->
|
||||
<item name="colorPrimary">@color/purple_200</item>
|
||||
<item name="colorPrimaryVariant">@color/purple_700</item>
|
||||
<item name="colorOnPrimary">@color/black</item>
|
||||
<!-- Secondary brand color. -->
|
||||
<item name="colorSecondary">@color/teal_200</item>
|
||||
<item name="colorSecondaryVariant">@color/teal_200</item>
|
||||
<item name="colorOnSecondary">@color/black</item>
|
||||
<!-- Status bar color. -->
|
||||
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
|
||||
<!-- Customize your theme here. -->
|
||||
</style>
|
||||
</resources>
|
@@ -13,4 +13,19 @@
|
||||
<string name="ignore_if_unlock_tip">忽略“%1$s”的错误直到设备重新解锁</string>
|
||||
<string name="ignore_if_restart_tip">忽略“%1$s”的错误直到设备重新启动</string>
|
||||
<string name="back">返回</string>
|
||||
<string name="copy_error_stack">复制异常堆栈</string>
|
||||
<string name="export_to_file">导出到文件</string>
|
||||
<string name="error_info">异常信息</string>
|
||||
<string name="error_type">异常类型</string>
|
||||
<string name="error_file_name">文件名</string>
|
||||
<string name="error_throw_class">抛出类</string>
|
||||
<string name="error_throw_method">抛出方法</string>
|
||||
<string name="error_line_number">行号</string>
|
||||
<string name="error_record_time">记录时间</string>
|
||||
<string name="copied">已复制</string>
|
||||
<string name="copy_fail">复制失败</string>
|
||||
<string name="print_to_logcat">打印到控制台</string>
|
||||
<string name="print_to_logcat_success">已打印到控制台</string>
|
||||
<string name="output_stack_success">已导出异常堆栈</string>
|
||||
<string name="output_stack_fail">导出异常堆栈失败</string>
|
||||
</resources>
|
@@ -13,4 +13,19 @@
|
||||
<string name="ignore_if_unlock_tip">忽略“%1$s”的錯誤直到設備重新開屏</string>
|
||||
<string name="ignore_if_restart_tip">忽略“%1$s”的錯誤直到設備重新開機</string>
|
||||
<string name="back">回退</string>
|
||||
<string name="copy_error_stack">複製異常堆棧</string>
|
||||
<string name="export_to_file">導出到副案</string>
|
||||
<string name="error_info">異常訊息</string>
|
||||
<string name="error_type">異常類型</string>
|
||||
<string name="error_file_name">文件名</string>
|
||||
<string name="error_throw_class">拋出 Class</string>
|
||||
<string name="error_throw_method">抛出 Method</string>
|
||||
<string name="error_line_number">行號</string>
|
||||
<string name="error_record_time">記錄時間</string>
|
||||
<string name="copied">已復制</string>
|
||||
<string name="copy_fail">複製失敗</string>
|
||||
<string name="print_to_logcat">打印到控制台</string>
|
||||
<string name="print_to_logcat_success">已打印到控制台</string>
|
||||
<string name="output_stack_success">已導出異常堆棧</string>
|
||||
<string name="output_stack_fail">導出異常堆棧失敗</string>
|
||||
</resources>
|
@@ -13,4 +13,19 @@
|
||||
<string name="ignore_if_unlock_tip">忽略“%1$s”的錯誤直到設備重新開屏</string>
|
||||
<string name="ignore_if_restart_tip">忽略“%1$s”的錯誤直到設備重新開機</string>
|
||||
<string name="back">回退</string>
|
||||
<string name="copy_error_stack">複製異常堆棧</string>
|
||||
<string name="export_to_file">導出到副案</string>
|
||||
<string name="error_info">異常訊息</string>
|
||||
<string name="error_type">異常類型</string>
|
||||
<string name="error_file_name">文件名</string>
|
||||
<string name="error_throw_class">拋出 Class</string>
|
||||
<string name="error_throw_method">抛出 Method</string>
|
||||
<string name="error_line_number">行號</string>
|
||||
<string name="error_record_time">記錄時間</string>
|
||||
<string name="copied">已復制</string>
|
||||
<string name="copy_fail">複製失敗</string>
|
||||
<string name="print_to_logcat">打印到控制台</string>
|
||||
<string name="print_to_logcat_success">已打印到控制台</string>
|
||||
<string name="output_stack_success">已導出異常堆棧</string>
|
||||
<string name="output_stack_fail">導出異常堆棧失敗</string>
|
||||
</resources>
|
@@ -13,4 +13,19 @@
|
||||
<string name="ignore_if_unlock_tip">忽略“%1$s”的錯誤直到設備重新開屏</string>
|
||||
<string name="ignore_if_restart_tip">忽略“%1$s”的錯誤直到設備重新開機</string>
|
||||
<string name="back">回退</string>
|
||||
<string name="copy_error_stack">複製異常堆棧</string>
|
||||
<string name="export_to_file">導出到副案</string>
|
||||
<string name="error_info">異常訊息</string>
|
||||
<string name="error_type">異常類型</string>
|
||||
<string name="error_file_name">文件名</string>
|
||||
<string name="error_throw_class">拋出 Class</string>
|
||||
<string name="error_throw_method">抛出 Method</string>
|
||||
<string name="error_line_number">行號</string>
|
||||
<string name="error_record_time">記錄時間</string>
|
||||
<string name="copied">已復制</string>
|
||||
<string name="copy_fail">複製失敗</string>
|
||||
<string name="print_to_logcat">打印到控制台</string>
|
||||
<string name="print_to_logcat_success">已打印到控制台</string>
|
||||
<string name="output_stack_success">已導出異常堆棧</string>
|
||||
<string name="output_stack_fail">導出異常堆棧失敗</string>
|
||||
</resources>
|
6
app/src/main/res/values/color.xml
Normal file
6
app/src/main/res/values/color.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorThemeBackground">#FFFFFFFF</color>
|
||||
<color name="colorTextDark">#FF777777</color>
|
||||
<color name="colorTextGray">#FF323B42</color>
|
||||
</resources>
|
11
app/src/main/res/values/colors.xml
Normal file
11
app/src/main/res/values/colors.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="purple_200">#656565</color>
|
||||
<color name="purple_500">#656565</color>
|
||||
<color name="purple_700">#656565</color>
|
||||
<color name="teal_200">#656565</color>
|
||||
<color name="teal_700">#656565</color>
|
||||
<color name="black">#FF000000</color>
|
||||
<color name="white">#FFFFFFFF</color>
|
||||
<color name="trans">#00000000</color>
|
||||
</resources>
|
@@ -12,4 +12,19 @@
|
||||
<string name="ignore_if_unlock_tip">Ignore errors for \'%1$s\' until device is re-unlocked</string>
|
||||
<string name="ignore_if_restart_tip">Ignore errors for \'%1$s\' until device reboots</string>
|
||||
<string name="back">Back</string>
|
||||
<string name="copy_error_stack">Copy error stack</string>
|
||||
<string name="export_to_file">Export to file</string>
|
||||
<string name="error_info">Error Info</string>
|
||||
<string name="error_type">Error Type</string>
|
||||
<string name="error_file_name">File Name</string>
|
||||
<string name="error_throw_class">Throw Class</string>
|
||||
<string name="error_throw_method">Throw Method</string>
|
||||
<string name="error_line_number">Line Number</string>
|
||||
<string name="error_record_time">Record Time</string>
|
||||
<string name="copied">Copied</string>
|
||||
<string name="copy_fail">Copy failed</string>
|
||||
<string name="print_to_logcat">Print to logcat</string>
|
||||
<string name="print_to_logcat_success">Printed to logcat</string>
|
||||
<string name="output_stack_success">Export exception stack succeeded</string>
|
||||
<string name="output_stack_fail">Failed to export exception stack</string>
|
||||
</resources>
|
@@ -1,4 +1,16 @@
|
||||
<resources>
|
||||
<!-- Base application theme. -->
|
||||
<style name="Theme.AppErrorsTracking" parent="android:Theme.DeviceDefault" />
|
||||
<style name="Theme.AppErrorsTracking" parent="Theme.Material3.DayNight">
|
||||
<!-- Primary brand color. -->
|
||||
<item name="colorPrimary">@color/purple_500</item>
|
||||
<item name="colorPrimaryVariant">@color/purple_700</item>
|
||||
<item name="colorOnPrimary">@color/teal_700</item>
|
||||
<!-- Secondary brand color. -->
|
||||
<item name="colorSecondary">@color/teal_200</item>
|
||||
<item name="colorSecondaryVariant">@color/teal_700</item>
|
||||
<item name="colorOnSecondary">@color/black</item>
|
||||
<!-- Status bar color. -->
|
||||
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
|
||||
<!-- Customize your theme here. -->
|
||||
</style>
|
||||
</resources>
|
Reference in New Issue
Block a user