Added export all errors record function

This commit is contained in:
2022-05-13 02:27:57 +08:00
parent 7611445b56
commit 786d46069c
9 changed files with 230 additions and 5 deletions

View File

@@ -202,4 +202,16 @@ object LocaleString {
/** @string Automatic generated */
fun areYouSureExportAllErrors(vararg objArrs: Any) = R.string.are_you_sure_export_all_errors.bind(*objArrs)
/** @string Automatic generated */
val exportAllErrorsSuccess get() = exportAllErrorsSuccess()
/** @string Automatic generated */
fun exportAllErrorsSuccess(vararg objArrs: Any) = R.string.export_all_errors_success.bind(*objArrs)
/** @string Automatic generated */
val exportAllErrorsFail get() = exportAllErrorsFail()
/** @string Automatic generated */
fun exportAllErrorsFail(vararg objArrs: Any) = R.string.export_all_errors_fail.bind(*objArrs)
}

View File

@@ -19,8 +19,12 @@
*
* This file is Created by fankes on 2022/5/11.
*/
@file:Suppress("DEPRECATION", "OVERRIDE_DEPRECATION")
package com.fankes.apperrorstracking.ui.activity
import android.app.Activity
import android.content.Intent
import android.view.*
import android.widget.AdapterView.AdapterContextMenuInfo
import android.widget.BaseAdapter
@@ -33,11 +37,21 @@ import com.fankes.apperrorstracking.locale.LocaleString
import com.fankes.apperrorstracking.ui.activity.base.BaseActivity
import com.fankes.apperrorstracking.utils.factory.*
import com.fankes.apperrorstracking.utils.tool.FrameworkTool
import java.text.SimpleDateFormat
import java.util.*
import com.fankes.apperrorstracking.utils.tool.ZipFileTool
import java.io.File
import java.io.FileInputStream
class AppErrorsRecordActivity : BaseActivity<ActivityAppErrorsRecordBinding>() {
companion object {
/** 请求保存文件回调标识 */
private const val WRITE_REQUEST_CODE = 0
}
/** 当前导出文件的路径 */
private var outPutFilePath = ""
/** 回调适配器改变 */
private var onChanged: (() -> Unit)? = null
@@ -60,8 +74,12 @@ class AppErrorsRecordActivity : BaseActivity<ActivityAppErrorsRecordBinding>() {
}
}
binding.exportAllIcon.setOnClickListener {
// TODO 待实现
toast(msg = "Coming soon")
showDialog {
title = LocaleString.notice
msg = LocaleString.areYouSureExportAllErrors
confirmButton { exportAll() }
cancelButton()
}
}
/** 设置列表元素和 Adapter */
binding.listView.apply {
@@ -84,7 +102,7 @@ class AppErrorsRecordActivity : BaseActivity<ActivityAppErrorsRecordBinding>() {
getItem(position).also {
holder.appIcon.setImageDrawable(appIcon(it.packageName))
holder.appNameText.text = appName(it.packageName)
holder.errorsTimeText.text = SimpleDateFormat.getDateTimeInstance().format(Date(it.timestamp))
holder.errorsTimeText.text = it.time
holder.errorTypeIcon.setImageResource(if (it.isNativeCrash) R.drawable.ic_cpp else R.drawable.ic_java)
holder.errorTypeText.text = if (it.isNativeCrash) "Native crash" else it.exceptionClassName.let { text ->
if (text.contains(other = ".")) text.split(".").let { e -> e[e.lastIndex] } else text
@@ -112,6 +130,32 @@ 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.timestamp}.log").writeText(it.stackOutputContent)
}
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()}.zip")
}, WRITE_REQUEST_CODE)
}.onFailure { toast(msg = "Start Android SAF failed") }
}
}
/** 清空导出的临时文件 */
private fun clearAllExportTemp() {
cacheDir.deleteRecursively()
cacheDir.mkdirs()
}
override fun onCreateContextMenu(menu: ContextMenu?, v: View?, menuInfo: ContextMenu.ContextMenuInfo?) {
menuInflater.inflate(R.menu.menu_list_detail_action, menu)
super.onCreateContextMenu(menu, v, menuInfo)
@@ -128,6 +172,17 @@ class AppErrorsRecordActivity : BaseActivity<ActivityAppErrorsRecordBinding>() {
return super.onContextItemSelected(item)
}
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(FileInputStream(outPutFilePath).readBytes()) }?.close()
clearAllExportTemp()
toast(LocaleString.exportAllErrorsSuccess)
} ?: toast(LocaleString.exportAllErrorsFail)
}.onFailure { toast(LocaleString.exportAllErrorsFail) }
}
override fun onResume() {
super.onResume()
/** 执行更新 */

View File

@@ -0,0 +1,146 @@
/*
* 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/13.
*/
@file:Suppress("unused")
package com.fankes.apperrorstracking.utils.tool
import java.io.*
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
import java.util.zip.ZipOutputStream
/**
* 处理压缩文件
*/
object ZipFileTool {
private const val BUFF_SIZE = 2048
/**
* 压缩整个文件夹中的所有文件 - 生成指定名称的 Zip 压缩包
* @param filePath 文件所在目录
* @param zipPath 压缩后 Zip 文件名称
* @param isDirFlag Zip 文件中第一层是否包含一级目录 - true 包含 false没有
*/
fun zipMultiFile(filePath: String, zipPath: String, isDirFlag: Boolean = false) {
runCatching {
val file = File(filePath)
val zipFile = File(zipPath)
val zipOut = ZipOutputStream(FileOutputStream(zipFile))
if (file.isDirectory) {
val files = file.listFiles() ?: emptyArray()
for (fileSec in files)
if (isDirFlag) recursionZip(zipOut, fileSec, file.name + File.separator)
else recursionZip(zipOut, fileSec, "")
}
zipOut.close()
}
}
/**
* 解压文件
* @param unZipPath 解压后的目录
* @param zipPath 压缩文件目录
*/
fun unZipFile(unZipPath: String, zipPath: String) {
runCatching {
unZipFileByInput(unZipPath, FileInputStream(zipPath))
}
}
/**
* 解压文件
* @param unZipPath 解压后的目录
* @param zips 压缩文件流
*/
private fun unZipFileByInput(unZipPath: String, zips: FileInputStream) {
val path = createSeparator(unZipPath)
var bos: BufferedOutputStream? = null
var zis: ZipInputStream? = null
try {
var filename: String
zis = ZipInputStream(BufferedInputStream(zips))
var ze: ZipEntry
val buffer = ByteArray(BUFF_SIZE)
var count: Int
while (zis.nextEntry.also { ze = it } != null) {
filename = ze.name
createSubFolders(filename, path)
if (ze.isDirectory) {
val fmd = File(path + filename)
fmd.mkdirs()
continue
}
bos = BufferedOutputStream(FileOutputStream(path + filename))
while (zis.read(buffer).also { count = it } != -1) bos.write(buffer, 0, count)
bos.flush()
bos.close()
}
} catch (_: IOException) {
} finally {
runCatching {
if (zis != null) {
zis.closeEntry()
zis.close()
}
bos?.close()
}
}
}
/** @base code */
private fun recursionZip(zipOut: ZipOutputStream, file: File, baseDir: String) {
if (file.isDirectory) {
val files = file.listFiles() ?: emptyArray()
for (fileSec in files) recursionZip(zipOut, fileSec, baseDir + file.name + File.separator)
} else {
val buf = ByteArray(1024)
val input: InputStream = FileInputStream(file)
zipOut.putNextEntry(ZipEntry(baseDir + file.name))
var len: Int
while (input.read(buf).also { len = it } != -1) {
zipOut.write(buf, 0, len)
}
input.close()
}
}
/** @base code */
private fun createSubFolders(filename: String, path: String) {
val subFolders = filename.split("/").toTypedArray()
if (subFolders.size <= 1) return
var pathNow = path
for (i in 0 until subFolders.size - 1) {
pathNow = pathNow + subFolders[i] + "/"
val fmd = File(pathNow)
if (fmd.exists()) continue
fmd.mkdirs()
}
}
/** @base code */
private fun createSeparator(path: String): String {
val dir = File(path)
if (!dir.exists()) dir.mkdirs()
return if (path.endsWith("/")) path else "$path/"
}
}

View File

@@ -40,4 +40,6 @@
<string name="all_errors_clear_success">すべてのアラーレコードがクリアされました</string>
<string name="are_you_sure_export_all_errors">すべてのログファイルをエクスポートしてもよろしいですか? 梱包プロセスには時間がかかる場合があります。</string>
<string name="view_detail">詳細を見る</string>
<string name="export_all_errors_success">すべてのエラーレコードがエクスポートされました</string>
<string name="export_all_errors_fail">すべてのエラーレコードのエクスポートに失敗しました</string>
</resources>

View File

@@ -40,4 +40,6 @@
<string name="all_errors_clear_success">全部异常记录已清空</string>
<string name="are_you_sure_export_all_errors">你确定要导出全部日志文件吗?打包过程可能会花费一点时间。</string>
<string name="view_detail">查看详情</string>
<string name="export_all_errors_success">已导出全部异常记录</string>
<string name="export_all_errors_fail">导出全部异常记录失败</string>
</resources>

View File

@@ -40,4 +40,6 @@
<string name="all_errors_clear_success">全部異常紀錄已清空</string>
<string name="are_you_sure_export_all_errors">你確認要導出全部日誌文件嗎?打包過程可能會花費一點時間。</string>
<string name="view_detail">查看詳情</string>
<string name="export_all_errors_success">已導出全部異常紀錄</string>
<string name="export_all_errors_fail">導出全部異常紀錄失敗</string>
</resources>

View File

@@ -40,4 +40,6 @@
<string name="all_errors_clear_success">全部異常紀錄已清空</string>
<string name="are_you_sure_export_all_errors">你確認要導出全部日誌文件嗎?打包過程可能會花費一點時間。</string>
<string name="view_detail">查看詳情</string>
<string name="export_all_errors_success">已導出全部異常紀錄</string>
<string name="export_all_errors_fail">導出全部異常紀錄失敗</string>
</resources>

View File

@@ -40,4 +40,6 @@
<string name="all_errors_clear_success">全部異常紀錄已清空</string>
<string name="are_you_sure_export_all_errors">你確認要導出全部日誌文件嗎?打包過程可能會花費一點時間。</string>
<string name="view_detail">查看詳情</string>
<string name="export_all_errors_success">已導出全部異常紀錄</string>
<string name="export_all_errors_fail">導出全部異常紀錄失敗</string>
</resources>

View File

@@ -39,4 +39,6 @@
<string name="all_errors_clear_success">All errors records have been cleared</string>
<string name="are_you_sure_export_all_errors">Are you sure you want to export all log files? The packaging process may take a while.</string>
<string name="view_detail">View detail</string>
<string name="export_all_errors_success">All errors record exported</string>
<string name="export_all_errors_fail">Failed to exported all errors record</string>
</resources>