Merge pull request #259 from Crotery/master

Display time ago and coords for users
pull/261/head^2
Kevin Hester 2021-03-19 15:01:05 +08:00 zatwierdzone przez GitHub
commit 8a18680d0a
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
9 zmienionych plików z 58 dodań i 40 usunięć

Wyświetl plik

@ -1242,7 +1242,7 @@ class MeshService : Service(), Logging {
MyNodeInfo( MyNodeInfo(
myNodeNum, myNodeNum,
hasGps, hasGps,
hwModel, hwModelDeprecated,
firmwareVersion, firmwareVersion,
firmwareUpdateFilename != null, firmwareUpdateFilename != null,
isBluetoothInterface && SoftwareUpdateService.shouldUpdate( isBluetoothInterface && SoftwareUpdateService.shouldUpdate(
@ -1552,10 +1552,10 @@ class MeshService : Service(), Logging {
*/ */
private fun setFirmwareUpdateFilename(info: MeshProtos.MyNodeInfo) { private fun setFirmwareUpdateFilename(info: MeshProtos.MyNodeInfo) {
firmwareUpdateFilename = try { firmwareUpdateFilename = try {
if (info.region != null && info.firmwareVersion != null && info.hwModel != null) if (info.region != null && info.firmwareVersion != null && info.hwModelDeprecated != null)
SoftwareUpdateService.getUpdateFilename( SoftwareUpdateService.getUpdateFilename(
this, this,
info.hwModel info.hwModelDeprecated
) )
else else
null null

Wyświetl plik

@ -160,7 +160,7 @@ class MockInterface(private val service: RadioInterfaceService) : Logging, IRadi
MeshProtos.FromRadio.newBuilder().apply { MeshProtos.FromRadio.newBuilder().apply {
myInfo = MeshProtos.MyNodeInfo.newBuilder().apply { myInfo = MeshProtos.MyNodeInfo.newBuilder().apply {
myNodeNum = MY_NODE myNodeNum = MY_NODE
hwModel = "Sim" hwModelDeprecated = "Sim"
messageTimeoutMsec = 5 * 60 * 1000 messageTimeoutMsec = 5 * 60 * 1000
firmwareVersion = service.getString(R.string.cur_firmware_version) firmwareVersion = service.getString(R.string.cur_firmware_version)
numBands = 13 numBands = 13

Wyświetl plik

@ -13,6 +13,7 @@ import com.geeksville.android.Logging
import com.geeksville.mesh.NodeInfo import com.geeksville.mesh.NodeInfo
import com.geeksville.mesh.R import com.geeksville.mesh.R
import com.geeksville.mesh.model.UIViewModel import com.geeksville.mesh.model.UIViewModel
import com.geeksville.util.formatAgo
import com.mapbox.geojson.Feature import com.mapbox.geojson.Feature
import com.mapbox.geojson.FeatureCollection import com.mapbox.geojson.FeatureCollection
import com.mapbox.geojson.Point import com.mapbox.geojson.Point
@ -79,7 +80,7 @@ class MapFragment : ScreenFragment("Map"), Logging {
) )
) )
node.user?.let { node.user?.let {
f.addStringProperty("name", it.longName) f.addStringProperty("name", it.longName + " " + formatAgo(p.time))
} }
f f
} }
@ -93,7 +94,8 @@ class MapFragment : ScreenFragment("Map"), Logging {
} }
fun zoomToNodes(map: MapboxMap) { fun zoomToNodes(map: MapboxMap) {
val nodesWithPosition = model.nodeDB.nodes.value?.values?.filter { it.validPosition != null } val nodesWithPosition =
model.nodeDB.nodes.value?.values?.filter { it.validPosition != null }
if (nodesWithPosition != null && nodesWithPosition.isNotEmpty()) { if (nodesWithPosition != null && nodesWithPosition.isNotEmpty()) {
val update = if (nodesWithPosition.size >= 2) { val update = if (nodesWithPosition.size >= 2) {
// Multiple nodes, make them all fit on the map view // Multiple nodes, make them all fit on the map view
@ -158,7 +160,10 @@ class MapFragment : ScreenFragment("Map"), Logging {
if (view != null) { // it might have gone away by now if (view != null) { // it might have gone away by now
// val markerIcon = BitmapFactory.decodeResource(context.resources, R.drawable.ic_twotone_person_pin_24) // val markerIcon = BitmapFactory.decodeResource(context.resources, R.drawable.ic_twotone_person_pin_24)
val markerIcon = val markerIcon =
ContextCompat.getDrawable(requireActivity(), R.drawable.ic_twotone_person_pin_24)!! ContextCompat.getDrawable(
requireActivity(),
R.drawable.ic_twotone_person_pin_24
)!!
map.setStyle(Style.OUTDOORS) { style -> map.setStyle(Style.OUTDOORS) { style ->
style.addSource(nodePositions) style.addSource(nodePositions)
@ -176,7 +181,7 @@ class MapFragment : ScreenFragment("Map"), Logging {
// Any times nodes change update our map // Any times nodes change update our map
model.nodeDB.nodes.observe(viewLifecycleOwner, Observer { nodes -> model.nodeDB.nodes.observe(viewLifecycleOwner, Observer { nodes ->
if(isViewVisible) if (isViewVisible)
onNodesChanged(map, nodes.values) onNodesChanged(map, nodes.values)
}) })
zoomToNodes(map) zoomToNodes(map)

Wyświetl plik

@ -2,11 +2,13 @@ package com.geeksville.mesh.ui
import android.os.Bundle import android.os.Bundle
import android.text.format.DateFormat import android.text.Html
import android.text.method.LinkMovementMethod
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.text.HtmlCompat
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
@ -17,13 +19,14 @@ import com.geeksville.mesh.R
import com.geeksville.mesh.databinding.AdapterNodeLayoutBinding import com.geeksville.mesh.databinding.AdapterNodeLayoutBinding
import com.geeksville.mesh.databinding.NodelistFragmentBinding import com.geeksville.mesh.databinding.NodelistFragmentBinding
import com.geeksville.mesh.model.UIViewModel import com.geeksville.mesh.model.UIViewModel
import java.text.ParseException import com.geeksville.util.formatAgo
import java.util.* import java.net.URLEncoder
class UsersFragment : ScreenFragment("Users"), Logging { class UsersFragment : ScreenFragment("Users"), Logging {
private var _binding: NodelistFragmentBinding? = null private var _binding: NodelistFragmentBinding? = null
// This property is only valid between onCreateView and onDestroyView. // This property is only valid between onCreateView and onDestroyView.
private val binding get() = _binding!! private val binding get() = _binding!!
@ -34,6 +37,7 @@ class UsersFragment : ScreenFragment("Users"), Logging {
class ViewHolder(itemView: AdapterNodeLayoutBinding) : RecyclerView.ViewHolder(itemView.root) { class ViewHolder(itemView: AdapterNodeLayoutBinding) : RecyclerView.ViewHolder(itemView.root) {
val nodeNameView = itemView.nodeNameView val nodeNameView = itemView.nodeNameView
val distanceView = itemView.distanceView val distanceView = itemView.distanceView
val coordsView = itemView.coordsView
val batteryPctView = itemView.batteryPercentageView val batteryPctView = itemView.batteryPercentageView
val lastTime = itemView.lastConnectionView val lastTime = itemView.lastConnectionView
val powerIcon = itemView.batteryIcon val powerIcon = itemView.batteryIcon
@ -104,8 +108,26 @@ class UsersFragment : ScreenFragment("Users"), Logging {
*/ */
override fun onBindViewHolder(holder: ViewHolder, position: Int) { override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val n = nodes[position] val n = nodes[position]
val name = n.user?.longName ?: n.user?.id ?: "Unknown node"
holder.nodeNameView.text = name
holder.nodeNameView.text = n.user?.longName ?: n.user?.id ?: "Unknown node" val pos = n.validPosition;
if (pos != null) {
val coords =
String.format("%.5f %.5f", pos.latitude, pos.longitude).replace(",", ".")
val html =
"<a href='geo:${pos.latitude},${pos.longitude}?z=17&label=${
URLEncoder.encode(
name,
"utf-8"
)
}'>${coords}</a>"
holder.coordsView.text = HtmlCompat.fromHtml(html, Html.FROM_HTML_MODE_LEGACY)
holder.coordsView.movementMethod = LinkMovementMethod.getInstance()
holder.coordsView.visibility = View.VISIBLE
} else {
holder.coordsView.visibility = View.INVISIBLE
}
val ourNodeInfo = model.nodeDB.ourNodeInfo val ourNodeInfo = model.nodeDB.ourNodeInfo
val distance = ourNodeInfo?.distanceStr(n) val distance = ourNodeInfo?.distanceStr(n)
@ -118,7 +140,7 @@ class UsersFragment : ScreenFragment("Users"), Logging {
renderBattery(n.batteryPctLevel, holder) renderBattery(n.batteryPctLevel, holder)
holder.lastTime.text = getLastTimeValue(n) holder.lastTime.text = formatAgo(n.lastSeen);
} }
private var nodes = arrayOf<NodeInfo>() private var nodes = arrayOf<NodeInfo>()
@ -150,30 +172,6 @@ class UsersFragment : ScreenFragment("Users"), Logging {
}) })
} }
private fun getLastTimeValue(n: NodeInfo): String {
var lastTimeText = "?"
val currentTime = (System.currentTimeMillis()/1000).toInt()
val threeDaysLong = 3 * 60*60*24
//if the lastSeen is too old
if (n.lastSeen < (currentTime - threeDaysLong))
return lastTimeText
try {
val toLong: Long = n.lastSeen.toLong()
val long1000 = toLong * 1000L
val date = Date(long1000)
val timeFormat = DateFormat.getTimeFormat(context)
lastTimeText = timeFormat.format(date)
} catch (e: ParseException) {
//
}
return lastTimeText
}
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?

@ -1 +1 @@
Subproject commit 7c025b9a4d54bb410ec17ee653122861b413f177 Subproject commit ac26ffdc71dad5765124186df5ec38771a0e5240

Wyświetl plik

@ -51,6 +51,20 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView" /> app:layout_constraintTop_toBottomOf="@+id/imageView" />
<TextView
android:id="@+id/coords_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
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" />
<ImageView <ImageView
android:id="@+id/batteryIcon" android:id="@+id/batteryIcon"
android:layout_width="wrap_content" android:layout_width="wrap_content"

Wyświetl plik

@ -94,5 +94,6 @@
<string name="okay">Okay</string> <string name="okay">Okay</string>
<string name="must_set_region">You must set a region!</string> <string name="must_set_region">You must set a region!</string>
<string name="region">Region</string> <string name="region">Region</string>
<string name="sample_coords">55.332244 34.442211</string>
<string name="save_messages">Save messages as csv...</string> <string name="save_messages">Save messages as csv...</string>
</resources> </resources>

2
design

@ -1 +1 @@
Subproject commit a81074152157fa54b0d02ccbbd6a6357cc3cedcf Subproject commit d0339f0297c629f1bd6873b4abccfecb98443538

@ -1 +1 @@
Subproject commit 99cf0da30fe41163a735ac291f3dd018a7d6295d Subproject commit 6da250358ed13e3c58fd4fa2a123b01b3826d4bf