Skip to content

Commit

Permalink
feat: add stack trace share optional dialog
Browse files Browse the repository at this point in the history
  • Loading branch information
fankes committed Nov 3, 2023
1 parent 2d42581 commit a64ad86
Show file tree
Hide file tree
Showing 11 changed files with 257 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -183,49 +183,80 @@ data class AppErrorsInfoBean(

/**
* 获取异常堆栈分享模板
* @param sDeviceBrand
* @param sDeviceModel
* @param sDisplay
* @param sPackageName
* @return [String]
*/
val stackOutputShareContent
get() = """
fun stackOutputShareContent(
sDeviceBrand: Boolean = true,
sDeviceModel: Boolean = true,
sDisplay: Boolean = true,
sPackageName: Boolean = true
) = """
Generated by AppErrorsTracking $ModuleVersion
Project URL: https://github.com/KitsunePie/AppErrorsTracking
===============
""".trimIndent() + "\n$environmentInfo"
""".trimIndent() + "\n${environmentInfo(sDeviceBrand, sDeviceModel, sDisplay, sPackageName)}"

/**
* 获取异常堆栈文件模板
* @param sDeviceBrand
* @param sDeviceModel
* @param sDisplay
* @param sPackageName
* @return [String]
*/
val stackOutputFileContent
get() = """
fun stackOutputFileContent(
sDeviceBrand: Boolean = true,
sDeviceModel: Boolean = true,
sDisplay: Boolean = true,
sPackageName: Boolean = true
) = """
================================================================
Generated by AppErrorsTracking $ModuleVersion
Project URL: https://github.com/KitsunePie/AppErrorsTracking
================================================================
""".trimIndent() + "\n$environmentInfo"
""".trimIndent() + "\n${environmentInfo(sDeviceBrand, sDeviceModel, sDisplay, sPackageName)}"

/**
* 获取运行环境信息
* @param sDeviceBrand
* @param sDeviceModel
* @param sDisplay
* @param sPackageName
* @return [String]
*/
private val environmentInfo
get() = """
[Device Brand]: ${Build.BRAND}
[Device Model]: ${Build.MODEL}
[Display]: ${Build.DISPLAY}
private fun environmentInfo(
sDeviceBrand: Boolean,
sDeviceModel: Boolean,
sDisplay: Boolean,
sPackageName: Boolean
) = """
[Device Brand]: ${Build.BRAND.by(sDeviceBrand)}
[Device Model]: ${Build.MODEL.by(sDeviceModel)}
[Display]: ${Build.DISPLAY.by(sDisplay)}
[Android Version]: ${Build.VERSION.RELEASE}
[Android API Level]: ${Build.VERSION.SDK_INT}
[System Locale]: ${Locale.getDefault()}
[Process ID]: $pid
[User ID]: $userId
[CPU ABI]: ${cpuAbi.ifBlank { "none" }}
[Package Name]: $packageName
[Package Name]: ${packageName.by(sPackageName)}
[Version Name]: ${versionName.ifBlank { "unknown" }}
[Version Code]: ${versionCode.takeIf { it != -1L } ?: "unknown"}
[Target SDK]: ${targetSdk.takeIf { it != -1 } ?: "unknown"}
[Min SDK]: ${minSdk.takeIf { it != -1 } ?: "unknown"}
[Error Type]: ${if (isNativeCrash) "Native" else "JVM"}
[Crash Time]: $utcTime
[Stack Trace]:
""".trimIndent() + "\n$stackTrace"
""".trimIndent() + "\n$stackTrace"

/**
* 判断字符串是否需要显示
* @param isDisplay 是否需要显示
* @return [String]
*/
private fun String.by(isDisplay: Boolean) = if (isDisplay) this else "***"
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import com.fankes.apperrorstracking.utils.factory.navigate
import com.fankes.apperrorstracking.utils.factory.openSelfSetting
import com.fankes.apperrorstracking.utils.factory.showDialog
import com.fankes.apperrorstracking.utils.factory.toast
import com.fankes.apperrorstracking.utils.tool.StackTraceShareHelper
import com.highcapable.yukihookapi.hook.log.loggerE

class AppErrorsDetailActivity : BaseActivity<ActivityAppErrorsDetailBinding>() {
Expand Down Expand Up @@ -121,22 +122,31 @@ class AppErrorsDetailActivity : BaseActivity<ActivityAppErrorsDetailBinding>() {
loggerE(msg = appErrorsInfo.stackTrace)
toast(locale.printToLogcatSuccess)
}
binding.copyIcon.setOnClickListener { copyToClipboard(appErrorsInfo.stackOutputShareContent) }
binding.copyIcon.setOnClickListener {
StackTraceShareHelper.showChoose(context = this, locale.copyErrorStack) { sDeviceBrand, sDeviceModel, sDisplay, sPackageName ->
copyToClipboard(appErrorsInfo.stackOutputShareContent(sDeviceBrand, sDeviceModel, sDisplay, sPackageName))
}
}
binding.exportIcon.setOnClickListener {
stackTrace = appErrorsInfo.stackOutputFileContent
runCatching {
startActivityForResult(Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "*/application"
putExtra(Intent.EXTRA_TITLE, "${appErrorsInfo.packageName}_${appErrorsInfo.utcTime}.log")
}, WRITE_REQUEST_CODE)
}.onFailure { toast(msg = "Start Android SAF failed") }
StackTraceShareHelper.showChoose(context = this, locale.exportToFile) { sDeviceBrand, sDeviceModel, sDisplay, sPackageName ->
stackTrace = appErrorsInfo.stackOutputFileContent(sDeviceBrand, sDeviceModel, sDisplay, sPackageName)
runCatching {
startActivityForResult(Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "*/application"
val packageName = if (sPackageName) appErrorsInfo.packageName else "anonymous"
putExtra(Intent.EXTRA_TITLE, "${packageName}_${appErrorsInfo.utcTime}.log")
}, WRITE_REQUEST_CODE)
}.onFailure { toast(msg = "Start Android SAF failed") }
}
}
binding.shareIcon.setOnClickListener {
startActivity(Intent.createChooser(Intent(Intent.ACTION_SEND).apply {
type = "text/plain"
putExtra(Intent.EXTRA_TEXT, appErrorsInfo.stackOutputShareContent)
}, locale.shareErrorStack))
StackTraceShareHelper.showChoose(context = this, locale.shareErrorStack) { sDeviceBrand, sDeviceModel, sDisplay, sPackageName ->
startActivity(Intent.createChooser(Intent(Intent.ACTION_SEND).apply {
type = "text/plain"
putExtra(Intent.EXTRA_TEXT, appErrorsInfo.stackOutputShareContent(sDeviceBrand, sDeviceModel, sDisplay, sPackageName))
}, locale.shareErrorStack))
}
}
binding.appIcon.setImageDrawable(appIconOf(appErrorsInfo.packageName))
binding.appNameText.text = appNameOf(appErrorsInfo.packageName).ifBlank { appErrorsInfo.packageName }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import com.fankes.apperrorstracking.utils.factory.showDialog
import com.fankes.apperrorstracking.utils.factory.toUtcTime
import com.fankes.apperrorstracking.utils.factory.toast
import com.fankes.apperrorstracking.utils.tool.FrameworkTool
import com.fankes.apperrorstracking.utils.tool.StackTraceShareHelper
import com.fankes.apperrorstracking.utils.tool.ZipFileTool
import com.fankes.apperrorstracking.wrapper.BuildConfigWrapper
import java.io.File
Expand Down Expand Up @@ -176,20 +177,24 @@ class AppErrorsRecordActivity : BaseActivity<ActivityAppErrorsRecordBinding>() {
/** 打包导出全部 */
private fun exportAll() {
clearAllExportTemp()
("${cacheDir.absolutePath}/temp").also { path ->
File(path).mkdirs()
listData.takeIf { it.isNotEmpty() }?.forEach {
File("$path/${it.packageName}_${it.utcTime}.log").writeText(it.stackOutputFileContent)
StackTraceShareHelper.showChoose(context = this, locale.exportAll) { sDeviceBrand, sDeviceModel, sDisplay, sPackageName ->
("${cacheDir.absolutePath}/temp").also { path ->
File(path).mkdirs()
listData.takeIf { it.isNotEmpty() }?.forEachIndexed { index, bean ->
val packageName = if (sPackageName) bean.packageName else "anonymous_$index"
File("$path/${packageName}_${bean.utcTime}.log")
.writeText(bean.stackOutputFileContent(sDeviceBrand, sDeviceModel, sDisplay, sPackageName))
}
outPutFilePath = "${cacheDir.absolutePath}/temp_${System.currentTimeMillis()}.zip"
ZipFileTool.zipMultiFile(path, outPutFilePath)
runCatching {
startActivityForResult(Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "*/application"
putExtra(Intent.EXTRA_TITLE, "app_errors_info_${System.currentTimeMillis().toUtcTime()}.zip")
}, WRITE_REQUEST_CODE)
}.onFailure { toast(msg = "Start Android SAF failed") }
}
outPutFilePath = "${cacheDir.absolutePath}/temp_${System.currentTimeMillis()}.zip"
ZipFileTool.zipMultiFile(path, outPutFilePath)
runCatching {
startActivityForResult(Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "*/application"
putExtra(Intent.EXTRA_TITLE, "app_errors_info_${System.currentTimeMillis().toUtcTime()}.zip")
}, WRITE_REQUEST_CODE)
}.onFailure { toast(msg = "Start Android SAF failed") }
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* AppErrorsTracking - Added more features to app's crash dialog, fixed custom rom deleted dialog, the best experience to Android developer.
* Copyright (C) 2017-2023 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 2023/11/3.
*/
package com.fankes.apperrorstracking.utils.tool

import android.content.Context
import com.fankes.apperrorstracking.databinding.DiaStackTraceShareBinding
import com.fankes.apperrorstracking.utils.factory.showDialog

/**
* 异常堆栈分享工具类
*/
object StackTraceShareHelper {

/**
* 显示分享选择器
* @param context 当前实例
* @param title 对话框标题
* @param onChoose 回调选择的结果
*/
fun showChoose(
context: Context,
title: String,
onChoose: (sDeviceBrand: Boolean, sDeviceModel: Boolean, sDisplay: Boolean, sPackageName: Boolean) -> Unit
) {
context.showDialog<DiaStackTraceShareBinding> {
this.title = title
confirmButton {
val sDeviceBrand = binding.configCheck0.isChecked
val sDeviceModel = binding.configCheck1.isChecked
val sDisplay = binding.configCheck2.isChecked
val sPackageName = binding.configCheck3.isChecked
onChoose(sDeviceBrand, sDeviceModel, sDisplay, sPackageName)
cancel()
}
cancelButton()
}
}
}
73 changes: 73 additions & 0 deletions module-app/src/main/res/layout/dia_stack_trace_share.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?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="wrap_content"
android:orientation="vertical"
android:paddingLeft="15dp"
android:paddingTop="15dp"
android:paddingRight="15dp"
tools:ignore="HardcodedText">

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="8.5dp"
android:layout_marginRight="8.5dp"
android:layout_marginBottom="15dp"
android:lineSpacingExtra="6dp"
android:text="@string/stack_trace_share_top_tip"
android:textSize="13sp" />

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="8.5dp"
android:layout_marginRight="8.5dp"
android:layout_marginBottom="10dp"
android:lineSpacingExtra="6dp"
android:text="@string/stack_trace_share_bottom_tip"
android:textColor="#FFFF5722"
android:textSize="13sp" />

<com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/config_check_0"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:checked="true"
android:text="@string/stack_trace_share_device_brand"
app:buttonTint="@color/colorPrimaryAccent" />

<com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/config_check_1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:checked="true"
android:text="@string/stack_trace_share_device_model"
app:buttonTint="@color/colorPrimaryAccent" />

<com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/config_check_2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:checked="true"
android:text="@string/stack_trace_share_system_build_id"
app:buttonTint="@color/colorPrimaryAccent" />

<com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/config_check_3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:checked="true"
android:text="@string/stack_trace_share_package_name"
app:buttonTint="@color/colorPrimaryAccent" />

<com.google.android.material.checkbox.MaterialCheckBox
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:checked="true"
android:enabled="false"
android:text="@string/stack_trace_share_other"
app:buttonTint="@color/colorPrimaryAccent" />
</LinearLayout>
7 changes: 7 additions & 0 deletions module-app/src/main/res/values-ja/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -148,4 +148,11 @@
<string name="record_count">%1$s の合計紀錄</string>
<string name="ci_notice_dialog_title">CI 自動ビルド手順</string>
<string name="ci_notice_dialog_content">コミット ID %1$s の CI 自動ビルドを使用しています。\n\nコードが送信された後、自動的にトリガーされてビルドされ、自動的にコンパイルされてリリースされますが、安定性についてはテストされていません。ご自身の責任で使用してください。</string>
<string name="stack_trace_share_top_tip">共有したいコンテンツを選択すると、表示したくないコンテンツの一部を非表示にすることができます。</string>
<string name="stack_trace_share_bottom_tip">システム言語地域、バージョン、アプリケーションのバージョン、およびエラー ログは、非表示にしてはいけないコンテンツとして共有する必要があります。これらのコンテンツは、開発者が問題を特定するのに役立ちます。</string>
<string name="stack_trace_share_device_brand">デバイスのブランド</string>
<string name="stack_trace_share_device_model">デバイスモデル</string>
<string name="stack_trace_share_system_build_id">システムビルドID</string>
<string name="stack_trace_share_package_name">アプリのパッケージ名</string>
<string name="stack_trace_share_other">その他の要件</string>
</resources>
7 changes: 7 additions & 0 deletions module-app/src/main/res/values-zh-rCN/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -148,4 +148,11 @@
<string name="record_count">共 %1$s 条记录</string>
<string name="ci_notice_dialog_title">CI 自动构建说明</string>
<string name="ci_notice_dialog_content">你正在使用的是 CI 自动构建版本,Commit ID 为 %1$s。\n\n它是由代码提交后自动触发并构建、自动编译发布的,并未经任何稳定性测试,使用风险自负。</string>
<string name="stack_trace_share_top_tip">选择你想要分享的内容,你可以隐藏部分你不想展示的内容。</string>
<string name="stack_trace_share_bottom_tip">系统语言区域、版本、应用版本和错误日志等作为必须分享的内容不能被隐藏,这些内容都有助于帮助开发者定位问题。</string>
<string name="stack_trace_share_device_brand">设备制造商</string>
<string name="stack_trace_share_device_model">设备型号</string>
<string name="stack_trace_share_system_build_id">系统构建 ID</string>
<string name="stack_trace_share_package_name">APP 包名</string>
<string name="stack_trace_share_other">其它必要信息</string>
</resources>
7 changes: 7 additions & 0 deletions module-app/src/main/res/values-zh-rHK/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -148,4 +148,11 @@
<string name="record_count">共 %1$s 條紀錄</string>
<string name="ci_notice_dialog_title">CI 自動建置說明</string>
<string name="ci_notice_dialog_content">你正在使用的是 CI 自動建置版本,Commit ID 為 %1$s。\n\n它是由程式碼提交後自動觸發並建置、自動編譯發布的,並未經任何穩定性測試,使用風險自負。</string>
<string name="stack_trace_share_top_tip">選擇您要分享的內容,您可以隱藏一些您不想顯示的內容。</string>
<string name="stack_trace_share_bottom_tip">系統語言區域、版本、應用程式版本和錯誤日誌必須作為內容共享,且不得隱藏,這些內容將幫助開發人員定位問題。</string>
<string name="stack_trace_share_device_brand">設備品牌</string>
<string name="stack_trace_share_device_model">設備型號</string>
<string name="stack_trace_share_system_build_id">系統建置 ID</string>
<string name="stack_trace_share_package_name">應用程式包名稱</string>
<string name="stack_trace_share_other">其它必要資訊</string>
</resources>
7 changes: 7 additions & 0 deletions module-app/src/main/res/values-zh-rMO/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -148,4 +148,11 @@
<string name="record_count">共 %1$s 條紀錄</string>
<string name="ci_notice_dialog_title">CI 自動建置說明</string>
<string name="ci_notice_dialog_content">你正在使用的是 CI 自動建置版本,Commit ID 為 %1$s。\n\n它是由程式碼提交後自動觸發並建置、自動編譯發布的,並未經任何穩定性測試,使用風險自負。</string>
<string name="stack_trace_share_top_tip">選擇您要分享的內容,您可以隱藏一些您不想顯示的內容。</string>
<string name="stack_trace_share_bottom_tip">系統語言區域、版本、應用程式版本和錯誤日誌必須作為內容共享,且不得隱藏,這些內容將幫助開發人員定位問題。</string>
<string name="stack_trace_share_device_brand">設備品牌</string>
<string name="stack_trace_share_device_model">設備型號</string>
<string name="stack_trace_share_system_build_id">系統建置 ID</string>
<string name="stack_trace_share_package_name">應用程式包名稱</string>
<string name="stack_trace_share_other">其它必要資訊</string>
</resources>
Loading

0 comments on commit a64ad86

Please sign in to comment.