diff --git a/README.md b/README.md index caa1de4..798fa90 100644 --- a/README.md +++ b/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 此项目依然在开发中,现在未解决的问题和包含的问题如下 -- “错误详情”按钮现在是无效的,还在开发 +- 异常历史记录和排除列表正在开发 - 后台进程可能依然会弹出崩溃对话框且开发者选项里的设置无效,还在排查 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c3c58d7..8c85c35 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,7 +1,13 @@ - + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/fankes/apperrorstracking/application/AppErrorsApplication.kt b/app/src/main/java/com/fankes/apperrorstracking/application/AppErrorsApplication.kt new file mode 100644 index 0000000..731b3fa --- /dev/null +++ b/app/src/main/java/com/fankes/apperrorstracking/application/AppErrorsApplication.kt @@ -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 + * + * + * 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) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/fankes/apperrorstracking/bean/AppErrorsInfoBean.kt b/app/src/main/java/com/fankes/apperrorstracking/bean/AppErrorsInfoBean.kt new file mode 100644 index 0000000..419b0bd --- /dev/null +++ b/app/src/main/java/com/fankes/apperrorstracking/bean/AppErrorsInfoBean.kt @@ -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 + * + * + * 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 \ No newline at end of file diff --git a/app/src/main/java/com/fankes/apperrorstracking/hook/entity/FrameworkHooker.kt b/app/src/main/java/com/fankes/apperrorstracking/hook/entity/FrameworkHooker.kt index a38f97e..acd9f22 100644 --- a/app/src/main/java/com/fankes/apperrorstracking/hook/entity/FrameworkHooker.kt +++ b/app/src/main/java/com/fankes/apperrorstracking/hook/entity/FrameworkHooker.kt @@ -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() + /** 已记录的 APP 异常信息数组 - 直到重新启动 */ + private val appErrorsRecords = arrayListOf() + /** 是否已经注册广播 */ 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() + /** 当前异常信息 */ + args().last().cast()?.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() + ) + ) + } + } + } } } } diff --git a/app/src/main/java/com/fankes/apperrorstracking/ui/activity/AppErrorsDetailActivity.kt b/app/src/main/java/com/fankes/apperrorstracking/ui/activity/AppErrorsDetailActivity.kt new file mode 100644 index 0000000..87ffe14 --- /dev/null +++ b/app/src/main/java/com/fankes/apperrorstracking/ui/activity/AppErrorsDetailActivity.kt @@ -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 + * + * + * 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() { + + 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() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/fankes/apperrorstracking/ui/activity/base/BaseActivity.kt b/app/src/main/java/com/fankes/apperrorstracking/ui/activity/base/BaseActivity.kt new file mode 100644 index 0000000..1c676a5 --- /dev/null +++ b/app/src/main/java/com/fankes/apperrorstracking/ui/activity/base/BaseActivity.kt @@ -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 + * + * + * 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 : 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).method { + name = "inflate" + param(LayoutInflaterClass) + }.get().invoke(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() +} \ No newline at end of file diff --git a/app/src/main/java/com/fankes/apperrorstracking/utils/factory/FunctionFactory.kt b/app/src/main/java/com/fankes/apperrorstracking/utils/factory/FunctionFactory.kt index de8c4f9..3c2ca58 100644 --- a/app/src/main/java/com/fankes/apperrorstracking/utils/factory/FunctionFactory.kt +++ b/app/src/main/java/com/fankes/apperrorstracking/utils/factory/FunctionFactory.kt @@ -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 包名 diff --git a/app/src/main/res/drawable-night/bg_permotion_round.xml b/app/src/main/res/drawable-night/bg_permotion_round.xml new file mode 100644 index 0000000..09cbc15 --- /dev/null +++ b/app/src/main/res/drawable-night/bg_permotion_round.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_button_round.xml b/app/src/main/res/drawable/bg_button_round.xml new file mode 100644 index 0000000..c7bf8cb --- /dev/null +++ b/app/src/main/res/drawable/bg_button_round.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_permotion_round.xml b/app/src/main/res/drawable/bg_permotion_round.xml new file mode 100644 index 0000000..b7d8b2b --- /dev/null +++ b/app/src/main/res/drawable/bg_permotion_round.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_stack_round.xml b/app/src/main/res/drawable/bg_stack_round.xml new file mode 100644 index 0000000..1ad006c --- /dev/null +++ b/app/src/main/res/drawable/bg_stack_round.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_copy.xml b/app/src/main/res/drawable/ic_copy.xml new file mode 100644 index 0000000..bc35d84 --- /dev/null +++ b/app/src/main/res/drawable/ic_copy.xml @@ -0,0 +1,12 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_cpp.xml b/app/src/main/res/drawable/ic_cpp.xml new file mode 100644 index 0000000..a2e83eb --- /dev/null +++ b/app/src/main/res/drawable/ic_cpp.xml @@ -0,0 +1,14 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_exception.xml b/app/src/main/res/drawable/ic_exception.xml new file mode 100644 index 0000000..b35d206 --- /dev/null +++ b/app/src/main/res/drawable/ic_exception.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_export.xml b/app/src/main/res/drawable/ic_export.xml new file mode 100644 index 0000000..dfb0a06 --- /dev/null +++ b/app/src/main/res/drawable/ic_export.xml @@ -0,0 +1,12 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_java.xml b/app/src/main/res/drawable/ic_java.xml new file mode 100644 index 0000000..de6d1d3 --- /dev/null +++ b/app/src/main/res/drawable/ic_java.xml @@ -0,0 +1,26 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_print.xml b/app/src/main/res/drawable/ic_print.xml new file mode 100644 index 0000000..a3c58f2 --- /dev/null +++ b/app/src/main/res/drawable/ic_print.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/activity_app_errors_detail.xml b/app/src/main/res/layout/activity_app_errors_detail.xml new file mode 100644 index 0000000..728c1d7 --- /dev/null +++ b/app/src/main/res/layout/activity_app_errors_detail.xml @@ -0,0 +1,402 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-xxhdpi/ic_back.png b/app/src/main/res/mipmap-xxhdpi/ic_back.png new file mode 100644 index 0000000..49415ff Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_back.png differ diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index ffc50e2..f273652 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -13,4 +13,19 @@ システイムが再起動するまで、「%1$s」のエラーを無視します ロックが解除されるまで、「%1$s」のエラーを無視します 戻る + 例外スタックをコピーする + ファイルにエクスポート + 例外情報 + 例外タイプ + ファイル名 + 投擲クラス + 投擲メソッド + 行番号 + 記録タイム + コピーしました + コピーに失敗しました + コンソールに印刷 + コンソールに印刷されていました + エクスポートされた例外スタックされていました + 例外スタックのエクスポートに失敗しました \ No newline at end of file diff --git a/app/src/main/res/values-night/color.xml b/app/src/main/res/values-night/color.xml new file mode 100644 index 0000000..809ab40 --- /dev/null +++ b/app/src/main/res/values-night/color.xml @@ -0,0 +1,6 @@ + + + #FF2D2D2D + #FFCFCFCF + #FFD3D3D3 + \ No newline at end of file diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml new file mode 100644 index 0000000..0bb12e5 --- /dev/null +++ b/app/src/main/res/values-night/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index f0f5345..5b3c17c 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -13,4 +13,19 @@ 忽略“%1$s”的错误直到设备重新解锁 忽略“%1$s”的错误直到设备重新启动 返回 + 复制异常堆栈 + 导出到文件 + 异常信息 + 异常类型 + 文件名 + 抛出类 + 抛出方法 + 行号 + 记录时间 + 已复制 + 复制失败 + 打印到控制台 + 已打印到控制台 + 已导出异常堆栈 + 导出异常堆栈失败 \ No newline at end of file diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index 24b892f..4381c3e 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -13,4 +13,19 @@ 忽略“%1$s”的錯誤直到設備重新開屏 忽略“%1$s”的錯誤直到設備重新開機 回退 + 複製異常堆棧 + 導出到副案 + 異常訊息 + 異常類型 + 文件名 + 拋出 Class + 抛出 Method + 行號 + 記錄時間 + 已復制 + 複製失敗 + 打印到控制台 + 已打印到控制台 + 已導出異常堆棧 + 導出異常堆棧失敗 \ No newline at end of file diff --git a/app/src/main/res/values-zh-rMO/strings.xml b/app/src/main/res/values-zh-rMO/strings.xml index 95f6a80..2d73e9f 100644 --- a/app/src/main/res/values-zh-rMO/strings.xml +++ b/app/src/main/res/values-zh-rMO/strings.xml @@ -13,4 +13,19 @@ 忽略“%1$s”的錯誤直到設備重新開屏 忽略“%1$s”的錯誤直到設備重新開機 回退 + 複製異常堆棧 + 導出到副案 + 異常訊息 + 異常類型 + 文件名 + 拋出 Class + 抛出 Method + 行號 + 記錄時間 + 已復制 + 複製失敗 + 打印到控制台 + 已打印到控制台 + 已導出異常堆棧 + 導出異常堆棧失敗 \ No newline at end of file diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 620f268..4789ad6 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -13,4 +13,19 @@ 忽略“%1$s”的錯誤直到設備重新開屏 忽略“%1$s”的錯誤直到設備重新開機 回退 + 複製異常堆棧 + 導出到副案 + 異常訊息 + 異常類型 + 文件名 + 拋出 Class + 抛出 Method + 行號 + 記錄時間 + 已復制 + 複製失敗 + 打印到控制台 + 已打印到控制台 + 已導出異常堆棧 + 導出異常堆棧失敗 \ No newline at end of file diff --git a/app/src/main/res/values/color.xml b/app/src/main/res/values/color.xml new file mode 100644 index 0000000..24917f9 --- /dev/null +++ b/app/src/main/res/values/color.xml @@ -0,0 +1,6 @@ + + + #FFFFFFFF + #FF777777 + #FF323B42 + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..0df5e08 --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,11 @@ + + + #656565 + #656565 + #656565 + #656565 + #656565 + #FF000000 + #FFFFFFFF + #00000000 + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d40b09d..9e61d6c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -12,4 +12,19 @@ Ignore errors for \'%1$s\' until device is re-unlocked Ignore errors for \'%1$s\' until device reboots Back + Copy error stack + Export to file + Error Info + Error Type + File Name + Throw Class + Throw Method + Line Number + Record Time + Copied + Copy failed + Print to logcat + Printed to logcat + Export exception stack succeeded + Failed to export exception stack \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index b1f8a06..581e416 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -1,4 +1,16 @@ - \ No newline at end of file