begin support for message status

This commit is contained in:
geeksville 2020-05-30 14:38:16 -07:00
parent d25223ba71
commit 67d95039bf
9 changed files with 105 additions and 69 deletions

View file

@ -0,0 +1,57 @@
package com.geeksville.mesh
import android.os.Parcelable
import kotlinx.android.parcel.Parcelize
import kotlinx.serialization.Serializable
@Parcelize
enum class MessageStatus : Parcelable {
UNKNOWN, // Not set for this message
RECEIVED, // Came in from the mesh
QUEUED, // Waiting to send to the mesh as soon as we connect to the device
ENROUTE, // Delivered to the radio, but no ACK or NAK received
DELIVERED // We received an ack
}
/**
* A parcelable version of the protobuf MeshPacket + Data subpacket.
*/
@Serializable
@Parcelize
data class DataPacket(
val from: String, // a nodeID string
val to: String, // a nodeID string
val rxTime: Long, // msecs since 1970
val id: Int,
val dataType: Int,
val bytes: ByteArray,
val status: MessageStatus = MessageStatus.UNKNOWN
) : Parcelable {
// Autogenerated comparision, because we have a byte array
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as DataPacket
if (from != other.from) return false
if (to != other.to) return false
if (rxTime != other.rxTime) return false
if (id != other.id) return false
if (dataType != other.dataType) return false
if (!bytes.contentEquals(other.bytes)) return false
return true
}
override fun hashCode(): Int {
var result = from.hashCode()
result = 31 * result + to.hashCode()
result = 31 * result + rxTime.hashCode()
result = 31 * result + id
result = 31 * result + dataType
result = 31 * result + bytes.contentHashCode()
return result
}
}

View file

@ -3,73 +3,22 @@ package com.geeksville.mesh
import android.os.Parcelable
import com.geeksville.mesh.ui.bearing
import com.geeksville.mesh.ui.latLongToMeter
import com.geeksville.util.anonymize
import kotlinx.android.parcel.Parcelize
import kotlinx.serialization.Serializable
/**
* When printing strings to logs sometimes we want to print useful debugging information about users
* or positions. But we don't want to leak things like usernames or locations. So this function
* if given a string, will return a string which is a maximum of three characters long, taken from the tail
* of the string. Which should effectively hide real usernames and locations,
* but still let us see if values were zero, empty or different.
*/
val Any?.anonymized: String
get() = if (this != null) ("..." + this.toString().takeLast(3)) else "null"
//
// model objects that directly map to the corresponding protobufs
//
/**
* A parcelable version of the protobuf MeshPacket + Data subpacket.
*/
@Serializable
@Parcelize
data class DataPacket(
val from: String, // a nodeID string
val to: String, // a nodeID string
val rxTime: Long, // msecs since 1970
val id: Int,
val dataType: Int,
val bytes: ByteArray
) : Parcelable {
// Autogenerated comparision, because we have a byte array
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as DataPacket
if (from != other.from) return false
if (to != other.to) return false
if (rxTime != other.rxTime) return false
if (id != other.id) return false
if (dataType != other.dataType) return false
if (!bytes.contentEquals(other.bytes)) return false
return true
}
override fun hashCode(): Int {
var result = from.hashCode()
result = 31 * result + to.hashCode()
result = 31 * result + rxTime.hashCode()
result = 31 * result + id
result = 31 * result + dataType
result = 31 * result + bytes.contentHashCode()
return result
}
}
@Serializable
@Parcelize
data class MeshUser(val id: String, val longName: String, val shortName: String) :
Parcelable {
override fun toString(): String {
return "MeshUser(id=${id.anonymized}, longName=${longName.anonymized}, shortName=${shortName.anonymized})"
return "MeshUser(id=${id.anonymize}, longName=${longName.anonymize}, shortName=${shortName.anonymize})"
}
}
@ -106,7 +55,7 @@ data class Position(
fun bearing(o: Position) = bearing(latitude, longitude, o.latitude, o.longitude)
override fun toString(): String {
return "Position(lat=${latitude.anonymized}, lon=${longitude.anonymized}, alt=${altitude.anonymized}, time=${time})"
return "Position(lat=${latitude.anonymize}, lon=${longitude.anonymize}, alt=${altitude.anonymize}, time=${time})"
}
}

View file

@ -1256,7 +1256,7 @@ class MeshService : Service(), Logging {
* Set our owner with either the new or old API
*/
fun setOwner(myId: String?, longName: String, shortName: String) {
debug("SetOwner $myId : ${longName.anonymized} : $shortName")
debug("SetOwner $myId : ${longName.anonymize} : $shortName")
val user = MeshProtos.User.newBuilder().also {
if (myId != null) // Only set the id if it was provided

View file

@ -19,7 +19,7 @@ import com.geeksville.android.Logging
import com.geeksville.concurrent.DeferredExecution
import com.geeksville.concurrent.handledLaunch
import com.geeksville.mesh.IRadioInterfaceService
import com.geeksville.mesh.anonymized
import com.geeksville.util.anonymize
import com.geeksville.util.exceptionReporter
import com.geeksville.util.ignoreException
import com.geeksville.util.toRemoteExceptions
@ -191,7 +191,7 @@ class RadioInterfaceService : Service(), Logging {
val address = getPrefs(context).getString(DEVADDR_KEY, null)
if (address != null && !allPaired.contains(address)) {
warn("Ignoring stale bond to ${address.anonymized}")
warn("Ignoring stale bond to ${address.anonymize}")
null
} else
address
@ -560,7 +560,7 @@ class RadioInterfaceService : Service(), Logging {
// device is off/not connected)
val device = getBluetoothAdapter(this)?.getRemoteDevice(address)
if (device != null) {
info("Creating radio interface service. device=${address.anonymized}")
info("Creating radio interface service. device=${address.anonymize}")
// Note this constructor also does no comm
val s = SafeBluetooth(this, device)

View file

@ -6,6 +6,8 @@ import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import android.widget.EditText
import android.widget.ImageView
import android.widget.TextView
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager
@ -15,6 +17,7 @@ import com.geeksville.mesh.R
import com.geeksville.mesh.model.TextMessage
import com.geeksville.mesh.model.UIViewModel
import com.geeksville.mesh.service.MeshService
import com.google.android.material.chip.Chip
import kotlinx.android.synthetic.main.adapter_message_layout.view.*
import kotlinx.android.synthetic.main.messages_fragment.*
import java.text.SimpleDateFormat
@ -38,9 +41,10 @@ class MessagesFragment : ScreenFragment("Messages"), Logging {
// Provide a direct reference to each of the views within a data item
// Used to cache the views within the item layout for fast access
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val username = itemView.username
val messageText = itemView.messageText
val messageTime = itemView.messageTime
val username: Chip = itemView.username
val messageText: TextView = itemView.messageText
val messageTime: TextView = itemView.messageTime
val messageStatusIcon: ImageView = itemView.messageStatusIcon
}
private val messagesAdapter = object : RecyclerView.Adapter<ViewHolder>() {

View file

@ -28,10 +28,10 @@ import com.geeksville.android.hideKeyboard
import com.geeksville.concurrent.handledLaunch
import com.geeksville.mesh.MainActivity
import com.geeksville.mesh.R
import com.geeksville.mesh.anonymized
import com.geeksville.mesh.model.UIViewModel
import com.geeksville.mesh.service.MeshService
import com.geeksville.mesh.service.RadioInterfaceService
import com.geeksville.util.anonymize
import com.geeksville.util.exceptionReporter
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.android.synthetic.main.settings_fragment.*
@ -102,7 +102,7 @@ class BTScanModel(app: Application) : AndroidViewModel(app), Logging {
// val isSelected get() = macAddress == selectedMacAddr
override fun toString(): String {
return "BTScanEntry(name=${name.anonymized}, addr=${macAddress.anonymized})"
return "BTScanEntry(name=${name.anonymize}, addr=${macAddress.anonymize})"
}
}
@ -282,7 +282,7 @@ class BTScanModel(app: Application) : AndroidViewModel(app), Logging {
/// Change to a new macaddr selection, updating GUI and radio
fun changeScanSelection(context: MainActivity, newAddr: String) {
info("Changing BT device to ${newAddr.anonymized}")
info("Changing BT device to ${newAddr.anonymize}")
selectedMacAddr = newAddr
changeDeviceSelection(context, newAddr)
}

View file

@ -0,0 +1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M19.21,12.04l-1.53,-0.11 -0.3,-1.5C16.88,7.86 14.62,6 12,6 9.94,6 8.08,7.14 7.12,8.96l-0.5,0.95 -1.07,0.11C3.53,10.24 2,11.95 2,14c0,2.21 1.79,4 4,4h13c1.65,0 3,-1.35 3,-3 0,-1.55 -1.22,-2.86 -2.79,-2.96z"
android:strokeAlpha="0.3"
android:fillAlpha="0.3"/>
<path
android:fillColor="@android:color/white"
android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96zM19,18H6c-2.21,0 -4,-1.79 -4,-4 0,-2.05 1.53,-3.76 3.56,-3.97l1.07,-0.11 0.5,-0.95C8.08,7.14 9.94,6 12,6c2.62,0 4.88,1.86 5.39,4.43l0.3,1.5 1.53,0.11c1.56,0.1 2.78,1.41 2.78,2.96 0,1.65 -1.35,3 -3,3z"/>
</vector>

View file

@ -1,11 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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="wrap_content"
android:layout_margin="4dp"
android:clipToPadding="false">
android:clipToPadding="false"
android:contentDescription="Message delivery status"
android:elevation="2dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
@ -40,9 +41,19 @@
android:id="@+id/messageTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:text="3 min"
app:layout_constraintTop_toBottomOf="@+id/username"
tools:layout_editor_absoluteX="25dp" />
app:layout_constraintStart_toStartOf="@+id/username"
app:layout_constraintTop_toBottomOf="@+id/username" />
<ImageView
android:id="@+id/messageStatusIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
app:layout_constraintBottom_toBottomOf="@+id/messageText"
app:layout_constraintEnd_toEndOf="parent"
app:srcCompat="@drawable/cloud_on" />
</androidx.constraintlayout.widget.ConstraintLayout>

@ -1 +1 @@
Subproject commit 81fee94de954fa4d8d942fff8fb929b9ef981a2f
Subproject commit 90cf0a03708d5cf8a564a8044e0ab2ced6ccef52