优化并增加日志

This commit is contained in:
马增飞
2025-11-18 18:49:46 +08:00
parent 79d14ca300
commit 096499cd65
17 changed files with 482 additions and 155 deletions
+5 -4
View File
@@ -26,7 +26,7 @@
android:theme="@style/Theme.IntelligentShelves" android:theme="@style/Theme.IntelligentShelves"
android:usesCleartextTraffic="true"> android:usesCleartextTraffic="true">
<activity <activity
android:name=".InitActivity" android:name=".activity.InitActivity"
android:exported="true" android:exported="true"
android:screenOrientation="portrait" android:screenOrientation="portrait"
tools:ignore="DiscouragedApi,LockedOrientationActivity"> tools:ignore="DiscouragedApi,LockedOrientationActivity">
@@ -37,13 +37,14 @@
</intent-filter> </intent-filter>
</activity> </activity>
<activity <activity
android:name=".HomeActivity" android:name=".activity.HomeActivity"
android:screenOrientation="portrait"/> android:screenOrientation="portrait"/>
<activity <activity
android:name=".ShelfActivity" android:name=".activity.ShelfActivity"
android:screenOrientation="portrait" android:screenOrientation="portrait"
tools:ignore="DiscouragedApi,LockedOrientationActivity" /> tools:ignore="DiscouragedApi,LockedOrientationActivity" />
<activity android:name="com.shuwei.intelligent.shelves.SettingActivity" /> <activity android:name="com.shuwei.intelligent.shelves.activity.SettingActivity" />
<activity android:name="com.shuwei.intelligent.shelves.activity.LogActivity" />
<receiver <receiver
android:name=".utils.BootReceiver" android:name=".utils.BootReceiver"
@@ -1,4 +1,4 @@
package com.shuwei.intelligent.shelves package com.shuwei.intelligent.shelves.activity
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Intent import android.content.Intent
@@ -14,6 +14,8 @@ import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import com.shuwei.intelligent.shelves.App
import com.shuwei.intelligent.shelves.R
import com.shuwei.intelligent.shelves.adapter.ShelfAdapter import com.shuwei.intelligent.shelves.adapter.ShelfAdapter
import com.shuwei.intelligent.shelves.base.BaseActivity import com.shuwei.intelligent.shelves.base.BaseActivity
import com.shuwei.intelligent.shelves.databinding.ActivityHomeBinding import com.shuwei.intelligent.shelves.databinding.ActivityHomeBinding
@@ -62,24 +64,24 @@ class HomeActivity : BaseActivity() {
// "${HEADER}000281C5D70A7D428A4FA67B50F7DACA241938938B4B9CBD73673E9C8E7F2E15062D${FOOTER}" // "${HEADER}000281C5D70A7D428A4FA67B50F7DACA241938938B4B9CBD73673E9C8E7F2E15062D${FOOTER}"
const val LEFT_SHELF_OPEN_CMD = const val LEFT_SHELF_OPEN_CMD =
"${HEADER}020101000000000000000000000000000000000000${FOOTER}" "${HEADER}020101000000000000000000000000000000000000$FOOTER"
const val RIGHT_SHELF_OPEN_CMD = const val RIGHT_SHELF_OPEN_CMD =
"${HEADER}020100010000000000000000000000000000000000${FOOTER}" "${HEADER}020100010000000000000000000000000000000000$FOOTER"
//const val TEMPERATURE_CMD = "${HEADER}1001${FOOTER}" //const val TEMPERATURE_CMD = "${HEADER}1001${FOOTER}"
const val DEVICE_INFO_CMD = "${HEADER}0301000000000000000000000000${FOOTER}" const val DEVICE_INFO_CMD = "${HEADER}0301000000000000000000000000$FOOTER"
//const val START_CMD= "${HEADER}0F01360000${FOOTER}" //const val START_CMD= "${HEADER}0F01360000${FOOTER}"
//const val START_CMD= "${HEADER}0F01050020${FOOTER}" //const val START_CMD= "${HEADER}0F01050020${FOOTER}"
const val ACTIVE_CMD = const val ACTIVE_CMD =
"${HEADER}000281C5D70A7D428948AD7254FADBB1516738938B4B9CBD73673E9D8E7F2E15062D${FOOTER}" "${HEADER}000281C5D70A7D428948AD7254FADBB1516738938B4B9CBD73673E9D8E7F2E15062D$FOOTER"
//开灯(开 继电器1 //开灯(开 继电器1
const val LIGHT_CLOSE = "${HEADER}020102020102020000000000000000000000000000${FOOTER}" const val LIGHT_CLOSE = "${HEADER}020102020102020000000000000000000000000000$FOOTER"
const val LIGHT_OPEN = "${HEADER}020102020002020000000000000000000000000000${FOOTER}" const val LIGHT_OPEN = "${HEADER}020102020002020000000000000000000000000000$FOOTER"
const val TEMPERATURE_CTRL = "${HEADER}0F01040000${FOOTER}" const val TEMPERATURE_CTRL = "${HEADER}0F01040000$FOOTER"
val list: MutableList<ShelfModel> = mutableListOf() val list: MutableList<ShelfModel> = mutableListOf()
val weightArray = SparseIntArray() val weightArray = SparseIntArray()
@@ -97,7 +99,6 @@ class HomeActivity : BaseActivity() {
private val viewModel: NetViewModel by viewModels() private val viewModel: NetViewModel by viewModels()
@SuppressLint("NotifyDataSetChanged", "HardwareIds") @SuppressLint("NotifyDataSetChanged", "HardwareIds")
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@@ -109,22 +110,22 @@ class HomeActivity : BaseActivity() {
updateLeftStatus("-°C / -%") updateLeftStatus("-°C / -%")
initRecyclerView() initRecyclerView()
val openState = SerialPortManager.open() lifecycleScope.launch {
Log.d(TAG, "onCreate: openState=$openState") val openState = SerialPortManager.open()
if (openState) { log("onCreate: openState=$openState")
SerialPortManager.startReceive { data -> if (openState) {
runOnUiThread { SerialPortManager.startReceive { data ->
// 更新UI显示接收数据 // 更新UI显示接收数据
try { lifecycleScope.launch {
receiveSerialPortData(data) try {
} catch (e: Exception) { receiveSerialPortData(data)
e.printStackTrace() } catch (e: Exception) {
e.printStackTrace()
}
} }
} }
//SerialPortManager.send(DEVICE_INFO_CMD)
} }
//SerialPortManager.send(DEVICE_INFO_CMD)
}
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.CREATED) { repeatOnLifecycle(Lifecycle.State.CREATED) {
launch { launch {
viewModel.getAccessTokenUiState.collect { state -> viewModel.getAccessTokenUiState.collect { state ->
@@ -152,7 +153,7 @@ class HomeActivity : BaseActivity() {
// var androidId = Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID) // var androidId = Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID)
//// androidId = "d0e7a2d5f25c775c" //// androidId = "d0e7a2d5f25c775c"
// Log.d(TAG, "onCreate: androidId=${androidId}") // log("onCreate: androidId=${androidId}")
// if (androidId == "65e1012638f78260" || androidId == "40f89e91ee40611d") { // if (androidId == "65e1012638f78260" || androidId == "40f89e91ee40611d") {
// androidId = "d0e7a2d5f25c775c" // androidId = "d0e7a2d5f25c775c"
// } // }
@@ -160,7 +161,7 @@ class HomeActivity : BaseActivity() {
// //"53babf69-0d2f-3875-a649-f2e12f80ad4c1" // //"53babf69-0d2f-3875-a649-f2e12f80ad4c1"
// //
// val swDpValue = resources?.configuration?.smallestScreenWidthDp // val swDpValue = resources?.configuration?.smallestScreenWidthDp
// Log.d(TAG, "onCreate: swDpValue=$swDpValue") // log("onCreate: swDpValue=$swDpValue")
// viewModel.getAccessToken(App.deviceId) // viewModel.getAccessToken(App.deviceId)
@@ -200,7 +201,7 @@ class HomeActivity : BaseActivity() {
} }
private fun getTokenSuccess(data: RespData<*>) { private fun getTokenSuccess(data: RespData<*>) {
Log.d(TAG, "getTokenSuccess: $data") log("getTokenSuccess: $data")
data.data?.let { data.data?.let {
App.accessToken = it.toString() App.accessToken = it.toString()
//UIUtils.toast(it.toString()) //UIUtils.toast(it.toString())
@@ -259,8 +260,8 @@ class HomeActivity : BaseActivity() {
private val serialReader by lazy { StringBuilder() } private val serialReader by lazy { StringBuilder() }
private fun receiveSerialPortData(srcData: String) { private suspend fun receiveSerialPortData(srcData: String) {
Log.d(TAG, "receiveSerialPortData: $srcData") log("receiveSerialPortData: $srcData")
var data = "" var data = ""
if (srcData.startsWith(HEADER) && srcData.contains(FOOTER)) { if (srcData.startsWith(HEADER) && srcData.contains(FOOTER)) {
serialReader.clear() serialReader.clear()
@@ -277,7 +278,7 @@ class HomeActivity : BaseActivity() {
} else { } else {
val endIndex = srcData.indexOf(FOOTER) + FOOTER.length val endIndex = srcData.indexOf(FOOTER) + FOOTER.length
serialReader.append(srcData.substring(0, endIndex)) serialReader.append(srcData.substring(0, endIndex))
Log.d(TAG, "receiveSerialPortData:serialReader: $serialReader") log("receiveSerialPortData:serialReader: $serialReader")
data = serialReader.toString().replace(",", "") data = serialReader.toString().replace(",", "")
} }
@@ -289,30 +290,30 @@ class HomeActivity : BaseActivity() {
if (reqBody.length == 64) { if (reqBody.length == 64) {
terminalId = reqBody.substring(2, 32) terminalId = reqBody.substring(2, 32)
terminalVersion = reqBody.substring(32, 42) terminalVersion = reqBody.substring(32, 42)
Log.d(TAG, "receiveSerialPortData:收到[激活]${cmd.substring(0,2)}指令:${data},终端id:${terminalId},固件版本:${terminalVersion},发送激活命令:${ACTIVE_CMD}") log("receiveSerialPortData:收到[激活]${cmd.substring(0,2)}指令:${data},终端id:${terminalId},固件版本:${terminalVersion},发送激活命令:$ACTIVE_CMD")
SerialPortManager.send(ACTIVE_CMD) sendCmd(ACTIVE_CMD)
} else { } else {
runOnUiThread { runOnUiThread {
toast("收到终端激活指令错误") toast("收到终端激活指令错误")
} }
SerialPortManager.send(ACTIVE_CMD) sendCmd(ACTIVE_CMD)
} }
} }
"0101","8101" -> { "0101","8101" -> {
//心跳0102${p1}${p2}${version} //心跳0102${p1}${p2}${version}
//心跳81(终端发起,当终端发送此命令时需要服务端存储串口屏显示信息) //心跳81(终端发起,当终端发送此命令时需要服务端存储串口屏显示信息)
val respCmd = "${HEADER}01020106${terminalVersion}${FOOTER}" val respCmd = "${HEADER}01020106${terminalVersion}$FOOTER"
Log.d(TAG, "receiveSerialPortData:收到[心跳]${cmd.substring(0,2)}指令:${data},发送应答指令:${respCmd}") log("receiveSerialPortData:收到[心跳]${cmd.substring(0,2)}指令:${data},发送应答指令:${respCmd}")
SerialPortManager.send(respCmd) sendCmd(respCmd)
heartBeat(data) heartBeat(data)
} }
"0202" -> { "0202" -> {
//开关锁反馈020200${deviceId}${version} //开关锁反馈020200${deviceId}${version}
val respCmd = "${HEADER}020200${terminalId}${terminalVersion}${FOOTER}" val respCmd = "${HEADER}020200${terminalId}${terminalVersion}$FOOTER"
Log.d(TAG, "receiveSerialPortData:收到[开锁]${cmd.substring(0,2)}指令:${data},发送应答指令:${respCmd}") log("receiveSerialPortData:收到[开锁]${cmd.substring(0,2)}指令:${data},发送应答指令:${respCmd}")
SerialPortManager.send(respCmd) sendCmd(respCmd)
//lockFeedback(data) //lockFeedback(data)
} }
@@ -323,16 +324,16 @@ class HomeActivity : BaseActivity() {
"0501" -> { "0501" -> {
//卡号上发 //卡号上发
val respCmd = "${HEADER}050203${FOOTER}" val respCmd = "${HEADER}050203$FOOTER"
Log.d(TAG, "receiveSerialPortData:收到[卡号上发]${cmd.substring(0,2)}指令:${data},发送应答指令:${respCmd}") log("receiveSerialPortData:收到[卡号上发]${cmd.substring(0,2)}指令:${data},发送应答指令:${respCmd}")
SerialPortManager.send(respCmd) sendCmd(respCmd)
} }
"0B01" -> { "0B01" -> {
//操作记录上传 //操作记录上传
val respCmd = "${HEADER}0B0203${FOOTER}" val respCmd = "${HEADER}0B0203$FOOTER"
Log.d(TAG, "receiveSerialPortData:收到[操作记录上传]${cmd.substring(0,2)}指令:${data},发送应答指令:${respCmd}") log("receiveSerialPortData:收到[操作记录上传]${cmd.substring(0,2)}指令:${data},发送应答指令:${respCmd}")
SerialPortManager.send(respCmd) sendCmd(respCmd)
} }
"0C01" -> { "0C01" -> {
@@ -340,28 +341,28 @@ class HomeActivity : BaseActivity() {
val timeLong = System.currentTimeMillis() / 1000 + (8 * 3600) val timeLong = System.currentTimeMillis() / 1000 + (8 * 3600)
val timeStr = "0000000" + String.format("%X", timeLong) val timeStr = "0000000" + String.format("%X", timeLong)
val time = timeStr.substring(timeStr.length - 8).chunked(2).reversed().joinToString("") val time = timeStr.substring(timeStr.length - 8).chunked(2).reversed().joinToString("")
val respCmd = "${HEADER}0B02${time}${FOOTER}" val respCmd = "${HEADER}0B02${time}$FOOTER"
Log.d(TAG, "receiveSerialPortData:收到[对时]${cmd.substring(0,2)}指令:${data},发送应答指令:${respCmd}") log("receiveSerialPortData:收到[对时]${cmd.substring(0,2)}指令:${data},发送应答指令:${respCmd}")
SerialPortManager.send(respCmd) sendCmd(respCmd)
} }
"0E01" -> { "0E01" -> {
//终端位置上报 //终端位置上报
val respCmd = "${HEADER}0E0200${FOOTER}" val respCmd = "${HEADER}0E0200$FOOTER"
Log.d(TAG, "receiveSerialPortData:收到[终端位置上报]${cmd.substring(0,2)}指令:${data},发送应答指令:${respCmd}") log("receiveSerialPortData:收到[终端位置上报]${cmd.substring(0,2)}指令:${data},发送应答指令:${respCmd}")
SerialPortManager.send(respCmd) sendCmd(respCmd)
} }
"1001" -> { "1001" -> {
//温控信息上报 //温控信息上报
val respCmd = "${HEADER}100200${FOOTER}" val respCmd = "${HEADER}100200$FOOTER"
Log.d(TAG, "receiveSerialPortData:收到[温控信息上报]${cmd.substring(0,2)}指令:${data},发送应答指令:${respCmd}") log("receiveSerialPortData:收到[温控信息上报]${cmd.substring(0,2)}指令:${data},发送应答指令:${respCmd}")
SerialPortManager.send(respCmd) sendCmd(respCmd)
} }
//"1002" -> { //"1002" -> {
// Log.d(TAG, "receiveSerialPortData,温控信息上报1002数据:${data}") // log("receiveSerialPortData,温控信息上报1002数据:${data}")
//} //}
//"0F02" -> { //"0F02" -> {
// Log.d(TAG, "receiveSerialPortData,温控信息应答数据:${data}") // log("receiveSerialPortData,温控信息应答数据:${data}")
//} //}
else -> {} else -> {}
@@ -370,27 +371,27 @@ class HomeActivity : BaseActivity() {
private var fixedDataBody = "" private var fixedDataBody = ""
private fun lockFeedback(data: String) { // private fun lockFeedback(data: String) {
runCatching { // runCatching {
val fixedDataBodyEndIndex = data.length - FOOTER.length // val fixedDataBodyEndIndex = data.length - FOOTER.length
val fixedDataBodyStartIndex = HEADER.length + "0202".length + 42 // val fixedDataBodyStartIndex = HEADER.length + "0202".length + 42
fixedDataBody = data.substring(fixedDataBodyStartIndex, fixedDataBodyEndIndex) // fixedDataBody = data.substring(fixedDataBodyStartIndex, fixedDataBodyEndIndex)
//
val lockNo = if (list[shelfIndex].deviceNo in 1..5) "01" else "02" // val lockNo = if (list[shelfIndex].deviceNo in 1..5) "01" else "02"
val lockOneState = data.substring(HEADER.length + 4, HEADER.length + 6) // val lockOneState = data.substring(HEADER.length + 4, HEADER.length + 6)
if (lockOneState == "00") { // if (lockOneState == "00") {
//锁1关 // //锁1关
SerialPortManager.send("${HEADER}0202${lockNo}${FOOTER}") // SerialPortManager.send("${HEADER}0202${lockNo}${FOOTER}")
} // }
val lockTwoState = data.substring(HEADER.length + 6, HEADER.length + 8) // val lockTwoState = data.substring(HEADER.length + 6, HEADER.length + 8)
if (lockTwoState == "00") { // if (lockTwoState == "00") {
//锁2关 // //锁2关
SerialPortManager.send("${HEADER}0202${lockNo}${FOOTER}") // SerialPortManager.send("${HEADER}0202${lockNo}${FOOTER}")
} // }
}.onFailure { // }.onFailure {
it.printStackTrace() // it.printStackTrace()
} // }
} // }
private fun heartBeat(data: String) { private fun heartBeat(data: String) {
runCatching { runCatching {
@@ -450,7 +451,7 @@ class HomeActivity : BaseActivity() {
it.weight = realWeight.toDouble() it.weight = realWeight.toDouble()
val pos = list.indexOf(it) val pos = list.indexOf(it)
shelfAdapter.notifyItemChanged(pos) shelfAdapter.notifyItemChanged(pos)
Log.d(TAG, "getWeightInfo: deviceNo=${it.deviceNo},realWeight=${realWeight}") log("getWeightInfo: deviceNo=${it.deviceNo},realWeight=${realWeight}")
EventBus.getDefault().post(SendWeightEvent(it.deviceNo, realWeight)) EventBus.getDefault().post(SendWeightEvent(it.deviceNo, realWeight))
} }
} }
@@ -464,10 +465,25 @@ class HomeActivity : BaseActivity() {
// intent.putExtra(ShelfActivity.SHELF_WEIGHT, weightArray[shelfIndex]) // intent.putExtra(ShelfActivity.SHELF_WEIGHT, weightArray[shelfIndex])
// LocalBroadcastManager.getInstance(this).sendBroadcast(intent) // LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
// } // }
val temperatureEndIndex = end
if (terminalId.isBlank() || terminalVersion.isBlank()) {
try {
val fixDataBodyStartIndex = temperatureEndIndex + 1
val fixDataBody = data.substring(fixDataBodyStartIndex, data.length- FOOTER.length)
if (fixDataBody.length == 42) {
terminalId = fixDataBody.substring(0, 30)
terminalVersion = fixDataBody.substring(30, 40)
log("receiveSerialPortData:从设备状态获取数据,终端id:${terminalId},固件版本:${terminalVersion}")
}
} catch (e: Exception) {
e.printStackTrace()
}
}
} }
private fun getDeviceInfo(data: String) { private fun getDeviceInfo(data: String) {
Log.d(TAG, "getDeviceInfo: data=$data") log("getDeviceInfo: data=$data")
val weightStartIndex = HEADER.length + 14 val weightStartIndex = HEADER.length + 14
val temperatureStartIndex = weightStartIndex + 12 * 8 val temperatureStartIndex = weightStartIndex + 12 * 8
val temperatureEndIndex = temperatureStartIndex + 2 val temperatureEndIndex = temperatureStartIndex + 2
@@ -491,19 +507,6 @@ class HomeActivity : BaseActivity() {
data = data data = data
) )
if (terminalId.isBlank() || terminalVersion.isBlank()) {
try {
val fixDataBodyStartIndex = temperatureEndIndex + 1
val fixDataBody = data.substring(fixDataBodyStartIndex)
if (fixDataBody.length == 62) {
terminalId = fixDataBody.substring(0, 30)
terminalVersion = fixDataBody.substring(30, 40)
Log.d(TAG, "receiveSerialPortData:从设备状态获取数据,终端id:${terminalId},固件版本:${terminalVersion}")
}
} catch (e: Exception) {
e.printStackTrace()
}
}
} }
override fun onDestroy() { override fun onDestroy() {
@@ -1,14 +1,13 @@
package com.shuwei.intelligent.shelves package com.shuwei.intelligent.shelves.activity
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.os.Bundle import android.os.Bundle
import android.provider.Settings
import android.util.Log import android.util.Log
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle import androidx.lifecycle.repeatOnLifecycle
import com.shuwei.intelligent.shelves.App
import com.shuwei.intelligent.shelves.base.BaseActivity import com.shuwei.intelligent.shelves.base.BaseActivity
import com.shuwei.intelligent.shelves.databinding.ActivityInitBinding import com.shuwei.intelligent.shelves.databinding.ActivityInitBinding
import com.shuwei.intelligent.shelves.model.DeviceConfigInfo import com.shuwei.intelligent.shelves.model.DeviceConfigInfo
@@ -24,17 +23,13 @@ import com.shuwei.intelligent.shelves.utils.ext.startActivity
import com.shuwei.intelligent.shelves.utils.ext.toJsonString import com.shuwei.intelligent.shelves.utils.ext.toJsonString
import com.shuwei.intelligent.shelves.utils.ext.toObject import com.shuwei.intelligent.shelves.utils.ext.toObject
import com.shuwei.intelligent.shelves.utils.ext.toast import com.shuwei.intelligent.shelves.utils.ext.toast
import com.shuwei.intelligent.shelves.utils.ext.visible
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.FlowCollector
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlin.collections.forEach import kotlin.collections.forEach
import kotlin.getValue import kotlin.getValue
import kotlin.jvm.java
import kotlin.ranges.downTo import kotlin.ranges.downTo
import kotlin.text.isBlank import kotlin.text.isBlank
import kotlin.to
class InitActivity : BaseActivity() { class InitActivity : BaseActivity() {
companion object { companion object {
@@ -53,7 +48,7 @@ class InitActivity : BaseActivity() {
App.deviceId = AppUtil.getUDID( this) App.deviceId = AppUtil.getUDID( this)
SpTool.put(SpTool.DEVICE_ID, App.deviceId) SpTool.put(SpTool.DEVICE_ID, App.deviceId)
App.configUrl = UrlConfig.BASE_URL App.configUrl = UrlConfig.BASE_URL
App.canteenId = "0" App.canteenId = "0"
@@ -0,0 +1,57 @@
package com.shuwei.intelligent.shelves.activity
import android.os.Bundle
import androidx.recyclerview.widget.LinearLayoutManager
import com.shuwei.intelligent.shelves.adapter.LogAdapter
import com.shuwei.intelligent.shelves.base.BaseActivity
import com.shuwei.intelligent.shelves.databinding.ActivityLogListBinding
import com.shuwei.intelligent.shelves.utils.ext.startActivity
class LogActivity : BaseActivity() {
companion object {
const val IS_LOG_DIR = "isLogDir"
const val LOG_DATE = "logDate"
}
private lateinit var binding:ActivityLogListBinding
private var isLogDir = false
private var logDate = ""
private val logList = mutableListOf<String>()
private val logAdapter by lazy {
LogAdapter(logList).apply {
setOnItemClickListener { _, _, position ->
if (isLogDir.not()) {
return@setOnItemClickListener
}
startActivity<LogActivity> {
putExtra(IS_LOG_DIR, false)
putExtra(LOG_DATE, logList[position])
}
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityLogListBinding.inflate(layoutInflater)
setBackground()
setContentView(binding.root)
isLogDir = intent.getBooleanExtra(IS_LOG_DIR, false)
logDate = intent.getStringExtra(LOG_DATE) ?: ""
binding.ivBack.setOnClickListener { finish() }
binding.tvTitle.text = if (isLogDir) "全部日志" else logDate
if (isLogDir) {
logList.addAll(fileLogger.getLogFiles())
} else {
logList.addAll(fileLogger.readLogsByName(logDate))
}
binding.rvLogList.let {
it.layoutManager = LinearLayoutManager(this)
it.adapter = logAdapter
}
}
}
@@ -1,14 +1,11 @@
package com.shuwei.intelligent.shelves package com.shuwei.intelligent.shelves.activity
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import androidx.lifecycle.lifecycleScope
import com.shuwei.intelligent.shelves.base.BaseActivity import com.shuwei.intelligent.shelves.base.BaseActivity
import com.shuwei.intelligent.shelves.databinding.ActivitySettingBinding import com.shuwei.intelligent.shelves.databinding.ActivitySettingBinding
import com.shuwei.intelligent.shelves.serial.SerialPortManager
import com.shuwei.intelligent.shelves.utils.KeyboardUtil import com.shuwei.intelligent.shelves.utils.KeyboardUtil
import kotlinx.coroutines.launch
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Date import java.util.Date
import java.util.Locale import java.util.Locale
@@ -30,9 +27,7 @@ class SettingActivity: BaseActivity() {
record = cmd+"\n"+record record = cmd+"\n"+record
binding.tvCmdRecord.text = record binding.tvCmdRecord.text = record
val realCmd = "${HomeActivity.HEADER}0F01${cmd}${HomeActivity.FOOTER}" val realCmd = "${HomeActivity.HEADER}0F01${cmd}${HomeActivity.FOOTER}"
lifecycleScope.launch { sendCmd(realCmd)
SerialPortManager.send(realCmd)
}
} }
binding.ivBack.setOnClickListener { finish() } binding.ivBack.setOnClickListener { finish() }
binding.root.setOnClickListener { v -> binding.root.setOnClickListener { v ->
@@ -1,4 +1,4 @@
package com.shuwei.intelligent.shelves package com.shuwei.intelligent.shelves.activity
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Intent import android.content.Intent
@@ -16,20 +16,20 @@ import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import com.chad.library.adapter4.util.setOnDebouncedItemClick import com.chad.library.adapter4.util.setOnDebouncedItemClick
import com.scwang.smart.refresh.layout.constant.RefreshState import com.scwang.smart.refresh.layout.constant.RefreshState
import com.shuwei.intelligent.shelves.HomeActivity.Companion.DEVICE_INFO_CMD import com.shuwei.intelligent.shelves.App
import com.shuwei.intelligent.shelves.R
import com.shuwei.intelligent.shelves.activity.HomeActivity.Companion.DEVICE_INFO_CMD
import com.shuwei.intelligent.shelves.adapter.SearchAdapter import com.shuwei.intelligent.shelves.adapter.SearchAdapter
import com.shuwei.intelligent.shelves.base.BaseActivity import com.shuwei.intelligent.shelves.base.BaseActivity
import com.shuwei.intelligent.shelves.databinding.ActivityShelfBinding import com.shuwei.intelligent.shelves.databinding.ActivityShelfBinding
import com.shuwei.intelligent.shelves.model.ClearShelfEvent import com.shuwei.intelligent.shelves.model.ClearShelfEvent
import com.shuwei.intelligent.shelves.model.GoodsModel import com.shuwei.intelligent.shelves.model.GoodsModel
import com.shuwei.intelligent.shelves.model.GoodsRecord
import com.shuwei.intelligent.shelves.model.SendWeightEvent import com.shuwei.intelligent.shelves.model.SendWeightEvent
import com.shuwei.intelligent.shelves.model.ShelfModel import com.shuwei.intelligent.shelves.model.ShelfModel
import com.shuwei.intelligent.shelves.net.Loading import com.shuwei.intelligent.shelves.net.Loading
import com.shuwei.intelligent.shelves.net.NetViewModel import com.shuwei.intelligent.shelves.net.NetViewModel
import com.shuwei.intelligent.shelves.net.RespData import com.shuwei.intelligent.shelves.net.RespData
import com.shuwei.intelligent.shelves.net.UiState import com.shuwei.intelligent.shelves.net.UiState
import com.shuwei.intelligent.shelves.serial.SerialPortManager
import com.shuwei.intelligent.shelves.task.TaskManager import com.shuwei.intelligent.shelves.task.TaskManager
import com.shuwei.intelligent.shelves.utils.DateTimeUtil import com.shuwei.intelligent.shelves.utils.DateTimeUtil
import com.shuwei.intelligent.shelves.utils.KeyboardUtil import com.shuwei.intelligent.shelves.utils.KeyboardUtil
@@ -149,9 +149,8 @@ class ShelfActivity : BaseActivity() {
//binding.tvFoodWeight.text = "0克" //binding.tvFoodWeight.text = "0克"
EventBus.getDefault().post(ClearShelfEvent(shelfModel!!.deviceNo, tempWeight)) EventBus.getDefault().post(ClearShelfEvent(shelfModel!!.deviceNo, tempWeight))
} }
sendCmd(DEVICE_INFO_CMD)
lifecycleScope.launch { lifecycleScope.launch {
SerialPortManager.send(DEVICE_INFO_CMD)
repeatOnLifecycle(Lifecycle.State.STARTED) { repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.getGoodsListUiState.collect { state -> viewModel.getGoodsListUiState.collect { state ->
when (state) { when (state) {
@@ -360,8 +359,8 @@ class ShelfActivity : BaseActivity() {
public fun receiveWeightEvent(event: SendWeightEvent) { public fun receiveWeightEvent(event: SendWeightEvent) {
if (event.shelfNo == (shelfModel?.deviceNo ?: 0)) { if (event.shelfNo == (shelfModel?.deviceNo ?: 0)) {
val intervalTime = System.currentTimeMillis() - startTime val intervalTime = System.currentTimeMillis() - startTime
Log.d(TAG, "receiveWeightEvent: ${event.shelfNo}号货架重量数据获取间隔时间为:$intervalTime")
val weight = event.weight val weight = event.weight
log("编号${event.shelfNo}货架获取重量成功:${weight}克,数据获取间隔时间为:${intervalTime}")
binding.tvFoodWeight.text = if(weight < 1000) "${weight}" else "%.3f千克".format(weight/1000.0) binding.tvFoodWeight.text = if(weight < 1000) "${weight}" else "%.3f千克".format(weight/1000.0)
startTime = System.currentTimeMillis() startTime = System.currentTimeMillis()
window.decorView.postDelayed({ Loading.dismiss() }, 2000) window.decorView.postDelayed({ Loading.dismiss() }, 2000)
@@ -374,13 +373,13 @@ class ShelfActivity : BaseActivity() {
val hex = shelfNo.toString(16).padStart(2, '0').uppercase() val hex = shelfNo.toString(16).padStart(2, '0').uppercase()
Log.d(TAG, "onCreate: hex=$hex") Log.d(TAG, "onCreate: hex=$hex")
val zeroClearingCmd = "${HomeActivity.HEADER}0401${hex}${HomeActivity.FOOTER}" val zeroClearingCmd = "${HomeActivity.HEADER}0401${hex}${HomeActivity.FOOTER}"
Log.d(TAG, "onCreate: zeroClearingCmd=$zeroClearingCmd") log("编号${shelfNo}货架执行清零指令:${zeroClearingCmd}")
SerialPortManager.send(zeroClearingCmd) sendCmd(zeroClearingCmd)
//清零 //清零
realWeight = 0 realWeight = 0
// binding.tvFoodWeight.text = "${realWeight}克" // binding.tvFoodWeight.text = "${realWeight}克"
SerialPortManager.send(DEVICE_INFO_CMD) sendCmd(DEVICE_INFO_CMD)
startTime = System.currentTimeMillis() startTime = System.currentTimeMillis()
Loading.show(this@ShelfActivity) Loading.show(this@ShelfActivity)
// window.decorView.postDelayed({ Loading.dismiss() }, 5000) // window.decorView.postDelayed({ Loading.dismiss() }, 5000)
@@ -0,0 +1,25 @@
package com.shuwei.intelligent.shelves.adapter
import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import com.chad.library.adapter4.BaseQuickAdapter
import com.chad.library.adapter4.viewholder.QuickViewHolder
import com.shuwei.intelligent.shelves.databinding.ListItemLogBinding
class LogAdapter(list: MutableList<String>) :
BaseQuickAdapter<String, LogAdapter.VH>(list) {
inner class VH(var binding: ListItemLogBinding) : QuickViewHolder(binding.root)
override fun onCreateViewHolder(context: Context, parent: ViewGroup, viewType: Int): VH {
val inflater = LayoutInflater.from(context)
val binding = ListItemLogBinding.inflate(inflater, parent, false)
return VH(binding)
}
override fun onBindViewHolder(holder: VH, position: Int, item: String?) {
holder.binding.tvLogName.text = item
}
}
@@ -4,8 +4,8 @@ import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.util.Log
import android.view.View import android.view.View
import android.widget.TextView
import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
@@ -13,19 +13,22 @@ import androidx.core.content.ContextCompat
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.gyf.immersionbar.BarHide import com.gyf.immersionbar.BarHide
import com.gyf.immersionbar.ImmersionBar import com.gyf.immersionbar.ImmersionBar
import com.shuwei.intelligent.shelves.HomeActivity import com.shuwei.intelligent.shelves.activity.HomeActivity
import com.shuwei.intelligent.shelves.HomeActivity.Companion.LIGHT_CLOSE import com.shuwei.intelligent.shelves.activity.HomeActivity.Companion.LIGHT_CLOSE
import com.shuwei.intelligent.shelves.HomeActivity.Companion.LIGHT_OPEN import com.shuwei.intelligent.shelves.activity.HomeActivity.Companion.LIGHT_OPEN
import com.shuwei.intelligent.shelves.R import com.shuwei.intelligent.shelves.R
import com.shuwei.intelligent.shelves.SettingActivity import com.shuwei.intelligent.shelves.activity.LogActivity
import com.shuwei.intelligent.shelves.activity.SettingActivity
import com.shuwei.intelligent.shelves.databinding.ActivityBaseBinding import com.shuwei.intelligent.shelves.databinding.ActivityBaseBinding
import com.shuwei.intelligent.shelves.serial.SerialPortManager import com.shuwei.intelligent.shelves.serial.SerialPortManager
import com.shuwei.intelligent.shelves.utils.FileLogger
import com.shuwei.intelligent.shelves.utils.ext.clickWithDebounce import com.shuwei.intelligent.shelves.utils.ext.clickWithDebounce
import com.shuwei.intelligent.shelves.utils.ext.startActivity import com.shuwei.intelligent.shelves.utils.ext.startActivity
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
open class BaseActivity : AppCompatActivity() { open class BaseActivity : AppCompatActivity() {
val TAG = this.javaClass.simpleName
private lateinit var binding: ActivityBaseBinding private lateinit var binding: ActivityBaseBinding
private var launcher: ActivityResultLauncher<Intent>? = null private var launcher: ActivityResultLauncher<Intent>? = null
@@ -143,7 +146,7 @@ open class BaseActivity : AppCompatActivity() {
if (tag == isOpen) { if (tag == isOpen) {
return return
} }
SerialPortManager.send(if (isOpen) LIGHT_OPEN else LIGHT_CLOSE) sendCmd(if (isOpen) LIGHT_OPEN else LIGHT_CLOSE)
binding.tvLeftStatus.tag = isOpen binding.tvLeftStatus.tag = isOpen
} }
@@ -154,8 +157,24 @@ open class BaseActivity : AppCompatActivity() {
switchLight(tag.not()) switchLight(tag.not())
} }
binding.tvRightStatus.clickWithDebounce { binding.tvRightStatus.clickWithDebounce {
startActivity<SettingActivity> { } //startActivity<SettingActivity> { }
startActivity<LogActivity> {
putExtra(LogActivity.IS_LOG_DIR, true)
}
} }
} }
fun sendCmd(cmd:String) {
lifecycleScope.launch {
SerialPortManager.send(cmd)
}
}
val fileLogger by lazy { FileLogger(this) }
fun log(message:String) {
Log.d(TAG, message)
fileLogger.log(message)
}
} }
@@ -21,36 +21,32 @@ object SerialPortManager {
private val scope = CoroutineScope(Dispatchers.IO) private val scope = CoroutineScope(Dispatchers.IO)
// 打开串口 // 打开串口
fun open(): Boolean { suspend fun open(): Boolean {
lifecycleScope.launch { return try {
return try { serialPort = SerialPortFactory.create().apply {
serialPort = SerialPortFactory.create().apply { open(
open( portName = portName,
portName = portName, baudRate = baudRate,
baudRate = baudRate, dataBits = 8,
dataBits = 8, stopBits = 1,
stopBits = 1, parity = SerialPort.PARITY_NONE
parity = SerialPort.PARITY_NONE )
) } as AndroidSerialPort?
} as AndroidSerialPort? true
true } catch (e: Exception) {
} catch (e: Exception) { e.printStackTrace()
e.printStackTrace() false
false
}
} }
} }
// 发送数据 // 发送数据
fun send(data: String): Boolean { suspend fun send(data: String): Boolean {
lifecycleScope.launch { return try {
return try { serialPort?.write(data.hexToBytes())
serialPort?.write(data.hexToBytes()) true
true } catch (e: Exception) {
} catch (e: Exception) { e.printStackTrace()
e.printStackTrace() false
false
}
} }
} }
@@ -4,7 +4,7 @@ import android.content.Context
import android.util.Log import android.util.Log
import androidx.work.CoroutineWorker import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import com.shuwei.intelligent.shelves.HomeActivity import com.shuwei.intelligent.shelves.activity.HomeActivity
import com.shuwei.intelligent.shelves.serial.SerialPortManager import com.shuwei.intelligent.shelves.serial.SerialPortManager
class SyncDeviceTask(appContext: Context, workerParams: WorkerParameters) : class SyncDeviceTask(appContext: Context, workerParams: WorkerParameters) :
@@ -14,7 +14,7 @@ class SyncDeviceTask(appContext: Context, workerParams: WorkerParameters) :
private const val TAG = "SyncDeviceTask" private const val TAG = "SyncDeviceTask"
} }
override fun doWork(): Result { override suspend fun doWork(): Result {
return try { return try {
// 执行后台任务逻辑 // 执行后台任务逻辑
Log.d(TAG, "doWork: ----------SyncDeviceTask----------") Log.d(TAG, "doWork: ----------SyncDeviceTask----------")
@@ -5,7 +5,7 @@ import android.util.Log
import androidx.work.CoroutineWorker import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import com.shuwei.intelligent.shelves.App import com.shuwei.intelligent.shelves.App
import com.shuwei.intelligent.shelves.HomeActivity import com.shuwei.intelligent.shelves.activity.HomeActivity
import com.shuwei.intelligent.shelves.model.ShelfBody import com.shuwei.intelligent.shelves.model.ShelfBody
import com.shuwei.intelligent.shelves.net.apiService import com.shuwei.intelligent.shelves.net.apiService
import com.shuwei.intelligent.shelves.utils.ext.toJsonString import com.shuwei.intelligent.shelves.utils.ext.toJsonString
@@ -3,7 +3,7 @@ package com.shuwei.intelligent.shelves.utils
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import com.shuwei.intelligent.shelves.InitActivity import com.shuwei.intelligent.shelves.activity.InitActivity
import kotlin.jvm.java import kotlin.jvm.java
class BootReceiver : BroadcastReceiver() { class BootReceiver : BroadcastReceiver() {
@@ -0,0 +1,109 @@
package com.shuwei.intelligent.shelves.utils
import android.content.Context
import java.io.*
import java.text.SimpleDateFormat
import java.util.*
/**
* 文件日志记录器
* 功能:保存日志到本地文件,每条数据一行,每天生成新文件
*/
class FileLogger(private val context: Context) {
companion object {
private const val LOG_DIR = "logs"
private const val FILE_PREFIX = "log_"
private const val FILE_EXTENSION = ".txt"
private val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
private val timeFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault())
}
/**
* 记录日志
* @param message 日志消息
* @param level 日志级别
*/
fun log(message: String, level: LogLevel = LogLevel.INFO) {
try {
val logEntry = formatLogEntry(message, level)
writeToFile(logEntry)
} catch (e: IOException) {
e.printStackTrace()
}
}
/**
* 格式化日志条目
*/
private fun formatLogEntry(message: String, level: LogLevel): String {
val timestamp = timeFormat.format(Date())
return "[$timestamp] [$level] $message\n"
}
/**
* 写入文件
*/
private fun writeToFile(logEntry: String) {
// val path = File(context.filesDir, LOG_DIR)
// if (path.exists().not()) {
// path.mkdirs()
// }
val fileName = getCurrentDateFileName()
// val file = File(path, fileName)
// if (file.exists().not()) {
// file.createNewFile()
// }
val output = context.openFileOutput(fileName, Context.MODE_APPEND)
val writer = BufferedWriter(OutputStreamWriter(output))
writer.use {
it.write(logEntry)
}
}
/**
* 获取当前日期对应的文件名
*/
private fun getCurrentDateFileName(): String {
val date = dateFormat.format(Date())
return "${FILE_PREFIX}${date}${FILE_EXTENSION}"
}
/**
* 读取指定日期的日志文件
* @param date 日期字符串,格式:yyyy-MM-dd
*/
fun readLogsByDate(date: String): List<String> {
val fileName = "${FILE_PREFIX}${date}${FILE_EXTENSION}"
return readLogsByName(fileName)
}
fun readLogsByName(fileName: String): List<String> {
return try {
val input = context.openFileInput(fileName)
val reader = BufferedReader(InputStreamReader(input))
reader.useLines { lines ->
lines.toList()
}
} catch (e: FileNotFoundException) {
emptyList()
}
}
/**
* 获取所有日志文件列表
*/
fun getLogFiles(): List<String> {
return context.fileList()
.filter { it.startsWith(FILE_PREFIX) && it.endsWith(FILE_EXTENSION) }
//.map { it.replace(FILE_EXTENSION, "") }
}
/**
* 日志级别枚举
*/
enum class LogLevel {
DEBUG, INFO, WARN, ERROR
}
}
@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center_vertical">
<ImageView
android:id="@+id/ivBack"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_gravity="top|start"
android:layout_marginStart="12dp"
android:layout_marginTop="12dp"
android:src="@drawable/ic_back_512"
tools:ignore="ContentDescription" />
<TextView
android:id="@+id/tvTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
tools:text="设置"
android:textColor="@color/black"
android:textSize="28sp" />
</FrameLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvLogList"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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="60dp">
<TextView
android:id="@+id/tvLogName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="30dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="@color/white"
android:textSize="20sp"
tools:text="2025-11-18" />
<com.google.android.material.divider.MaterialDivider
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_gravity="bottom"
android:layout_marginHorizontal="30dp"
app:dividerColor="@color/gray_edit" />
</FrameLayout>
@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center_vertical">
<ImageView
android:id="@+id/ivBack"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_gravity="top|start"
android:layout_marginStart="12dp"
android:layout_marginTop="12dp"
android:src="@drawable/ic_back_512"
tools:ignore="ContentDescription" />
<TextView
android:id="@+id/tvTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
tools:text="设置"
android:textColor="@color/black"
android:textSize="28sp" />
</FrameLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvLogList"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
+26
View File
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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="60dp">
<TextView
android:id="@+id/tvLogName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="30dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="@color/white"
android:textSize="20sp"
tools:text="2025-11-18" />
<com.google.android.material.divider.MaterialDivider
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_gravity="bottom"
android:layout_marginHorizontal="30dp"
app:dividerColor="@color/gray_edit" />
</FrameLayout>