2 Commits
Author SHA1 Message Date
zxj b83a442fe3 修改了配色,添加了解除绑定提示 2025-08-08 17:26:09 +08:00
zxj 2c10731b8d 修复了识别追踪信息功能 2025-08-08 10:50:11 +08:00
36 changed files with 564 additions and 124 deletions
+7 -25
View File
@@ -26,6 +26,10 @@
android:supportsRtl="true"
android:theme="@style/Theme.SmartPlateCabinet"
tools:targetApi="31">
<activity
android:name=".activity.UnBindDialogActivity"
android:exported="false"
android:theme="@style/DialogActivityTheme" />
<activity
android:name=".activity.InitActivity"
android:exported="true">
@@ -35,41 +39,19 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".activity.TestActivity"
android:exported="true" />
<activity
android:name=".activity.LoginByPwdActivity"
android:exported="true"
android:exported="false"
android:launchMode="singleTask">
<!-- <intent-filter> -->
<!-- <action android:name="android.intent.action.MAIN" /> -->
<!-- <category android:name="android.intent.category.LAUNCHER" /> -->
<!-- </intent-filter> -->
</activity>
<activity
android:name=".activity.LoginByFaceActivity"
android:exported="true"
android:exported="false"
android:launchMode="singleTask">
<!-- <intent-filter>-->
<!-- <action android:name="android.intent.action.MAIN" />-->
<!-- <category android:name="android.intent.category.LAUNCHER" />-->
<!-- </intent-filter>-->
</activity>
<activity
android:name=".activity.MainActivity"
android:exported="true">
<!-- <intent-filter> -->
<!-- <action android:name="android.intent.action.MAIN" /> -->
<!-- <category android:name="android.intent.category.LAUNCHER" /> -->
<!-- </intent-filter> -->
android:exported="false">
</activity>
</application>
@@ -48,4 +48,5 @@ object GlobalKey {
const val KEY_TOKEN = "tokenKey"
const val KEY_FIRST_RUN = "firstRun"
const val KEY_USER_INFO = "userInfoKey"
const val PARAM_EQUIPMENT_INFO = "equipmentUserInfo"
}
@@ -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)
}
/**
@@ -26,13 +26,12 @@ import com.sw.platecabinet.databinding.ItemTitleTimeBinding
import com.sw.platecabinet.ext.setClickListeners
import com.sw.platecabinet.model.response.EquipmentUserInfo
import com.sw.platecabinet.utils.PermissionHelper
import com.sw.platecabinet.view.CustomDialog
import com.sw.platecabinet.view.CustomLoadingDialog
import com.sw.platecabinet.viewmodel.SettingViewModel
import com.sw.platecabinet.viewmodel.UserViewModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.launch
import timber.log.Timber
@@ -44,7 +43,7 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
private var headerBinding: ItemTitleTimeBinding? = null
protected lateinit var context: Context
private var timeJob: Job? = null
private var mDialogWaiting: CustomDialog? = null
private var mDialogWaiting: CustomLoadingDialog? = null
private val permissionHelpers = mutableMapOf<Int, PermissionHelper>()
protected var keyEventHelper: ScanGunKeyEventHelper? = null
@@ -181,7 +180,15 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
enforceImmersiveMode()
}
override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus)
if (hasFocus) {
disableSystemUICompletely()
}
}
private fun disableSystemUICompletely() {
Timber.d("disableSystemUICompletely")
// 禁用系统手势(Android 10+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
window.insetsController?.systemBarsBehavior =
@@ -206,6 +213,7 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
private fun enforceImmersiveMode() {
Timber.d("enforceImmersiveMode")
// 持续强制隐藏系统栏(防止手势触发)
window.decorView.postDelayed({
window.decorView.systemUiVisibility = (
@@ -223,7 +231,7 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
hideWaitingDialog()
val view = View.inflate(this, R.layout.dialog_waiting, null)
if (!TextUtils.isEmpty(tip)) (view.findViewById<View?>(R.id.tvTip) as TextView).setText(tip)
mDialogWaiting = CustomDialog(this, view, R.style.MyDialog)
mDialogWaiting = CustomLoadingDialog(this, view, R.style.MyDialog)
mDialogWaiting!!.show()
mDialogWaiting!!.setCancelable(true)
return mDialogWaiting
@@ -259,7 +267,7 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
}
}
lifecycleScope.launch {
viewModel.currentUserInfo.drop(1).collect {
viewModel.currentUserInfo.collect {
if (it == null) return@collect
handleLoginSuccess(it, false)
}
@@ -275,14 +283,14 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
}
}
lifecycleScope.launch {
settingViewModel.searchUserInfo.drop(1).collect {
settingViewModel.searchUserInfo.collect {
Timber.d("registerDataChange 管理员 currentUserInfo = $it")
if (it == null) return@collect
handleLoginSuccess(it, true)
}
}
lifecycleScope.launch {
settingViewModel.equipmentList.drop(1).collect {
settingViewModel.equipmentList.collect {
if (it.isEmpty()) return@collect
val unbindList = it.filter { !it.isBound() }
if (unbindList.isEmpty()) {
@@ -343,7 +351,6 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
}
override fun onDestroy() {
SerialApi.close()
timeJob?.cancel()
keyEventHelper?.onDestroy()
super.onDestroy()
@@ -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,54 @@
package com.sw.platecabinet.activity
import android.graphics.Color
import android.view.Gravity
import android.view.ViewGroup
import androidx.core.graphics.drawable.toDrawable
import androidx.core.view.WindowCompat
import com.sw.platecabinet.GlobalKey
import com.sw.platecabinet.databinding.ActivityUnbindDialogBinding
import com.sw.platecabinet.databinding.ItemTitleTimeBinding
import com.sw.platecabinet.ext.dp
import com.sw.platecabinet.model.response.EquipmentUserInfo
class UnBindDialogActivity : BaseActivity<ActivityUnbindDialogBinding>() {
private var equipmentUserInfo: EquipmentUserInfo? = null
override fun inflateViewBinding(): ActivityUnbindDialogBinding {
return ActivityUnbindDialogBinding.inflate(layoutInflater)
}
override fun inflateTitleBinding(): ItemTitleTimeBinding? {
return null
}
override fun initialize() {
window?.apply {
WindowCompat.setDecorFitsSystemWindows(this, false)
setBackgroundDrawable(Color.TRANSPARENT.toDrawable())
attributes = attributes.apply {
y = 314.dp
width = (context.resources.displayMetrics.widthPixels * 0.8).toInt()
height = ViewGroup.LayoutParams.MATCH_PARENT
gravity = Gravity.CENTER
}
}
equipmentUserInfo =
intent.getParcelableExtra<EquipmentUserInfo>(GlobalKey.PARAM_EQUIPMENT_INFO)
if (equipmentUserInfo == null) return
binding.tvTipInfo.text = "即将解除【${equipmentUserInfo!!.name}】的餐盘"
binding.tvCancel.setOnClickListener {
finish()
}
binding.tvConfirm.setOnClickListener {
setResult(RESULT_OK)
finish()
}
}
override fun finish() {
super.finish()
overridePendingTransition(0, 0) // 确保退出也无动画
}
}
@@ -12,7 +12,7 @@ import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.viewbinding.ViewBinding
import com.sw.platecabinet.R
import com.sw.platecabinet.view.CustomDialog
import com.sw.platecabinet.view.CustomLoadingDialog
import com.sw.platecabinet.viewmodel.SettingViewModel
import kotlinx.coroutines.launch
@@ -21,7 +21,7 @@ abstract class BaseFragment<VB : ViewBinding>(
) : Fragment() {
private var _binding: VB? = null
protected val binding get() = _binding!!
private var mDialogWaiting: CustomDialog? = null
private var mDialogWaiting: CustomLoadingDialog? = null
protected val viewModel by viewModels<SettingViewModel>()
override fun onCreateView(
@@ -59,13 +59,12 @@ abstract class BaseFragment<VB : ViewBinding>(
hideWaitingDialog()
val view = View.inflate(requireContext(), R.layout.dialog_waiting, null)
if (!TextUtils.isEmpty(tip)) (view.findViewById<View?>(R.id.tvTip) as TextView).text = tip
mDialogWaiting = CustomDialog(requireContext(), view, R.style.MyDialog)
mDialogWaiting = CustomLoadingDialog(requireContext(), view, R.style.MyDialog)
mDialogWaiting!!.show()
mDialogWaiting!!.setCancelable(true)
return mDialogWaiting
}
/**
* 隐藏等待提示框
*/
@@ -26,7 +26,6 @@ import com.sw.platecabinet.model.response.EquipmentUserInfo
import com.sw.platecabinet.model.response.SearchResult
import com.sw.platecabinet.utils.Debouncer
import com.sw.platecabinet.utils.KeyboardUtils
import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.launch
import timber.log.Timber
@@ -150,6 +149,17 @@ class BindPlateFragment : BaseFragment<FragmentBindPlateBinding>(
false // 未处理其他动作
}
}
binding.ivSearch.setOnClickListener {
debouncer.debounce {
// 执行搜索操作
viewModel.getSearchMemberList(
param = binding.etUserInfo.text.toString(),
pageNum = 1
)
// 隐藏键盘
KeyboardUtils.hideKeyboard(requireActivity())
}
}
binding.llBind.setOnClickListener {
if (info == null || info!!.equipmentCode?.isEmpty() == true || info!!.equipmentBoxCode?.isEmpty() == true) {
ToastUtils.showToast("传入的设备信息异常")
@@ -186,12 +196,12 @@ class BindPlateFragment : BaseFragment<FragmentBindPlateBinding>(
override fun registerDataChange() {
super.registerDataChange()
lifecycleScope.launch {
viewModel.searchMemberList.drop(1).collect {
viewModel.searchMemberList.collect {
adapter.updateData(it)
}
}
lifecycleScope.launch {
viewModel.bindStateChange.drop(1).collect {
viewModel.bindStateChange.collect {
if (it.first == null) return@collect // 解绑状态过滤
val errorInfo = it.second
if (errorInfo.isSuccess()) {
@@ -30,7 +30,7 @@ class PlateOpenFragment :
arguments?.let {
info = it.getParcelable(KEY_ACTION_TYPE)
info?.let {
if (!it.isBound()) {
if (!it.hasEquipmentBox()) {
binding.ivType.setImageResource(R.drawable.ic_tip_warn)
binding.tvTitle.text = "暂无餐盘信息"
binding.tvSubTitle.text = "请联系管理员"
@@ -25,7 +25,6 @@ import com.sw.platecabinet.ext.maskName
import com.sw.platecabinet.model.response.EquipmentUserInfo
import com.sw.platecabinet.utils.ListArrangementUtil
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import timber.log.Timber
@@ -49,7 +48,7 @@ class SettingListFragment :
override fun registerDataChange() {
super.registerDataChange()
lifecycleScope.launch {
viewModel.equipmentList.drop(1).collect { it ->
viewModel.equipmentList.collect { it ->
Timber.d("registerDateChange updateAdapter it = $it")
updateAdapter(it)
}
@@ -128,7 +127,7 @@ class SettingListFragment :
)
)
this.llOpen.setBackgroundResource(R.drawable.grid_button_bind)
this.tvOpen.setTextColor(resources.getColor(R.color.bind_F0C8B4))
this.tvOpen.setTextColor(resources.getColor(R.color.bind_FFCC99))
this.llBindInfo.visibility = View.VISIBLE
this.tvUnbind.visibility = View.GONE
@@ -136,7 +135,7 @@ class SettingListFragment :
this.tvLastTime.text = DateTimeUtils.getTimeAgo(date)
} else {
this.llRoot.setBackgroundResource(R.drawable.grid_item_unbind)
this.tvNum.setTextColor(resources.getColor(R.color.bind_F0C8B4))
this.tvNum.setTextColor(resources.getColor(R.color.bind_FFCC99))
this.llOpen.setBackgroundResource(R.drawable.grid_button_unbind)
this.tvOpen.setTextColor(resources.getColor(R.color.unbind_32283C))
this.llBindInfo.visibility = View.GONE
@@ -1,14 +1,18 @@
package com.sw.platecabinet.fragment
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import androidx.activity.result.contract.ActivityResultContracts
import androidx.lifecycle.lifecycleScope
import com.sw.inbound.utils.DateTimeUtils
import com.sw.plate.utils.ToastUtils
import com.sw.platecabinet.GlobalKey
import com.sw.platecabinet.activity.UnBindDialogActivity
import com.sw.platecabinet.databinding.FragmentUnbindPlateBinding
import com.sw.platecabinet.ext.maskName
import com.sw.platecabinet.ext.maskPhone
import com.sw.platecabinet.model.response.EquipmentUserInfo
import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.launch
/**
@@ -30,6 +34,17 @@ class UnBindPlateFragment private constructor() : BaseFragment<FragmentUnbindPla
}
}
private val dialogLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode == Activity.RESULT_OK) {
viewModel.unbindPlate(
equipmentBoxCode = info!!.equipmentBoxCode!!,
equipmentCode = info!!.equipmentCode!!
)
}
}
override fun initialize() {
arguments?.let {
info = it.getParcelable(ARG_PARAM1)
@@ -45,10 +60,11 @@ class UnBindPlateFragment private constructor() : BaseFragment<FragmentUnbindPla
}
binding.llUnbind.setOnClickListener {
if (info == null) return@setOnClickListener
viewModel.unbindPlate(
equipmentBoxCode = info!!.equipmentBoxCode!!,
equipmentCode = info!!.equipmentCode!!
)
val intent = Intent(requireContext(), UnBindDialogActivity::class.java).apply {
putExtra(GlobalKey.PARAM_EQUIPMENT_INFO, info)
}
dialogLauncher.launch(intent)
requireActivity().overridePendingTransition(0, 0)
}
binding.tvBack.setOnClickListener {
activity?.finish()
@@ -58,12 +74,15 @@ class UnBindPlateFragment private constructor() : BaseFragment<FragmentUnbindPla
override fun registerDataChange() {
super.registerDataChange()
lifecycleScope.launch {
viewModel.bindStateChange.drop(1).collect {
viewModel.bindStateChange.collect {
if (it.first == null) return@collect // 绑定状态过滤
val errorInfo = it.second
if (errorInfo.isSuccess()) {
ToastUtils.showToast("解绑成功")
activity?.finish()
// activity?.finish()
// 关闭宿主 Activity
requireActivity().finish()
requireActivity().overridePendingTransition(0, 0)
} else {
ToastUtils.showToast(errorInfo.msg)
}
@@ -2,6 +2,7 @@ package com.sw.platecabinet.model.response
import android.os.Parcelable
import android.text.TextUtils
import com.google.gson.annotations.SerializedName
import com.sw.platecabinet.GlobalData
import kotlinx.parcelize.Parcelize
@@ -74,6 +75,10 @@ data class EquipmentUserInfo(
return equipmentCode != null && equipmentCode != "" && GlobalData.globalEquipmentCode != equipmentCode
}
fun hasEquipmentBox(): Boolean {
return !TextUtils.isEmpty(equipmentBoxCode)
}
fun isPlaceholder(): Boolean {
return equipmentBoxCode == null || equipmentBoxCode == ""
}
@@ -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()
}
}
}
}
@@ -10,13 +10,13 @@ import android.view.View;
import android.view.Window;
import android.view.WindowManager;
public class CustomDialog extends Dialog {
public class CustomLoadingDialog extends Dialog {
/**
* 宽高由布局文件中指定但是最底层的宽度无效可以多嵌套一层解决
*/
public CustomDialog(Context context, View layout, int style) {
public CustomLoadingDialog(Context context, View layout, int style) {
super(context, style);
@@ -35,8 +35,8 @@ public class CustomDialog extends Dialog {
/**
* 宽高由该方法的参数设置
*/
public CustomDialog(Context context, int width, int height, View layout,
int style) {
public CustomLoadingDialog(Context context, int width, int height, View layout,
int style) {
super(context, style);
// 设置内容
setContentView(layout);
@@ -89,6 +89,7 @@ class SettingViewModel : BaseViewModel() {
memberId: Int,
plateNumber: String
) {
_bindStateChange.value = null to ErrorInfo()
launchWithLoading {
val bindParam = BindParam(
appVersion = GlobalData.appVersion,
@@ -136,6 +137,7 @@ class SettingViewModel : BaseViewModel() {
plateNumber: String,
equipmentCode: String = globalEquipmentCode
) {
_searchUserInfo.value = null
launchWithLoading {
val bindParam = BindParam(
appVersion = GlobalData.appVersion,
@@ -128,6 +128,7 @@ class UserViewModel : BaseViewModel() {
* 通过用户id获取用户信息
*/
fun getUserInfoById(equipmentCode: String = globalEquipmentCode, memberId: Int) {
_currentUserInfo.value = null
launchWithLoading {
val loginParam = LoginParam(
appVersion = GlobalData.appVersion,
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#000000" /> <!-- 整体黑色背景 -->
<stroke
android:width="2dp"
android:color="#333333" /> <!-- 边框颜色和宽度 -->
<corners android:radius="12dp" /> <!-- 圆角半径 -->
</shape>
@@ -4,5 +4,5 @@
<corners android:radius="12dp" />
<stroke
android:width="2dp"
android:color="#887872" />
android:color="#FFCC99" />
</shape>
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="6dp" />
<solid android:color="#F0C8B4" />
<solid android:color="#FFCC99" />
</shape>
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

+1 -4
View File
@@ -1,8 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="12dp" />
<!-- <solid android:color="#F3D2BD"/>-->
<gradient
android:endColor="#E5B79F"
android:startColor="#F9DDC9" />
<solid android:color="#FFCC99" />
</shape>
+1 -1
View File
@@ -3,5 +3,5 @@
<corners android:radius="12dp" />
<stroke
android:width="2dp"
android:color="#F0C8B4" />
android:color="#FFCC99" />
</shape>
@@ -21,7 +21,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="手机后四位"
android:textColor="#F0C8B4"
android:textColor="#FFCC99"
android:textSize="26sp"
android:textStyle="bold" />
@@ -62,7 +62,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="25dp"
android:text="校验码"
android:textColor="#F0C8B4"
android:textColor="#FFCC99"
android:textSize="26sp"
android:textStyle="bold" />
@@ -115,7 +115,7 @@
android:layout_marginTop="40dp"
android:padding="8dp"
android:text="人脸识别"
android:textColor="#887872"
android:textColor="#FFCC99"
android:textSize="26sp"
android:textStyle="bold" />
</LinearLayout>
+10 -10
View File
@@ -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
@@ -60,7 +60,7 @@
android:layout_marginTop="129dp"
android:text="请正视屏幕"
android:textStyle="bold"
android:textColor="#fff0c8b4"
android:textColor="#FFCC99"
android:textSize="36sp" />
<LinearLayout
@@ -69,7 +69,7 @@
android:layout_height="wrap_content"
android:layout_marginStart="208dp"
android:layout_marginTop="730dp"
android:background="@drawable/btn_outline_887872"
android:background="@drawable/btn_outline"
android:gravity="center"
android:paddingHorizontal="45dp"
android:paddingVertical="18dp">
@@ -78,7 +78,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="密码登录"
android:textColor="#ff887872"
android:textColor="#FFCC99"
android:textSize="24sp" />
</LinearLayout>
@@ -90,7 +90,7 @@
android:padding="12dp"
android:gravity="center"
android:layout_marginTop="185dp"
android:textColor="#F0C8B4"
android:textColor="#FFCC99"
android:textSize="36sp" />
</FrameLayout>
@@ -0,0 +1,81 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="1024dp"
xmlns:tools="http://schemas.android.com/tools">
<LinearLayout
android:id="@+id/dialogContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:layout_marginTop="0dp"
android:background="@drawable/bg_dialog"
android:gravity="center"
android:orientation="vertical"
android:paddingTop="30dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_tip_warn_unbind" />
<TextView
android:id="@+id/tv_tip_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
tools:text="即将解除【曹德磊】的餐盘"
android:textColor="#ffffffff"
android:textSize="28sp"
android:textStyle="bold" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:text="解绑后该用户将无法使用餐盘柜"
android:textColor="#ff999999"
android:textSize="24sp" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="30dp"
android:background="#ff333333" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="90dp"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_cancel"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="取消"
android:textColor="#ff999999"
android:textSize="30sp"
android:textStyle="bold" />
<View
android:layout_width="1dp"
android:layout_height="match_parent"
android:background="#333333" />
<TextView
android:id="@+id/tv_confirm"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="确定解绑"
android:textColor="#ffffcc99"
android:textSize="30sp"
android:textStyle="bold" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
+22 -14
View File
@@ -14,7 +14,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="60dp"
android:text="柜体编号"
android:textColor="#ff7c6d6a"
android:textColor="#999999"
android:textSize="20sp" />
<TextView
@@ -22,7 +22,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:textColor="#fff0c8b4"
android:textColor="#FFCC99"
android:textSize="36sp"
android:textStyle="bold"
tools:text="1-12" />
@@ -43,7 +43,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="餐盘码"
android:textColor="#fff0c8b4"
android:textColor="#FFCC99"
android:textSize="26sp" />
<Space
@@ -55,7 +55,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="确认餐盘码是否有误"
android:textColor="#ff7c6d6a"
android:textColor="#666666"
android:textSize="20sp" />
</LinearLayout>
@@ -72,11 +72,11 @@
android:layout_height="match_parent"
android:background="@color/transparent"
android:gravity="center"
android:maxLines="1"
android:hint="请录入餐盘码"
android:inputType="number"
android:textColor="#fff0c8b4"
android:textColorHint="#ff7c6d6a"
android:maxLines="1"
android:textColor="#FFCC99"
android:textColorHint="#999999"
android:textSize="26sp"
android:textStyle="bold"
tools:text="" />
@@ -92,7 +92,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="会员"
android:textColor="#fff0c8b4"
android:textColor="#FFCC99"
android:textSize="26sp" />
<Space
@@ -104,7 +104,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="仅限VIP会员绑定,非VIP暂不支持"
android:textColor="#ff7c6d6a"
android:textColor="#666666"
android:textSize="20sp" />
</LinearLayout>
@@ -117,17 +117,25 @@
<EditText
android:id="@+id/et_user_info"
android:layout_width="match_parent"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@color/transparent"
android:gravity="center"
android:hint="请输入会员姓名或手机号"
android:imeOptions="actionSearch"
android:inputType="text"
android:maxLines="1"
android:textColor="#F0C8B4"
android:textColorHint="#ff7c6d6a"
android:textColor="#FFCC99"
android:textColorHint="#999999"
android:textSize="26sp" />
<ImageView
android:id="@+id/iv_search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="24dp"
android:src="@drawable/ic_search" />
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
@@ -150,7 +158,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="绑定开门"
android:textColor="#ff11111c"
android:textColor="#11111C"
android:textSize="30sp"
android:textStyle="bold" />
</LinearLayout>
@@ -162,6 +170,6 @@
android:layout_marginTop="46dp"
android:paddingHorizontal="20dp"
android:text="返回"
android:textColor="#ff887872"
android:textColor="#FFCC99"
android:textSize="26sp" />
</LinearLayout>
@@ -18,7 +18,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="48dp"
android:text="餐盘柜已满"
android:textColor="#fff0c8b4"
android:textColor="#FFCC99"
android:textSize="36sp" />
<TextView
@@ -27,7 +27,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:text="3秒后返回主屏"
android:textColor="#ff887872"
android:textColor="#FFCC99"
android:textSize="20sp" />
@@ -51,7 +51,7 @@
android:layout_marginHorizontal="20dp"
android:maxLines="1"
android:ellipsize="end"
android:textColor="#ff887872"
android:textColor="#FFCC99"
android:textSize="24sp" />
<TextView
@@ -60,7 +60,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="34dp"
android:text="3"
android:textColor="#fff0c8b4"
android:textColor="#FFCC99"
android:textSize="36sp" />
</LinearLayout>
</FrameLayout>
@@ -20,7 +20,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="25dp"
android:text="柜体编号"
android:textColor="#ff7c6d6a"
android:textColor="#999999"
android:textSize="20sp" />
<TextView
@@ -30,7 +30,7 @@
android:layout_marginTop="23dp"
tools:text="1-12"
android:textStyle="bold"
android:textColor="#fff0c8b4"
android:textColor="#FFCC99"
android:textSize="36sp" />
@@ -53,7 +53,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="餐盘码"
android:textColor="#ff7c6d6a"
android:textColor="#999999"
android:textSize="24sp" />
<Space
@@ -66,7 +66,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="100888"
android:textColor="#fff0c8b4"
android:textColor="#FFCC99"
android:textSize="24sp"
android:textStyle="bold" />
</LinearLayout>
@@ -88,7 +88,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="姓名"
android:textColor="#ff7c6d6a"
android:textColor="#999999"
android:textSize="24sp" />
<Space
@@ -101,7 +101,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="曹*磊"
android:textColor="#fff0c8b4"
android:textColor="#FFCC99"
android:textSize="24sp"
android:textStyle="bold" />
</LinearLayout>
@@ -123,7 +123,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="手机号"
android:textColor="#ff7c6d6a"
android:textColor="#999999"
android:textSize="24sp" />
<Space
@@ -136,7 +136,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="138****8000"
android:textColor="#fff0c8b4"
android:textColor="#FFCC99"
android:textSize="24sp"
android:textStyle="bold" />
</LinearLayout>
@@ -157,7 +157,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="最近使用"
android:textColor="#ff7c6d6a"
android:textColor="#999999"
android:textSize="24sp" />
<Space
@@ -170,7 +170,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="14天前使用"
android:textColor="#fff0c8b4"
android:textColor="#FFCC99"
android:textSize="24sp"
android:textStyle="bold" />
</LinearLayout>
@@ -188,7 +188,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="解除绑定"
android:textColor="#ff11111c"
android:textColor="#11111C"
android:textSize="30sp"
android:textStyle="bold" />
</LinearLayout>
@@ -200,6 +200,6 @@
android:layout_marginTop="48dp"
android:paddingHorizontal="20dp"
android:text="返回"
android:textColor="#ff887872"
android:textColor="#FFCC99"
android:textSize="26sp" />
</LinearLayout>
+2 -2
View File
@@ -16,7 +16,7 @@
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:text="02"
android:textColor="#fff0c8b4"
android:textColor="#FFCC99"
android:textSize="24sp"
android:textStyle="bold" />
@@ -27,7 +27,7 @@
android:layout_marginStart="24dp"
android:layout_weight="1"
android:text="未绑定"
android:textColor="#fff0c8b4"
android:textColor="#FFCC99"
android:textSize="18sp"
android:textStyle="bold"
android:visibility="gone" />
@@ -12,7 +12,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="马*飞(8000)"
android:textColor="#fff0c8b4"
android:textColor="#FFCC99"
android:textSize="26sp"
android:textStyle="bold" />
@@ -22,7 +22,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="138****8000"
android:textColor="#ff7c6d6a"
android:textColor="#999999"
android:textSize="20sp"
android:textStyle="bold" />
</LinearLayout>
+2 -2
View File
@@ -12,7 +12,7 @@
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:paddingTop="20dp"
android:textColor="#F0C8B4"
android:textColor="#FFCC99"
android:textSize="18sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
@@ -24,7 +24,7 @@
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:paddingTop="20dp"
android:textColor="#F0C8B4"
android:textColor="#FFCC99"
android:textSize="18sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/tvLeftDate"
+4 -4
View File
@@ -7,12 +7,12 @@
<color name="bind_4E535D">#4E535D</color>
<color name="bind_585868">#585868</color>
<color name="bind_time_old">#FF9632</color>
<color name="bind_F0C8B4">#F0C8B4</color>
<color name="unbind_F0C8B4">#F0C8B4</color>
<color name="bind_FFCC99">#FFCC99</color>
<color name="unbind_FFCC99">#FFCC99</color>
<color name="unbind_32283C">#32283C</color>
<color name="divColor">#4c91ceff</color>
<color name="tip_title_success">#ff02f1be</color>
<color name="tip_title_fail">#FF9933</color>
<color name="tip_sub_title">#887872</color>
<color name="tip_title_fail">#FFCC99</color>
<color name="tip_sub_title">#FFCC99</color>
</resources>
+10
View File
@@ -22,4 +22,14 @@
<item name="windowActionBar">false</item>
</style>
<style name="DialogActivityTheme" parent="Theme.AppCompat.Dialog">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<!--取消默认的居中-->
<item name="android:windowIsFloating">false</item>
<item name="android:windowBackground">#BF000000</item>
<item name="android:windowFullscreen">true</item>
<!--触摸外部关闭-->
<item name="android:windowCloseOnTouchOutside">false</item>
</style>
</resources>
@@ -36,6 +36,10 @@ public class SerialApi {
}
public static void close() {
if (serialPort != null) {
serialPort.close();
serialPort = null;
}
if (serialPortManager != null) {
serialPortManager.close();
}