修复了识别追踪信息功能
This commit is contained in:
@@ -2,6 +2,7 @@ package com.sw.platecabinet
|
||||
|
||||
import com.sw.plate.App
|
||||
import com.sw.plate.utils.AppUtil
|
||||
import com.sw.platecabinet.utils.CrashHandler
|
||||
import timber.log.Timber
|
||||
|
||||
class MyApp : App() {
|
||||
@@ -14,6 +15,9 @@ class MyApp : App() {
|
||||
Timber.plant(Timber.DebugTree())
|
||||
Timber.d("初始化")
|
||||
initGlobalData()
|
||||
|
||||
// 初始化崩溃处理器
|
||||
CrashHandler.init(this)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -248,25 +248,37 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
||||
displayOrientation: Int,
|
||||
isMirror: Boolean
|
||||
) {
|
||||
Timber.d("initRgbCamera Rgb onCameraOpened: cameraId = $cameraId, displayOrientation = $displayOrientation")
|
||||
runOnUiThread({
|
||||
val previewSizeRgb = camera.getParameters().getPreviewSize()
|
||||
val layoutParams = adjustPreviewViewSize(
|
||||
binding.dualCameraTexturePreviewRgb,
|
||||
binding.dualCameraTexturePreviewRgb, binding.dualCameraFaceRectView,
|
||||
previewSizeRgb, displayOrientation, 1f
|
||||
previewSizeRgb, displayOrientation, 0.8f
|
||||
)
|
||||
Timber.d("initRgbCamera previewSizeRgb = ${previewSizeRgb.width}-${previewSizeRgb.height}")
|
||||
Timber.d("initRgbCamera layoutParams = ${layoutParams.width}-${layoutParams.height}")
|
||||
Timber.d(
|
||||
"initRgbCamera isMirror = ${isMirror}, isDrawRgbRectHorizontalMirror = ${
|
||||
ConfigUtil.isDrawRgbRectHorizontalMirror(
|
||||
context
|
||||
)
|
||||
}, isDrawRgbRectVerticalMirror = ${
|
||||
ConfigUtil.isDrawRgbRectVerticalMirror(
|
||||
context
|
||||
)
|
||||
}"
|
||||
)
|
||||
rgbFaceRectTransformer = FaceRectTransformer(
|
||||
previewSizeRgb.width,
|
||||
previewSizeRgb.height,
|
||||
layoutParams.width,
|
||||
layoutParams.height,
|
||||
displayOrientation,
|
||||
90,
|
||||
cameraId,
|
||||
isMirror,
|
||||
ConfigUtil.isDrawRgbRectHorizontalMirror(context),
|
||||
ConfigUtil.isDrawRgbRectVerticalMirror(context)
|
||||
true,
|
||||
true
|
||||
)
|
||||
|
||||
recognizeViewModel.onRgbCameraOpened(camera)
|
||||
@@ -289,20 +301,20 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
|
||||
}
|
||||
|
||||
override fun onCameraClosed() {
|
||||
Timber.i("onCameraClosed: ")
|
||||
Timber.i("initRgbCamera onCameraClosed: ")
|
||||
}
|
||||
|
||||
override fun onCameraError(e: java.lang.Exception) {
|
||||
Timber.i("onCameraError: %s", e.message)
|
||||
Timber.i("initRgbCamera onCameraError: %s", e.message)
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
override fun onCameraConfigurationChanged(cameraID: Int, displayOrientation: Int) {
|
||||
Timber.i("onCameraConfigurationChanged: threadName = ${Thread.currentThread().name}")
|
||||
Timber.i("initRgbCamera onCameraConfigurationChanged: threadName = ${Thread.currentThread().name}")
|
||||
if (rgbFaceRectTransformer != null) {
|
||||
rgbFaceRectTransformer!!.cameraDisplayOrientation = displayOrientation
|
||||
}
|
||||
Timber.i("onCameraConfigurationChanged: $cameraID $displayOrientation")
|
||||
Timber.i("initRgbCamera onCameraConfigurationChanged: $cameraID $displayOrientation")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,237 @@
|
||||
package com.sw.platecabinet.utils
|
||||
|
||||
import android.app.ActivityManager
|
||||
import android.app.AlarmManager
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.os.Process
|
||||
import com.sw.platecabinet.activity.InitActivity
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
import java.io.PrintWriter
|
||||
import java.io.StringWriter
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
/**
|
||||
* 崩溃处理
|
||||
*/
|
||||
class CrashHandler private constructor(private val context: Context) :
|
||||
Thread.UncaughtExceptionHandler {
|
||||
|
||||
companion object {
|
||||
private const val TAG = "CrashHandler"
|
||||
private const val CRASH_REPORTS_DIR = "crash_reports"
|
||||
private const val LOG_LINES = 500 // 收集最近500行日志
|
||||
|
||||
@Volatile
|
||||
private var instance: CrashHandler? = null
|
||||
|
||||
fun init(context: Context) {
|
||||
if (instance == null) {
|
||||
synchronized(CrashHandler::class.java) {
|
||||
if (instance == null) {
|
||||
instance = CrashHandler(context.applicationContext)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getCrashReportFiles(context: Context): Array<File> {
|
||||
val crashDir = File(context.getExternalFilesDir(null), CRASH_REPORTS_DIR)
|
||||
return if (crashDir.exists() && crashDir.isDirectory) {
|
||||
crashDir.listFiles { _, name -> name.endsWith(".log") } ?: emptyArray()
|
||||
} else {
|
||||
emptyArray()
|
||||
}
|
||||
}
|
||||
|
||||
fun clearCrashReports(context: Context) {
|
||||
getCrashReportFiles(context).forEach { it.delete() }
|
||||
}
|
||||
}
|
||||
|
||||
private val defaultHandler = Thread.getDefaultUncaughtExceptionHandler()
|
||||
|
||||
init {
|
||||
Thread.setDefaultUncaughtExceptionHandler(this)
|
||||
}
|
||||
|
||||
override fun uncaughtException(thread: Thread, ex: Throwable) {
|
||||
handleException(thread, ex)
|
||||
|
||||
// 如果系统提供了默认的异常处理器,则交给系统去结束程序
|
||||
// 否则自己结束程序
|
||||
defaultHandler?.uncaughtException(thread, ex) ?: run {
|
||||
Process.killProcess(Process.myPid())
|
||||
exitProcess(1)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动重启app
|
||||
*/
|
||||
private fun restartApp() {
|
||||
// 延迟1秒后重启应用
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
val intent = Intent(context, InitActivity::class.java).apply {
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
|
||||
val pendingIntent = PendingIntent.getActivity(
|
||||
context, 0, intent,
|
||||
PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
|
||||
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
||||
alarmManager.set(AlarmManager.RTC, System.currentTimeMillis() + 100, pendingIntent)
|
||||
|
||||
Process.killProcess(Process.myPid())
|
||||
exitProcess(1)
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
private fun handleException(thread: Thread, ex: Throwable) {
|
||||
// 收集设备信息和异常信息
|
||||
val crashInfo = collectCrashInfo(thread, ex)
|
||||
|
||||
// 保存日志文件
|
||||
saveCrashInfoToFile(crashInfo)
|
||||
|
||||
// 这里可以添加其他处理逻辑,比如上传到服务器等
|
||||
}
|
||||
|
||||
private fun collectCrashInfo(thread: Thread, ex: Throwable): String {
|
||||
return buildString {
|
||||
// 收集设备信息
|
||||
collectDeviceInfo(this)
|
||||
|
||||
// 收集应用日志
|
||||
append("\n\n").append(collectLogs())
|
||||
|
||||
// 收集线程和异常信息
|
||||
append("\n\n========== Thread & Exception Info ==========\n")
|
||||
append("Thread: ${thread.name}\n")
|
||||
append("Stack Trace:\n")
|
||||
|
||||
val sw = StringWriter()
|
||||
val pw = PrintWriter(sw)
|
||||
ex.printStackTrace(pw)
|
||||
var cause: Throwable? = ex.cause
|
||||
while (cause != null) {
|
||||
cause.printStackTrace(pw)
|
||||
cause = cause.cause
|
||||
}
|
||||
pw.close()
|
||||
append(sw.toString())
|
||||
}
|
||||
}
|
||||
|
||||
private fun collectDeviceInfo(sb: StringBuilder) {
|
||||
sb.append("========== Device Info ==========\n")
|
||||
try {
|
||||
// 应用信息
|
||||
val pm = context.packageManager
|
||||
val pi = pm.getPackageInfo(context.packageName, 0)
|
||||
|
||||
sb.append("App Version: ${pi.versionName}_${pi.versionCode}\n")
|
||||
|
||||
// Android 版本信息
|
||||
sb.append("OS Version: ${Build.VERSION.RELEASE}_${Build.VERSION.SDK_INT}\n")
|
||||
|
||||
// 设备信息
|
||||
sb.append("Vendor: ${Build.MANUFACTURER}\n")
|
||||
sb.append("Model: ${Build.MODEL}\n")
|
||||
sb.append("CPU ABI: ${Build.SUPPORTED_ABIS[0]}\n")
|
||||
|
||||
// 其他信息
|
||||
sb.append("Locale: ${Locale.getDefault()}\n")
|
||||
sb.append(
|
||||
"Current Time: ${
|
||||
SimpleDateFormat(
|
||||
"yyyy-MM-dd HH:mm:ss",
|
||||
Locale.getDefault()
|
||||
).format(Date())
|
||||
}\n"
|
||||
)
|
||||
|
||||
// 内存信息
|
||||
val memoryInfo = ActivityManager.MemoryInfo()
|
||||
val activityManager =
|
||||
context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
|
||||
activityManager.getMemoryInfo(memoryInfo)
|
||||
sb.append("Available Memory: ${memoryInfo.availMem / (1024 * 1024)}MB\n")
|
||||
sb.append("Total Memory: ${memoryInfo.totalMem / (1024 * 1024)}MB\n")
|
||||
sb.append("Low Memory: ${memoryInfo.lowMemory}\n")
|
||||
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "Error while collecting device info")
|
||||
sb.append("Error while collecting device info: ${e.message}\n")
|
||||
}
|
||||
}
|
||||
|
||||
private fun collectLogs(): String {
|
||||
return buildString {
|
||||
append("========== Application Logs ==========\n")
|
||||
try {
|
||||
val process = Runtime.getRuntime().exec("logcat -d -v threadtime")
|
||||
val reader = process.inputStream.bufferedReader()
|
||||
|
||||
val logLines = reader.readLines()
|
||||
val start = kotlin.comparisons.maxOf(0, logLines.size - LOG_LINES)
|
||||
|
||||
logLines.subList(start, logLines.size).forEach {
|
||||
append(it).append("\n")
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
Timber.e(e, "Error collecting logs")
|
||||
append("Error collecting logs: ${e.message}\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveCrashInfoToFile(crashInfo: String) {
|
||||
try {
|
||||
val time = SimpleDateFormat("yyyy-MM-dd_HH-mm-ss", Locale.getDefault()).format(Date())
|
||||
val fileName = "crash_$time.log"
|
||||
|
||||
val crashDir = File(context.getExternalFilesDir(null), CRASH_REPORTS_DIR)
|
||||
if (!crashDir.exists() && !crashDir.mkdirs()) {
|
||||
Timber.tag(TAG).e("Failed to create crash report directory")
|
||||
return
|
||||
}
|
||||
|
||||
val crashFile = File(crashDir, fileName)
|
||||
FileOutputStream(crashFile).use { it.write(crashInfo.toByteArray()) }
|
||||
|
||||
Timber.tag(TAG).d("Crash info saved to: ${crashFile.absolutePath}")
|
||||
} catch (e: Exception) {
|
||||
Timber.tag(TAG).e(e, "Error saving crash info to file")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理旧的崩溃日志
|
||||
*/
|
||||
fun cleanupOldCrashReports(maxAgeDays: Int = 7) {
|
||||
val crashDir = File(context.getExternalFilesDir(null), CRASH_REPORTS_DIR)
|
||||
if (!crashDir.exists() || !crashDir.isDirectory) return
|
||||
|
||||
val now = System.currentTimeMillis()
|
||||
val maxAgeMillis = maxAgeDays * 24 * 60 * 60 * 1000L
|
||||
|
||||
crashDir.listFiles()?.forEach { file ->
|
||||
if (file.lastModified() < now - maxAgeMillis) {
|
||||
file.delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,12 +21,12 @@
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center_vertical"
|
||||
android:visibility="visible">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="380dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="0dp"
|
||||
@@ -34,15 +34,15 @@
|
||||
|
||||
<TextureView
|
||||
android:id="@+id/dual_camera_texture_preview_rgb"
|
||||
android:layout_width="380dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="visible" />
|
||||
|
||||
<com.sw.plate.utils.arcface.FaceRectView
|
||||
android:id="@+id/dual_camera_face_rect_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="380dp"
|
||||
android:visibility="gone" />
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="visible" />
|
||||
</FrameLayout>
|
||||
|
||||
<ImageView
|
||||
|
||||
Reference in New Issue
Block a user