sforkowany z mirror/meshtastic-android
rename Packet to MeshLog
rodzic
c2d681b11e
commit
a93d4e1dcd
|
@ -1,7 +1,7 @@
|
|||
package com.geeksville.mesh.database
|
||||
|
||||
import android.app.Application
|
||||
import com.geeksville.mesh.database.dao.PacketDao
|
||||
import com.geeksville.mesh.database.dao.MeshLogDao
|
||||
import com.geeksville.mesh.database.dao.QuickChatActionDao
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
|
@ -18,8 +18,8 @@ class DatabaseModule {
|
|||
MeshtasticDatabase.getDatabase(app)
|
||||
|
||||
@Provides
|
||||
fun providePacketDao(database: MeshtasticDatabase): PacketDao {
|
||||
return database.packetDao()
|
||||
fun provideMeshLogDao(database: MeshtasticDatabase): MeshLogDao {
|
||||
return database.meshLogDao()
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
package com.geeksville.mesh.database
|
||||
|
||||
import com.geeksville.mesh.database.dao.MeshLogDao
|
||||
import com.geeksville.mesh.database.entity.MeshLog
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.withContext
|
||||
import javax.inject.Inject
|
||||
|
||||
class MeshLogRepository @Inject constructor(private val meshLogDaoLazy: dagger.Lazy<MeshLogDao>) {
|
||||
private val meshLogDao by lazy {
|
||||
meshLogDaoLazy.get()
|
||||
}
|
||||
|
||||
suspend fun getAllLogs(): Flow<List<MeshLog>> = withContext(Dispatchers.IO) {
|
||||
meshLogDao.getAllLogs(MAX_ITEMS)
|
||||
}
|
||||
|
||||
suspend fun getAllLogsInReceiveOrder(maxItems: Int = MAX_ITEMS): Flow<List<MeshLog>> = withContext(Dispatchers.IO) {
|
||||
meshLogDao.getAllLogsInReceiveOrder(maxItems)
|
||||
}
|
||||
|
||||
suspend fun insert(log: MeshLog) = withContext(Dispatchers.IO) {
|
||||
meshLogDao.insert(log)
|
||||
}
|
||||
|
||||
suspend fun deleteAll() = withContext(Dispatchers.IO) {
|
||||
meshLogDao.deleteAll()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val MAX_ITEMS = 500
|
||||
}
|
||||
}
|
|
@ -4,14 +4,14 @@ import android.content.Context
|
|||
import androidx.room.Database
|
||||
import androidx.room.Room
|
||||
import androidx.room.RoomDatabase
|
||||
import com.geeksville.mesh.database.dao.PacketDao
|
||||
import com.geeksville.mesh.database.dao.MeshLogDao
|
||||
import com.geeksville.mesh.database.dao.QuickChatActionDao
|
||||
import com.geeksville.mesh.database.entity.Packet
|
||||
import com.geeksville.mesh.database.entity.MeshLog
|
||||
import com.geeksville.mesh.database.entity.QuickChatAction
|
||||
|
||||
@Database(entities = [Packet::class, QuickChatAction::class], version = 2, exportSchema = false)
|
||||
@Database(entities = [MeshLog::class, QuickChatAction::class], version = 3, exportSchema = false)
|
||||
abstract class MeshtasticDatabase : RoomDatabase() {
|
||||
abstract fun packetDao(): PacketDao
|
||||
abstract fun meshLogDao(): MeshLogDao
|
||||
abstract fun quickChatActionDao(): QuickChatActionDao
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
package com.geeksville.mesh.database
|
||||
|
||||
import com.geeksville.mesh.database.dao.PacketDao
|
||||
import com.geeksville.mesh.database.entity.Packet
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.withContext
|
||||
import javax.inject.Inject
|
||||
|
||||
class PacketRepository @Inject constructor(private val packetDaoLazy: dagger.Lazy<PacketDao>) {
|
||||
private val packetDao by lazy {
|
||||
packetDaoLazy.get()
|
||||
}
|
||||
|
||||
suspend fun getAllPackets(): Flow<List<Packet>> = withContext(Dispatchers.IO) {
|
||||
packetDao.getAllPacket(MAX_ITEMS)
|
||||
}
|
||||
|
||||
suspend fun getAllPacketsInReceiveOrder(maxItems: Int = MAX_ITEMS): Flow<List<Packet>> = withContext(Dispatchers.IO) {
|
||||
packetDao.getAllPacketsInReceiveOrder(maxItems)
|
||||
}
|
||||
|
||||
suspend fun insert(packet: Packet) = withContext(Dispatchers.IO) {
|
||||
packetDao.insert(packet)
|
||||
}
|
||||
|
||||
suspend fun deleteAll() = withContext(Dispatchers.IO) {
|
||||
packetDao.deleteAll()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val MAX_ITEMS = 500
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package com.geeksville.mesh.database.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.Query
|
||||
import com.geeksville.mesh.database.entity.MeshLog
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface MeshLogDao {
|
||||
|
||||
@Query("Select * from log order by received_date desc limit 0,:maxItem")
|
||||
fun getAllLogs(maxItem: Int): Flow<List<MeshLog>>
|
||||
|
||||
@Query("Select * from log order by received_date asc limit 0,:maxItem")
|
||||
fun getAllLogsInReceiveOrder(maxItem: Int): Flow<List<MeshLog>>
|
||||
|
||||
@Insert
|
||||
fun insert(log: MeshLog)
|
||||
|
||||
@Query("DELETE from log")
|
||||
fun deleteAll()
|
||||
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
package com.geeksville.mesh.database.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.Query
|
||||
import com.geeksville.mesh.database.entity.Packet
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface PacketDao {
|
||||
|
||||
@Query("Select * from packet order by received_date desc limit 0,:maxItem")
|
||||
fun getAllPacket(maxItem: Int): Flow<List<Packet>>
|
||||
|
||||
@Query("Select * from packet order by received_date asc limit 0,:maxItem")
|
||||
fun getAllPacketsInReceiveOrder(maxItem: Int): Flow<List<Packet>>
|
||||
|
||||
@Insert
|
||||
fun insert(packet: Packet)
|
||||
|
||||
@Query("DELETE from packet")
|
||||
fun deleteAll()
|
||||
|
||||
}
|
|
@ -1,54 +1,54 @@
|
|||
package com.geeksville.mesh.database.entity
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import com.geeksville.mesh.MeshProtos
|
||||
import com.geeksville.mesh.Portnums
|
||||
import com.google.protobuf.TextFormat
|
||||
import java.io.IOException
|
||||
|
||||
|
||||
@Entity(tableName = "packet")
|
||||
data class Packet(@PrimaryKey val uuid: String,
|
||||
@ColumnInfo(name = "type") val message_type: String,
|
||||
@ColumnInfo(name = "received_date") val received_date: Long,
|
||||
@ColumnInfo(name = "message") val raw_message: String
|
||||
) {
|
||||
|
||||
val meshPacket: MeshProtos.MeshPacket?
|
||||
get() {
|
||||
if (message_type == "Packet") {
|
||||
val builder = MeshProtos.MeshPacket.newBuilder()
|
||||
try {
|
||||
TextFormat.getParser().merge(raw_message, builder)
|
||||
return builder.build()
|
||||
} catch (e: IOException) {
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
val nodeInfo: MeshProtos.NodeInfo?
|
||||
get() {
|
||||
if (message_type == "NodeInfo") {
|
||||
val builder = MeshProtos.NodeInfo.newBuilder()
|
||||
try {
|
||||
TextFormat.getParser().merge(raw_message, builder)
|
||||
return builder.build()
|
||||
} catch (e: IOException) {
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
val position: MeshProtos.Position?
|
||||
get() {
|
||||
return meshPacket?.run {
|
||||
if (hasDecoded() && decoded.portnumValue == Portnums.PortNum.POSITION_APP_VALUE) {
|
||||
return MeshProtos.Position.parseFrom(decoded.payload)
|
||||
}
|
||||
return null
|
||||
} ?: nodeInfo?.position
|
||||
}
|
||||
package com.geeksville.mesh.database.entity
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import com.geeksville.mesh.MeshProtos
|
||||
import com.geeksville.mesh.Portnums
|
||||
import com.google.protobuf.TextFormat
|
||||
import java.io.IOException
|
||||
|
||||
|
||||
@Entity(tableName = "log")
|
||||
data class MeshLog(@PrimaryKey val uuid: String,
|
||||
@ColumnInfo(name = "type") val message_type: String,
|
||||
@ColumnInfo(name = "received_date") val received_date: Long,
|
||||
@ColumnInfo(name = "message") val raw_message: String
|
||||
) {
|
||||
|
||||
val meshPacket: MeshProtos.MeshPacket?
|
||||
get() {
|
||||
if (message_type == "Packet") {
|
||||
val builder = MeshProtos.MeshPacket.newBuilder()
|
||||
try {
|
||||
TextFormat.getParser().merge(raw_message, builder)
|
||||
return builder.build()
|
||||
} catch (e: IOException) {
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
val nodeInfo: MeshProtos.NodeInfo?
|
||||
get() {
|
||||
if (message_type == "NodeInfo") {
|
||||
val builder = MeshProtos.NodeInfo.newBuilder()
|
||||
try {
|
||||
TextFormat.getParser().merge(raw_message, builder)
|
||||
return builder.build()
|
||||
} catch (e: IOException) {
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
val position: MeshProtos.Position?
|
||||
get() {
|
||||
return meshPacket?.run {
|
||||
if (hasDecoded() && decoded.portnumValue == Portnums.PortNum.POSITION_APP_VALUE) {
|
||||
return MeshProtos.Position.parseFrom(decoded.payload)
|
||||
}
|
||||
return null
|
||||
} ?: nodeInfo?.position
|
||||
}
|
||||
}
|
|
@ -14,9 +14,9 @@ import androidx.lifecycle.viewModelScope
|
|||
import com.geeksville.mesh.android.Logging
|
||||
import com.geeksville.mesh.*
|
||||
import com.geeksville.mesh.ConfigProtos.Config
|
||||
import com.geeksville.mesh.database.PacketRepository
|
||||
import com.geeksville.mesh.database.MeshLogRepository
|
||||
import com.geeksville.mesh.database.QuickChatActionRepository
|
||||
import com.geeksville.mesh.database.entity.Packet
|
||||
import com.geeksville.mesh.database.entity.MeshLog
|
||||
import com.geeksville.mesh.database.entity.QuickChatAction
|
||||
import com.geeksville.mesh.LocalOnlyProtos.LocalConfig
|
||||
import com.geeksville.mesh.repository.datastore.ChannelSetRepository
|
||||
|
@ -65,15 +65,15 @@ fun getInitials(nameIn: String): String {
|
|||
@HiltViewModel
|
||||
class UIViewModel @Inject constructor(
|
||||
private val app: Application,
|
||||
private val packetRepository: PacketRepository,
|
||||
private val meshLogRepository: MeshLogRepository,
|
||||
private val channelSetRepository: ChannelSetRepository,
|
||||
private val localConfigRepository: LocalConfigRepository,
|
||||
private val quickChatActionRepository: QuickChatActionRepository,
|
||||
private val preferences: SharedPreferences
|
||||
) : ViewModel(), Logging {
|
||||
|
||||
private val _allPacketState = MutableStateFlow<List<Packet>>(emptyList())
|
||||
val allPackets: StateFlow<List<Packet>> = _allPacketState
|
||||
private val _meshLog = MutableStateFlow<List<MeshLog>>(emptyList())
|
||||
val meshLog: StateFlow<List<MeshLog>> = _meshLog
|
||||
|
||||
private val _localConfig = MutableStateFlow<LocalConfig>(LocalConfig.getDefaultInstance())
|
||||
val localConfig: StateFlow<LocalConfig> = _localConfig
|
||||
|
@ -87,8 +87,8 @@ class UIViewModel @Inject constructor(
|
|||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
packetRepository.getAllPackets().collect { packets ->
|
||||
_allPacketState.value = packets
|
||||
meshLogRepository.getAllLogs().collect { logs ->
|
||||
_meshLog.value = logs
|
||||
}
|
||||
}
|
||||
viewModelScope.launch {
|
||||
|
@ -109,8 +109,8 @@ class UIViewModel @Inject constructor(
|
|||
debug("ViewModel created")
|
||||
}
|
||||
|
||||
fun deleteAllPacket() = viewModelScope.launch(Dispatchers.IO) {
|
||||
packetRepository.deleteAll()
|
||||
fun deleteAllLogs() = viewModelScope.launch(Dispatchers.IO) {
|
||||
meshLogRepository.deleteAll()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -341,7 +341,7 @@ class UIViewModel @Inject constructor(
|
|||
// Packets are ordered by time, we keep most recent position of
|
||||
// our device in localNodePosition.
|
||||
val dateFormat = SimpleDateFormat("yyyy-MM-dd,HH:mm:ss", Locale.getDefault())
|
||||
packetRepository.getAllPacketsInReceiveOrder(Int.MAX_VALUE).first().forEach { packet ->
|
||||
meshLogRepository.getAllLogsInReceiveOrder(Int.MAX_VALUE).first().forEach { packet ->
|
||||
// If we get a NodeInfo packet, use it to update our position data (if valid)
|
||||
packet.nodeInfo?.let { nodeInfo ->
|
||||
positionToPos.invoke(nodeInfo.position)?.let { _ ->
|
||||
|
|
|
@ -15,8 +15,8 @@ import com.geeksville.mesh.LocalOnlyProtos.LocalConfig
|
|||
import com.geeksville.mesh.MeshProtos.MeshPacket
|
||||
import com.geeksville.mesh.MeshProtos.ToRadio
|
||||
import com.geeksville.mesh.android.hasBackgroundPermission
|
||||
import com.geeksville.mesh.database.PacketRepository
|
||||
import com.geeksville.mesh.database.entity.Packet
|
||||
import com.geeksville.mesh.database.MeshLogRepository
|
||||
import com.geeksville.mesh.database.entity.MeshLog
|
||||
import com.geeksville.mesh.model.DeviceVersion
|
||||
import com.geeksville.mesh.repository.datastore.ChannelSetRepository
|
||||
import com.geeksville.mesh.repository.datastore.LocalConfigRepository
|
||||
|
@ -50,7 +50,7 @@ class MeshService : Service(), Logging {
|
|||
lateinit var dispatchers: CoroutineDispatchers
|
||||
|
||||
@Inject
|
||||
lateinit var packetRepository: Lazy<PacketRepository>
|
||||
lateinit var meshLogRepository: Lazy<MeshLogRepository>
|
||||
|
||||
@Inject
|
||||
lateinit var radioInterfaceService: RadioInterfaceService
|
||||
|
@ -740,13 +740,13 @@ class MeshService : Service(), Logging {
|
|||
val ch = a.getChannelResponse
|
||||
debug("Admin: Received channel ${ch.index}")
|
||||
|
||||
val packetToSave = Packet(
|
||||
val packetToSave = MeshLog(
|
||||
UUID.randomUUID().toString(),
|
||||
"Channel",
|
||||
System.currentTimeMillis(),
|
||||
ch.toString()
|
||||
)
|
||||
insertPacket(packetToSave)
|
||||
insertMeshLog(packetToSave)
|
||||
|
||||
if (ch.index + 1 < mi.maxChannels) {
|
||||
|
||||
|
@ -852,13 +852,13 @@ class MeshService : Service(), Logging {
|
|||
sendToRadio(packet)
|
||||
|
||||
if (packet.hasDecoded()) {
|
||||
val packetToSave = Packet(
|
||||
val packetToSave = MeshLog(
|
||||
UUID.randomUUID().toString(),
|
||||
"Packet",
|
||||
System.currentTimeMillis(),
|
||||
packet.toString()
|
||||
)
|
||||
insertPacket(packetToSave)
|
||||
insertMeshLog(packetToSave)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -898,13 +898,13 @@ class MeshService : Service(), Logging {
|
|||
|
||||
// debug("Recieved: $packet")
|
||||
if (packet.hasDecoded()) {
|
||||
val packetToSave = Packet(
|
||||
val packetToSave = MeshLog(
|
||||
UUID.randomUUID().toString(),
|
||||
"Packet",
|
||||
System.currentTimeMillis(),
|
||||
packet.toString()
|
||||
)
|
||||
insertPacket(packetToSave)
|
||||
insertMeshLog(packetToSave)
|
||||
|
||||
// Update last seen for the node that sent the packet, but also for _our node_ because anytime a packet passes
|
||||
// through our node on the way to the phone that means that local node is also alive in the mesh
|
||||
|
@ -930,11 +930,11 @@ class MeshService : Service(), Logging {
|
|||
}
|
||||
}
|
||||
|
||||
private fun insertPacket(packetToSave: Packet) {
|
||||
private fun insertMeshLog(packetToSave: MeshLog) {
|
||||
serviceScope.handledLaunch {
|
||||
// Do not log, because might contain PII
|
||||
// info("insert: ${packetToSave.message_type} = ${packetToSave.raw_message.toOneLineString()}")
|
||||
packetRepository.get().insert(packetToSave)
|
||||
meshLogRepository.get().insert(packetToSave)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1148,13 +1148,13 @@ class MeshService : Service(), Logging {
|
|||
|
||||
private fun handleDeviceConfig(config: ConfigProtos.Config) {
|
||||
debug("Received config ${config.toOneLineString()}")
|
||||
val packetToSave = Packet(
|
||||
val packetToSave = MeshLog(
|
||||
UUID.randomUUID().toString(),
|
||||
"Config ${config.payloadVariantCase}",
|
||||
System.currentTimeMillis(),
|
||||
config.toString()
|
||||
)
|
||||
insertPacket(packetToSave)
|
||||
insertMeshLog(packetToSave)
|
||||
setLocalConfig(config)
|
||||
}
|
||||
|
||||
|
@ -1190,13 +1190,13 @@ class MeshService : Service(), Logging {
|
|||
private fun handleNodeInfo(info: MeshProtos.NodeInfo) {
|
||||
debug("Received nodeinfo num=${info.num}, hasUser=${info.hasUser()}, hasPosition=${info.hasPosition()}, hasDeviceMetrics=${info.hasDeviceMetrics()}")
|
||||
|
||||
val packetToSave = Packet(
|
||||
val packetToSave = MeshLog(
|
||||
UUID.randomUUID().toString(),
|
||||
"NodeInfo",
|
||||
System.currentTimeMillis(),
|
||||
info.toString()
|
||||
)
|
||||
insertPacket(packetToSave)
|
||||
insertMeshLog(packetToSave)
|
||||
|
||||
logAssert(newNodes.size <= 256) // Sanity check to make sure a device bug can't fill this list forever
|
||||
newNodes.add(info)
|
||||
|
@ -1273,13 +1273,13 @@ class MeshService : Service(), Logging {
|
|||
* Update the nodeinfo (called from either new API version or the old one)
|
||||
*/
|
||||
private fun handleMyInfo(myInfo: MeshProtos.MyNodeInfo) {
|
||||
val packetToSave = Packet(
|
||||
val packetToSave = MeshLog(
|
||||
UUID.randomUUID().toString(),
|
||||
"MyNodeInfo",
|
||||
System.currentTimeMillis(),
|
||||
myInfo.toString()
|
||||
)
|
||||
insertPacket(packetToSave)
|
||||
insertMeshLog(packetToSave)
|
||||
|
||||
rawMyNodeInfo = myInfo
|
||||
regenMyNodeInfo()
|
||||
|
@ -1305,13 +1305,13 @@ class MeshService : Service(), Logging {
|
|||
private fun handleConfigComplete(configCompleteId: Int) {
|
||||
if (configCompleteId == configNonce) {
|
||||
|
||||
val packetToSave = Packet(
|
||||
val packetToSave = MeshLog(
|
||||
UUID.randomUUID().toString(),
|
||||
"ConfigComplete",
|
||||
System.currentTimeMillis(),
|
||||
configCompleteId.toString()
|
||||
)
|
||||
insertPacket(packetToSave)
|
||||
insertMeshLog(packetToSave)
|
||||
|
||||
// This was our config request
|
||||
if (newMyNodeInfo == null || newNodes.isEmpty())
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
package com.geeksville.mesh.ui
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.database.entity.MeshLog
|
||||
import java.text.DateFormat
|
||||
import java.util.*
|
||||
|
||||
class DebugAdapter internal constructor(
|
||||
context: Context
|
||||
) : RecyclerView.Adapter<DebugAdapter.DebugViewHolder>() {
|
||||
|
||||
private val inflater: LayoutInflater = LayoutInflater.from(context)
|
||||
private var logs = emptyList<MeshLog>()
|
||||
|
||||
private val timeFormat: DateFormat =
|
||||
DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM)
|
||||
|
||||
inner class DebugViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
val logTypeView: TextView = itemView.findViewById(R.id.type)
|
||||
val logDateReceivedView: TextView = itemView.findViewById(R.id.dateReceived)
|
||||
val logRawMessage: TextView = itemView.findViewById(R.id.rawMessage)
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DebugViewHolder {
|
||||
val itemView = inflater.inflate(R.layout.adapter_debug_layout, parent, false)
|
||||
return DebugViewHolder(itemView)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: DebugViewHolder, position: Int) {
|
||||
val current = logs[position]
|
||||
holder.logTypeView.text = current.message_type
|
||||
holder.logRawMessage.text = current.raw_message
|
||||
val date = Date(current.received_date)
|
||||
holder.logDateReceivedView.text = timeFormat.format(date)
|
||||
}
|
||||
|
||||
internal fun setLogs(logs: List<MeshLog>) {
|
||||
this.logs = logs
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun getItemCount() = logs.size
|
||||
|
||||
}
|
|
@ -10,14 +10,14 @@ import androidx.lifecycle.asLiveData
|
|||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.databinding.DebugFragmentBinding
|
||||
import com.geeksville.mesh.databinding.FragmentDebugBinding
|
||||
import com.geeksville.mesh.model.UIViewModel
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@AndroidEntryPoint
|
||||
class DebugFragment : Fragment() {
|
||||
|
||||
private var _binding: DebugFragmentBinding? = null
|
||||
private var _binding: FragmentDebugBinding? = null
|
||||
|
||||
// This property is only valid between onCreateView and onDestroyView.
|
||||
private val binding get() = _binding!!
|
||||
|
@ -28,27 +28,27 @@ class DebugFragment : Fragment() {
|
|||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
_binding = DebugFragmentBinding.inflate(inflater, container, false)
|
||||
_binding = FragmentDebugBinding.inflate(inflater, container, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
val recyclerView = view.findViewById<RecyclerView>(R.id.packets_recyclerview)
|
||||
val adapter = PacketListAdapter(requireContext())
|
||||
val recyclerView = view.findViewById<RecyclerView>(R.id.debug_recyclerview)
|
||||
val adapter = DebugAdapter(requireContext())
|
||||
|
||||
recyclerView.adapter = adapter
|
||||
recyclerView.layoutManager = LinearLayoutManager(requireContext())
|
||||
|
||||
binding.clearButton.setOnClickListener {
|
||||
model.deleteAllPacket()
|
||||
model.deleteAllLogs()
|
||||
}
|
||||
|
||||
binding.closeButton.setOnClickListener {
|
||||
parentFragmentManager.popBackStack()
|
||||
}
|
||||
model.allPackets.asLiveData().observe(viewLifecycleOwner) { packets ->
|
||||
packets?.let { adapter.setPackets(it) }
|
||||
model.meshLog.asLiveData().observe(viewLifecycleOwner) { logs ->
|
||||
logs?.let { adapter.setLogs(it) }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
package com.geeksville.mesh.ui
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.database.entity.Packet
|
||||
import java.text.DateFormat
|
||||
import java.util.*
|
||||
|
||||
class PacketListAdapter internal constructor(
|
||||
context: Context
|
||||
) : RecyclerView.Adapter<PacketListAdapter.PacketViewHolder>() {
|
||||
|
||||
private val inflater: LayoutInflater = LayoutInflater.from(context)
|
||||
private var packets = emptyList<Packet>()
|
||||
|
||||
private val timeFormat: DateFormat =
|
||||
DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM)
|
||||
|
||||
inner class PacketViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
val packetTypeView: TextView = itemView.findViewById(R.id.type)
|
||||
val packetDateReceivedView: TextView = itemView.findViewById(R.id.dateReceived)
|
||||
val packetRawMessage: TextView = itemView.findViewById(R.id.rawMessage)
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PacketViewHolder {
|
||||
val itemView = inflater.inflate(R.layout.adapter_packet_layout, parent, false)
|
||||
return PacketViewHolder(itemView)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: PacketViewHolder, position: Int) {
|
||||
val current = packets[position]
|
||||
holder.packetTypeView.text = current.message_type
|
||||
holder.packetRawMessage.text = current.raw_message
|
||||
val date = Date(current.received_date)
|
||||
holder.packetDateReceivedView.text = timeFormat.format(date)
|
||||
}
|
||||
|
||||
internal fun setPackets(packets: List<Packet>) {
|
||||
this.packets = packets
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun getItemCount() = packets.size
|
||||
|
||||
}
|
|
@ -1,86 +1,86 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.google.android.material.card.MaterialCardView 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:id="@+id/adapterPacketLayout"
|
||||
style="@style/Widget.App.CardView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="4dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/type"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/cloudDownloadIcon"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/cloudDownloadIcon"
|
||||
tools:text="NodeInfo" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dateReceived"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/cloudDownloadIcon"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/cloudDownloadIcon"
|
||||
tools:text="9/27/20 21:00:58" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/rawMessage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:fontFamily="monospace"
|
||||
android:singleLine="false"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
||||
android:textIsSelectable="true"
|
||||
android:textSize="8sp"
|
||||
android:typeface="monospace"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/cloudDownloadIcon"
|
||||
app:layout_constraintVertical_weight="1"
|
||||
tools:text="# com.geeksville.mesh.MeshProtos$MeshPacket@1b1ea594\n
|
||||
decoded {\n
|
||||
position {\n
|
||||
altitude: 60\n
|
||||
battery_level: 81\n
|
||||
latitude_i: 411111136\n
|
||||
longitude_i: -711111805\n
|
||||
time: 1600390966\n
|
||||
}\n
|
||||
}\n
|
||||
from: -1409794164\n
|
||||
hop_limit: 3\n
|
||||
id: 1737414295\n
|
||||
rx_snr: 9.5\n
|
||||
rx_time: 316400569\n
|
||||
to: -1409790708" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/cloudDownloadIcon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:alpha="0.4"
|
||||
app:layout_constraintEnd_toStartOf="@+id/dateReceived"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/cloud_download_outline_24"
|
||||
android:contentDescription="TODO"
|
||||
app:tint="@color/colorIconTint" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.google.android.material.card.MaterialCardView 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:id="@+id/adapterDebugLayout"
|
||||
style="@style/Widget.App.CardView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="4dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/type"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/cloudDownloadIcon"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/cloudDownloadIcon"
|
||||
tools:text="NodeInfo" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dateReceived"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/cloudDownloadIcon"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/cloudDownloadIcon"
|
||||
tools:text="9/27/20 21:00:58" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/rawMessage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:fontFamily="monospace"
|
||||
android:singleLine="false"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
||||
android:textIsSelectable="true"
|
||||
android:textSize="8sp"
|
||||
android:typeface="monospace"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/cloudDownloadIcon"
|
||||
app:layout_constraintVertical_weight="1"
|
||||
tools:text="# com.geeksville.mesh.MeshProtos$MeshPacket@1b1ea594\n
|
||||
decoded {\n
|
||||
position {\n
|
||||
altitude: 60\n
|
||||
battery_level: 81\n
|
||||
latitude_i: 411111136\n
|
||||
longitude_i: -711111805\n
|
||||
time: 1600390966\n
|
||||
}\n
|
||||
}\n
|
||||
from: -1409794164\n
|
||||
hop_limit: 3\n
|
||||
id: 1737414295\n
|
||||
rx_snr: 9.5\n
|
||||
rx_time: 316400569\n
|
||||
to: -1409790708" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/cloudDownloadIcon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:alpha="0.4"
|
||||
app:layout_constraintEnd_toStartOf="@+id/dateReceived"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/cloud_download_outline_24"
|
||||
android:contentDescription="TODO"
|
||||
app:tint="@color/colorIconTint" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/packets_recyclerview"
|
||||
android:id="@+id/debug_recyclerview"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="8dp"
|
||||
|
@ -19,7 +19,7 @@
|
|||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/clearButton"
|
||||
tools:itemCount="8"
|
||||
tools:listitem="@layout/adapter_packet_layout" />
|
||||
tools:listitem="@layout/adapter_debug_layout" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/clearButton"
|
||||
|
@ -57,7 +57,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:text="@string/debug_last_messages"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toTopOf="@+id/packets_recyclerview"
|
||||
app:layout_constraintBottom_toTopOf="@+id/debug_recyclerview"
|
||||
app:layout_constraintEnd_toStartOf="@+id/clearButton"
|
||||
app:layout_constraintStart_toEndOf="@+id/closeButton"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
Ładowanie…
Reference in New Issue