diff --git a/app/src/main/java/com/geeksville/mesh/MainActivity.kt b/app/src/main/java/com/geeksville/mesh/MainActivity.kt index 1acff242c..c7879cf1d 100644 --- a/app/src/main/java/com/geeksville/mesh/MainActivity.kt +++ b/app/src/main/java/com/geeksville/mesh/MainActivity.kt @@ -61,11 +61,9 @@ import com.google.android.material.tabs.TabLayoutMediator import com.google.protobuf.InvalidProtocolBufferException import com.vorlonsoft.android.rate.AppRate import com.vorlonsoft.android.rate.StoreType -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import kotlinx.coroutines.cancel +import kotlinx.coroutines.* import java.io.FileOutputStream +import java.lang.Runnable import java.nio.charset.Charset import java.text.DateFormat import java.util.* @@ -556,10 +554,16 @@ class MainActivity : AppCompatActivity(), Logging, CREATE_CSV_FILE -> { if (resultCode == Activity.RESULT_OK) { data?.data?.let { file_uri -> + // model.allPackets is a result of a query, so we need to use observer for + // the query to materialize model.allPackets.observe(this, { packets -> if (packets != null) { - saveMessagesCSV(file_uri, packets) + // no need for observer once got non-null list model.allPackets.removeObservers(this) + // execute on the default thread pool to not block the main thread + CoroutineScope(Dispatchers.Default + Job()).handledLaunch { + saveMessagesCSV(file_uri, packets) + } } }) } @@ -1086,7 +1090,7 @@ class MainActivity : AppCompatActivity(), Logging, applicationContext.contentResolver.openFileDescriptor(file_uri, "w")?.use { FileOutputStream(it.fileDescriptor).use { fs -> // Write header - fs.write(("from,snr,time,dist\n").toByteArray()); + fs.write(("from,rssi,snr,time,dist\n").toByteArray()); // Packets are ordered by time, we keep most recent position of // our device in my_position. var my_position: MeshProtos.Position? = null @@ -1096,13 +1100,9 @@ class MainActivity : AppCompatActivity(), Logging, if (packet_proto.from == myNodeNum) { my_position = position } else if (my_position != null) { - val dist: Int = - positionToMeter(my_position!!, position).roundToInt() - fs.write( - ("${packet_proto.from.toUInt().toString(16)}," + - "${packet_proto.rxSnr},${packet_proto.rxTime},$dist\n") - .toByteArray() - ) + val dist = positionToMeter(my_position!!, position).roundToInt() + fs.write("%x,%d,%f,%d,%d\n".format(packet_proto.from,packet_proto.rxRssi, + packet_proto.rxSnr, packet_proto.rxTime, dist).toByteArray()) } } } diff --git a/app/src/main/java/com/geeksville/mesh/NodeInfo.kt b/app/src/main/java/com/geeksville/mesh/NodeInfo.kt index bd8bacc85..f4864dd91 100644 --- a/app/src/main/java/com/geeksville/mesh/NodeInfo.kt +++ b/app/src/main/java/com/geeksville/mesh/NodeInfo.kt @@ -82,7 +82,9 @@ data class Position( data class NodeInfo( val num: Int, // This is immutable, and used as a key var user: MeshUser? = null, - var position: Position? = null + var position: Position? = null, + var snr: Float = Float.MAX_VALUE, + var rssi: Int = Int.MAX_VALUE ) : Parcelable { /// Return the last time we've seen this node in secs since 1970 diff --git a/app/src/main/java/com/geeksville/mesh/service/MeshService.kt b/app/src/main/java/com/geeksville/mesh/service/MeshService.kt index b58f90d56..68e9e1652 100644 --- a/app/src/main/java/com/geeksville/mesh/service/MeshService.kt +++ b/app/src/main/java/com/geeksville/mesh/service/MeshService.kt @@ -717,6 +717,7 @@ class MeshService : Service(), Logging { // Handle new style position info Portnums.PortNum.POSITION_APP_VALUE -> { val u = MeshProtos.Position.parseFrom(data.payload) + debug("position_app ${packet.from} ${u.toOneLineString()}") handleReceivedPosition(packet.from, u, dataPacket.time) } @@ -827,6 +828,7 @@ class MeshService : Service(), Logging { defaultTime: Long = System.currentTimeMillis() ) { updateNodeInfo(fromNum) { + debug("update ${it.user?.longName} with ${p.toOneLineString()}") it.position = Position(p) updateNodeInfoTime(it, (defaultTime / 1000).toInt()) } @@ -923,6 +925,8 @@ class MeshService : Service(), Logging { updateNodeInfo(fromNum) { // Update our last seen based on any valid timestamps. If the device didn't provide a timestamp make one updateNodeInfoTime(it, rxTime) + it.snr = packet.rxSnr + it.rssi = packet.rxRssi } handleReceivedData(packet) diff --git a/app/src/main/java/com/geeksville/mesh/ui/UsersFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/UsersFragment.kt index 66bc2c270..0649aa429 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/UsersFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/UsersFragment.kt @@ -21,6 +21,7 @@ import com.geeksville.mesh.databinding.NodelistFragmentBinding import com.geeksville.mesh.model.UIViewModel import com.geeksville.util.formatAgo import java.net.URLEncoder +import kotlin.math.roundToInt class UsersFragment : ScreenFragment("Users"), Logging { @@ -41,6 +42,7 @@ class UsersFragment : ScreenFragment("Users"), Logging { val batteryPctView = itemView.batteryPercentageView val lastTime = itemView.lastConnectionView val powerIcon = itemView.batteryIcon + val signalView = itemView.signalView } private val nodesAdapter = object : RecyclerView.Adapter() { @@ -137,10 +139,22 @@ class UsersFragment : ScreenFragment("Users"), Logging { } else { holder.distanceView.visibility = View.INVISIBLE } - renderBattery(n.batteryPctLevel, holder) holder.lastTime.text = formatAgo(n.lastSeen); + + if ((n.num == ourNodeInfo?.num) || (n.snr > 100f)) { + holder.signalView.visibility = View.INVISIBLE + } else { + val text = if (n.rssi < 0) { + "rssi:${n.rssi} snr:${n.snr.roundToInt()}" + } else { + // Older devices do not send rssi. Remove this branch once upgraded past 1.2.1 + "snr:${n.snr.roundToInt()}" + } + holder.signalView.text = text + holder.signalView.visibility = View.VISIBLE + } } private var nodes = arrayOf() diff --git a/app/src/main/proto b/app/src/main/proto index ac26ffdc7..820fa497d 160000 --- a/app/src/main/proto +++ b/app/src/main/proto @@ -1 +1 @@ -Subproject commit ac26ffdc71dad5765124186df5ec38771a0e5240 +Subproject commit 820fa497dfde07e129cad6955bf2f4b2b9cecebc diff --git a/app/src/main/res/layout/adapter_node_layout.xml b/app/src/main/res/layout/adapter_node_layout.xml index f5577aaac..b30bc1394 100644 --- a/app/src/main/res/layout/adapter_node_layout.xml +++ b/app/src/main/res/layout/adapter_node_layout.xml @@ -60,7 +60,7 @@ android:layout_marginBottom="8dp" android:text="@string/sample_coords" android:textAppearance="@style/TextAppearance.AppCompat.Small" - app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toEndOf="@+id/distance_view" app:layout_constraintTop_toBottomOf="@+id/imageView" app:layout_constraintVertical_bias="0.0" /> @@ -92,7 +92,8 @@ android:layout_marginEnd="8dp" android:layout_marginBottom="8dp" android:visibility="visible" - app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintTop_toBottomOf="@id/batteryIcon" + app:layout_constraintBottom_toTopOf="@id/signalView" app:layout_constraintEnd_toStartOf="@+id/lastConnectionView" app:srcCompat="@drawable/ic_antenna_24" /> @@ -106,6 +107,17 @@ app:layout_constraintBottom_toBottomOf="@+id/lastCommIcon" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="@+id/lastCommIcon" /> + + + \ No newline at end of file