kopia lustrzana https://github.com/meshtastic/Meshtastic-Android
progress on keeeping gps off
rodzic
aab062b06f
commit
5b83320a69
1
TODO.md
1
TODO.md
|
@ -7,6 +7,7 @@ the channel is encrypted, you can share the the channel key with others by qr co
|
|||
* take video
|
||||
* make a working currently vs not working list
|
||||
|
||||
* show offline nodes as greyed out
|
||||
* make node list view not look like ass
|
||||
* record analytics events when radio connects/disconnects, include # of nodes in mesh
|
||||
* make channel button look like a button
|
||||
|
|
|
@ -37,11 +37,17 @@ data class MeshUser(val id: String, val longName: String, val shortName: String)
|
|||
}
|
||||
}
|
||||
|
||||
data class Position(val latitude: Double, val longitude: Double, val altitude: Int) :
|
||||
data class Position(
|
||||
val latitude: Double,
|
||||
val longitude: Double,
|
||||
val altitude: Int,
|
||||
val time: Int = (System.currentTimeMillis() / 1000).toInt() // default to current time in secs
|
||||
) :
|
||||
Parcelable {
|
||||
constructor(parcel: Parcel) : this(
|
||||
parcel.readDouble(),
|
||||
parcel.readDouble(),
|
||||
parcel.readInt(),
|
||||
parcel.readInt()
|
||||
) {
|
||||
}
|
||||
|
@ -56,6 +62,7 @@ data class Position(val latitude: Double, val longitude: Double, val altitude: I
|
|||
parcel.writeDouble(latitude)
|
||||
parcel.writeDouble(longitude)
|
||||
parcel.writeInt(altitude)
|
||||
parcel.writeInt(time)
|
||||
}
|
||||
|
||||
override fun describeContents(): Int {
|
||||
|
@ -77,17 +84,32 @@ data class Position(val latitude: Double, val longitude: Double, val altitude: I
|
|||
data class NodeInfo(
|
||||
val num: Int, // This is immutable, and used as a key
|
||||
var user: MeshUser? = null,
|
||||
var position: Position? = null,
|
||||
var lastSeen: Int? = null
|
||||
var position: Position? = null
|
||||
) : Parcelable {
|
||||
constructor(parcel: Parcel) : this(
|
||||
parcel.readInt(),
|
||||
parcel.readParcelable(MeshUser::class.java.classLoader),
|
||||
parcel.readParcelable(Position::class.java.classLoader),
|
||||
parcel.readValue(Int::class.java.classLoader) as? Int
|
||||
parcel.readParcelable(Position::class.java.classLoader)
|
||||
) {
|
||||
}
|
||||
|
||||
/// Return the last time we've seen this node in secs since 1970
|
||||
val lastSeen get() = position?.time ?: 0
|
||||
|
||||
/**
|
||||
* true if the device was heard from recently
|
||||
*
|
||||
* Note: if a node has never had its time set, it will have a time of zero. In that
|
||||
* case assume it is online - so that we will start sending GPS updates
|
||||
*/
|
||||
val isOnline: Boolean
|
||||
get() {
|
||||
val now = System.currentTimeMillis() / 1000
|
||||
// FIXME - use correct timeout from the device settings
|
||||
val timeout = 5 * 60
|
||||
return (now - lastSeen <= timeout) || lastSeen == 0
|
||||
}
|
||||
|
||||
/// @return distance in meters to some other node (or null if unknown)
|
||||
fun distance(o: NodeInfo?): Int? {
|
||||
val p = position
|
||||
|
@ -111,7 +133,6 @@ data class NodeInfo(
|
|||
parcel.writeInt(num)
|
||||
parcel.writeParcelable(user, flags)
|
||||
parcel.writeParcelable(position, flags)
|
||||
parcel.writeValue(lastSeen)
|
||||
}
|
||||
|
||||
override fun describeContents(): Int {
|
||||
|
|
|
@ -23,8 +23,7 @@ object NodeDB {
|
|||
"Kevin MesterNoLoc",
|
||||
"KLO"
|
||||
),
|
||||
null,
|
||||
12345
|
||||
null
|
||||
)
|
||||
|
||||
val testNodes = testPositions.mapIndexed { index, it ->
|
||||
|
@ -35,8 +34,7 @@ object NodeDB {
|
|||
"Kevin Mester$index",
|
||||
"KM$index"
|
||||
),
|
||||
it,
|
||||
12345
|
||||
it
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -132,57 +132,62 @@ class MeshService : Service(), Logging {
|
|||
private var fusedLocationClient: FusedLocationProviderClient? = null
|
||||
|
||||
/**
|
||||
* start our location requests
|
||||
* start our location requests (if they weren't already running)
|
||||
*
|
||||
* per https://developer.android.com/training/location/change-location-settings
|
||||
*/
|
||||
@SuppressLint("MissingPermission")
|
||||
private fun startLocationRequests() {
|
||||
val request = LocationRequest.create().apply {
|
||||
interval =
|
||||
60 * 1000 // FIXME, do more like once every 5 mins while we are connected to our radio _and_ someone else is in the mesh
|
||||
if (fusedLocationClient == null) {
|
||||
val request = LocationRequest.create().apply {
|
||||
interval =
|
||||
20 * 1000 // FIXME, do more like once every 5 mins while we are connected to our radio _and_ someone else is in the mesh
|
||||
|
||||
priority = LocationRequest.PRIORITY_HIGH_ACCURACY
|
||||
priority = LocationRequest.PRIORITY_HIGH_ACCURACY
|
||||
}
|
||||
val builder = LocationSettingsRequest.Builder().addLocationRequest(request)
|
||||
val locationClient = LocationServices.getSettingsClient(this)
|
||||
val locationSettingsResponse = locationClient.checkLocationSettings(builder.build())
|
||||
|
||||
locationSettingsResponse.addOnSuccessListener {
|
||||
debug("We are now successfully listening to the GPS")
|
||||
}
|
||||
|
||||
locationSettingsResponse.addOnFailureListener { exception ->
|
||||
error("Failed to listen to GPS")
|
||||
if (exception is ResolvableApiException) {
|
||||
exceptionReporter {
|
||||
// Location settings are not satisfied, but this can be fixed
|
||||
// by showing the user a dialog.
|
||||
|
||||
// FIXME
|
||||
// Show the dialog by calling startResolutionForResult(),
|
||||
// and check the result in onActivityResult().
|
||||
/* exception.startResolutionForResult(
|
||||
this@MainActivity,
|
||||
REQUEST_CHECK_SETTINGS
|
||||
) */
|
||||
}
|
||||
} else
|
||||
reportException(exception)
|
||||
}
|
||||
|
||||
val client = LocationServices.getFusedLocationProviderClient(this)
|
||||
|
||||
|
||||
// FIXME - should we use Looper.myLooper() in the third param per https://github.com/android/location-samples/blob/432d3b72b8c058f220416958b444274ddd186abd/LocationUpdatesForegroundService/app/src/main/java/com/google/android/gms/location/sample/locationupdatesforegroundservice/LocationUpdatesService.java
|
||||
client.requestLocationUpdates(request, locationCallback, null)
|
||||
|
||||
fusedLocationClient = client
|
||||
}
|
||||
val builder = LocationSettingsRequest.Builder().addLocationRequest(request)
|
||||
val locationClient = LocationServices.getSettingsClient(this)
|
||||
val locationSettingsResponse = locationClient.checkLocationSettings(builder.build())
|
||||
|
||||
locationSettingsResponse.addOnSuccessListener {
|
||||
debug("We are now successfully listening to the GPS")
|
||||
}
|
||||
|
||||
locationSettingsResponse.addOnFailureListener { exception ->
|
||||
error("Failed to listen to GPS")
|
||||
if (exception is ResolvableApiException) {
|
||||
exceptionReporter {
|
||||
// Location settings are not satisfied, but this can be fixed
|
||||
// by showing the user a dialog.
|
||||
|
||||
// FIXME
|
||||
// Show the dialog by calling startResolutionForResult(),
|
||||
// and check the result in onActivityResult().
|
||||
/* exception.startResolutionForResult(
|
||||
this@MainActivity,
|
||||
REQUEST_CHECK_SETTINGS
|
||||
) */
|
||||
}
|
||||
} else
|
||||
reportException(exception)
|
||||
}
|
||||
|
||||
val client = LocationServices.getFusedLocationProviderClient(this)
|
||||
|
||||
|
||||
// FIXME - should we use Looper.myLooper() in the third param per https://github.com/android/location-samples/blob/432d3b72b8c058f220416958b444274ddd186abd/LocationUpdatesForegroundService/app/src/main/java/com/google/android/gms/location/sample/locationupdatesforegroundservice/LocationUpdatesService.java
|
||||
client.requestLocationUpdates(request, locationCallback, null)
|
||||
|
||||
fusedLocationClient = client
|
||||
}
|
||||
|
||||
private fun stopLocationRequests() {
|
||||
fusedLocationClient?.removeLocationUpdates(locationCallback)
|
||||
fusedLocationClient = null
|
||||
if (fusedLocationClient != null) {
|
||||
debug("Stopping location requests")
|
||||
fusedLocationClient?.removeLocationUpdates(locationCallback)
|
||||
fusedLocationClient = null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -386,7 +391,11 @@ class MeshService : Service(), Logging {
|
|||
id
|
||||
)
|
||||
|
||||
// ?: getOrCreateNodeInfo(10) // FIXME hack for now - throw IdNotFoundException(id)
|
||||
|
||||
/**
|
||||
* How many nodes are currently online (including our local node)
|
||||
*/
|
||||
private val numOnlineNodes get() = nodeDBbyNodeNum.values.count { it.isOnline }
|
||||
|
||||
private fun toNodeNum(id: String) = toNodeInfo(id).num
|
||||
|
||||
|
@ -497,7 +506,9 @@ class MeshService : Service(), Logging {
|
|||
it.position = Position(
|
||||
p.latitude,
|
||||
p.longitude,
|
||||
p.altitude
|
||||
p.altitude,
|
||||
if (p.time != 0) p.time else it.position?.time
|
||||
?: 0 // if this position didn't include time, just keep our old one
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -512,11 +523,11 @@ class MeshService : Service(), Logging {
|
|||
|
||||
val p = packet.payload
|
||||
|
||||
// Update our last seen based on any valid timestamps
|
||||
if (packet.rxTime != 0) {
|
||||
updateNodeInfo(fromNum) {
|
||||
it.lastSeen = packet.rxTime
|
||||
}
|
||||
// Update our last seen based on any valid timestamps. If the device didn't provide a timestamp make one
|
||||
val lastSeen =
|
||||
if (packet.rxTime != 0) packet.rxTime else (System.currentTimeMillis() / 1000).toInt()
|
||||
updateNodeInfo(fromNum) {
|
||||
it.position = it.position?.copy(time = lastSeen)
|
||||
}
|
||||
|
||||
when (p.variantCase.number) {
|
||||
|
@ -530,6 +541,8 @@ class MeshService : Service(), Logging {
|
|||
handleReceivedUser(fromNum, p.user)
|
||||
else -> TODO("Unexpected SubPacket variant")
|
||||
}
|
||||
|
||||
onNodeDBChanged()
|
||||
}
|
||||
|
||||
/// We are reconnecting to a radio, redownload the full state. This operation might take hundreds of milliseconds
|
||||
|
@ -566,17 +579,32 @@ class MeshService : Service(), Logging {
|
|||
it.position = Position(
|
||||
info.position.latitude,
|
||||
info.position.longitude,
|
||||
info.position.altitude
|
||||
info.position.altitude,
|
||||
info.position.time
|
||||
)
|
||||
|
||||
it.lastSeen = info.lastSeen
|
||||
}
|
||||
|
||||
// advance to next
|
||||
infoBytes = connectedRadio.readNodeInfo()
|
||||
}
|
||||
|
||||
onNodeDBChanged()
|
||||
}
|
||||
|
||||
/// If we just changed our nodedb, we might want to do somethings
|
||||
private fun onNodeDBChanged() {
|
||||
// we don't ask for GPS locations from android if our device has a built in GPS
|
||||
if (!myNodeInfo!!.hasGPS) {
|
||||
// If we have at least one other person in the mesh, send our GPS position otherwise stop listening to GPS
|
||||
|
||||
if (numOnlineNodes >= 2)
|
||||
startLocationRequests()
|
||||
else
|
||||
stopLocationRequests()
|
||||
} else
|
||||
debug("Our radio has a built in GPS, so not reading GPS in phone")
|
||||
}
|
||||
|
||||
/// Called when we gain/lose connection to our radio
|
||||
private fun onConnectionChanged(c: Boolean) {
|
||||
|
@ -586,12 +614,6 @@ class MeshService : Service(), Logging {
|
|||
// Do our startup init
|
||||
try {
|
||||
reinitFromRadio()
|
||||
|
||||
// we don't ask for GPS locations from android if our device has a built in GPS
|
||||
if (!myNodeInfo!!.hasGPS)
|
||||
startLocationRequests()
|
||||
else
|
||||
debug("Our radio has a built in GPS, so not reading GPS in phone")
|
||||
} catch (ex: RemoteException) {
|
||||
// It seems that when the ESP32 goes offline it can briefly come back for a 100ms ish which
|
||||
// causes the phone to try and reconnect. If we fail downloading our initial radio state we don't want to
|
||||
|
@ -662,6 +684,7 @@ class MeshService : Service(), Logging {
|
|||
it.latitude = lat
|
||||
it.longitude = lon
|
||||
it.altitude = alt
|
||||
it.time = (System.currentTimeMillis() / 1000).toInt() // Include our current timestamp
|
||||
}.build()
|
||||
|
||||
// encapsulate our payload in the proper protobufs and fire it off
|
||||
|
|
|
@ -62,6 +62,9 @@ message Position {
|
|||
|
||||
/// true if this position came from the GPS inside the esp32 board, false if it was from a helper app on the phone
|
||||
bool from_hardware = 5;
|
||||
|
||||
/// This is usually not sent over the mesh (to save space), but it is sent from the phone so that the local device can set its RTC
|
||||
uint32 time = 6; // seconds since 1970
|
||||
}
|
||||
|
||||
// a data message to forward to an external app (or possibly also be consumed internally in the case of CLEAR_TEXT and CLEAR_READACK
|
||||
|
@ -192,6 +195,9 @@ message RadioConfig {
|
|||
// Send our owner info at least this often (also we always send once at boot - to rejoin the mesh)
|
||||
uint32 send_owner_secs = 2;
|
||||
|
||||
/// If we miss this many owner messages from a node, we declare the node offline (defaults to 3 - to allow for some lost packets)
|
||||
uint32 num_missed_to_fail = 3;
|
||||
|
||||
// If true, radio should not try to be smart about what packets to queue to the phone
|
||||
bool keep_all_packets = 100;
|
||||
|
||||
|
@ -229,11 +235,9 @@ SET_CONFIG (switches device to a new set of radio params and preshared key, drop
|
|||
message NodeInfo {
|
||||
int32 num = 1; // the node number
|
||||
User user = 2;
|
||||
Position position = 3;
|
||||
|
||||
// Times are typically not sent over the mesh, but they will be added to any Packet (chain of SubPacket)
|
||||
// sent to the phone (so the phone can know exact time of reception)
|
||||
uint32 last_seen = 4; // seconds since 1970
|
||||
/// This position data will also contain a time last seen
|
||||
Position position = 3;
|
||||
|
||||
/// Returns the Signal-to-noise ratio (SNR) of the last received message, as measured
|
||||
/// by the receiver.
|
||||
|
@ -295,10 +299,10 @@ message DeviceState {
|
|||
|
||||
/// We bump up the integer values in this enum to indicate minimum levels of encodings for saved files
|
||||
/// if your file is below the Minimum you should discard it.
|
||||
Minimum = 13;
|
||||
Minimum = 15;
|
||||
|
||||
/// The current value we are using for saved files
|
||||
Current = 13;
|
||||
Current = 15;
|
||||
};
|
||||
|
||||
/// A version integer used to invalidate old save files when we make incompatible changes
|
||||
|
|
Ładowanie…
Reference in New Issue