diff --git a/app/src/main/java/com/geeksville/mesh/MainActivity.kt b/app/src/main/java/com/geeksville/mesh/MainActivity.kt index 2dbd0c17f..113b63d58 100644 --- a/app/src/main/java/com/geeksville/mesh/MainActivity.kt +++ b/app/src/main/java/com/geeksville/mesh/MainActivity.kt @@ -134,10 +134,7 @@ class MainActivity : AppCompatActivity(), Logging, "Messages", R.drawable.ic_twotone_message_24, ComposeFragment("Messages", 1) { MessagesContent() }), - TabInfo( - "Settings", - R.drawable.ic_twotone_settings_applications_24, - ComposeFragment("Settings", 2) { SettingsContent() }), + TabInfo( "Users", R.drawable.ic_twotone_people_24, @@ -150,7 +147,11 @@ class MainActivity : AppCompatActivity(), Logging, "Map", R.drawable.ic_twotone_map_24, MapFragment() - ) + ), + TabInfo( + "Settings", + R.drawable.ic_twotone_settings_applications_24, + BTScanFragment("Settings", 2) { SettingsContent() }) ) private @@ -301,7 +302,7 @@ class MainActivity : AppCompatActivity(), Logging, val pager = findViewById(R.id.pager) pager.adapter = tabsAdapter TabLayoutMediator(tab_layout, pager) { tab, position -> - tab.text = tabInfos[position].text + // tab.text = tabInfos[position].text // I think it looks better with icons only tab.icon = getDrawable(tabInfos[position].icon) }.attach() } @@ -539,7 +540,6 @@ class MainActivity : AppCompatActivity(), Logging, } override fun onStop() { - ScanState.stopScan() unregisterMeshReceiver() // No point in receiving updates while the GUI is gone, we'll get them when the user launches the activity unbindMeshService() diff --git a/app/src/main/java/com/geeksville/mesh/ui/BTScanScreen.kt b/app/src/main/java/com/geeksville/mesh/ui/BTScanScreen.kt index 1e9a0b32e..1935b7d5e 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/BTScanScreen.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/BTScanScreen.kt @@ -65,6 +65,15 @@ data class BTScanEntry(val name: String, val macAddress: String, val bonded: Boo } +class BTScanFragment(screenName: String, id: Int, private val content: @Composable() () -> Unit) : + ComposeFragment(screenName, id, content) { + + override fun onStop() { + ScanState.stopScan() + super.onStop() + } +} + @Composable fun BTScanScreen() { val context = ContextAmbient.current @@ -146,11 +155,6 @@ fun BTScanScreen() { ScanState.callback = scanCallback } } - - onDispose { - ScanState.debug("BTScan component deactivated") - ScanState.stopScan() - } } Column { diff --git a/app/src/main/java/com/geeksville/mesh/ui/ComposeFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/ComposeFragment.kt index 1986d4b08..28e7f0793 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/ComposeFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/ComposeFragment.kt @@ -7,7 +7,6 @@ import android.view.ViewGroup import android.widget.FrameLayout import androidx.compose.Composable import androidx.ui.core.setContent -import com.geeksville.android.Logging fun androidx.fragment.app.Fragment.setComposable( id: Int, @@ -27,9 +26,12 @@ fun androidx.fragment.app.Fragment.setComposable( } -class ComposeFragment(screenName: String, id: Int, private val content: @Composable() () -> Unit) : - ScreenFragment(screenName), - Logging { +open class ComposeFragment( + screenName: String, + id: Int, + private val content: @Composable() () -> Unit +) : + ScreenFragment(screenName) { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? diff --git a/app/src/main/java/com/geeksville/mesh/ui/Map.kt b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt similarity index 54% rename from app/src/main/java/com/geeksville/mesh/ui/Map.kt rename to app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt index 3c5934fd1..e7ef92a17 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/Map.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt @@ -17,6 +17,7 @@ import com.mapbox.mapboxsdk.camera.CameraUpdateFactory import com.mapbox.mapboxsdk.geometry.LatLng import com.mapbox.mapboxsdk.geometry.LatLngBounds import com.mapbox.mapboxsdk.maps.MapView +import com.mapbox.mapboxsdk.maps.MapboxMap import com.mapbox.mapboxsdk.maps.Style import com.mapbox.mapboxsdk.style.expressions.Expression import com.mapbox.mapboxsdk.style.layers.Property @@ -28,7 +29,81 @@ import com.mapbox.mapboxsdk.style.sources.GeoJsonSource class MapFragment : ScreenFragment("Map"), Logging { - + + private val nodeSourceId = "node-positions" + private val nodeLayerId = "node-layer" + private val labelLayerId = "label-layer" + private val markerImageId = "my-marker-image" + + private val nodePositions = GeoJsonSource(nodeSourceId) + + private val nodeLayer = SymbolLayer(nodeLayerId, nodeSourceId).withProperties( + iconImage(markerImageId), + iconAnchor(Property.ICON_ANCHOR_BOTTOM), + iconAllowOverlap(true) + ) + + private val labelLayer = SymbolLayer(labelLayerId, nodeSourceId).withProperties( + textField(Expression.get("name")), + textSize(12f), + textColor(Color.RED), + textVariableAnchor(arrayOf(TEXT_ANCHOR_TOP)), + textJustify(TEXT_JUSTIFY_AUTO), + textAllowOverlap(true) + ) + + private fun nodesWithPosition() = NodeDB.nodes.values.filter { it.validPosition != null } + + /** + * Using the latest nodedb, generate geojson features + */ + private fun getCurrentNodes(): FeatureCollection { + // Find all nodes with valid locations + + val locations = nodesWithPosition().map { node -> + val p = node.position!! + debug("Showing on map: $node") + + val f = Feature.fromGeometry( + Point.fromLngLat( + p.longitude, + p.latitude + ) + ) + node.user?.let { + f.addStringProperty("name", it.longName) + } + f + } + + return FeatureCollection.fromFeatures(locations) + } + + private fun zoomToNodes(map: MapboxMap) { + val nodes = nodesWithPosition() + if (nodes.isNotEmpty()) { + val update = if (nodes.size >= 2) { + // Multiple nodes, make them all fit on the map view + val bounds = LatLngBounds.Builder() + + // Add all positions + bounds.includes(nodes.map { it.position!! } + .map { LatLng(it.latitude, it.longitude) }) + + CameraUpdateFactory.newLatLngBounds(bounds.build(), 150) + } else { + // Only one node, just zoom in on it + val it = nodes[0].position!! + + val cameraPos = CameraPosition.Builder().target( + LatLng(it.latitude, it.longitude) + ).zoom(9.0).build() + CameraUpdateFactory.newCameraPosition(cameraPos) + } + map.animateCamera(update, 1000) + } + } + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? @@ -44,48 +119,9 @@ class MapFragment : ScreenFragment("Map"), Logging { mapView.getMapAsync { map -> - // Find all nodes with valid locations - val nodesWithPosition = NodeDB.nodes.values.filter { it.validPosition != null } - val locations = nodesWithPosition.map { node -> - val p = node.position!! - debug("Showing on map: $node") - - val f = Feature.fromGeometry( - Point.fromLngLat( - p.longitude, - p.latitude - ) - ) - node.user?.let { - f.addStringProperty("name", it.longName) - } - f - } - val nodeSourceId = "node-positions" - val nodeLayerId = "node-layer" - val labelLayerId = "label-layer" - val markerImageId = "my-marker-image" - val nodePositions = - GeoJsonSource(nodeSourceId, FeatureCollection.fromFeatures(locations)) - // val markerIcon = BitmapFactory.decodeResource(context.resources, R.drawable.ic_twotone_person_pin_24) val markerIcon = activity!!.getDrawable(R.drawable.ic_twotone_person_pin_24)!! - val nodeLayer = SymbolLayer(nodeLayerId, nodeSourceId).withProperties( - iconImage(markerImageId), - iconAnchor(Property.ICON_ANCHOR_BOTTOM), - iconAllowOverlap(true) - ) - - val labelLayer = SymbolLayer(labelLayerId, nodeSourceId).withProperties( - textField(Expression.get("name")), - textSize(12f), - textColor(Color.RED), - textVariableAnchor(arrayOf(TEXT_ANCHOR_TOP)), - textJustify(TEXT_JUSTIFY_AUTO), - textAllowOverlap(true) - ) - map.setStyle(Style.OUTDOORS) { style -> style.addSource(nodePositions) style.addImage(markerImageId, markerIcon) @@ -95,28 +131,6 @@ class MapFragment : ScreenFragment("Map"), Logging { //map.uiSettings.isScrollGesturesEnabled = true //map.uiSettings.isZoomGesturesEnabled = true - - if (nodesWithPosition.isNotEmpty()) { - val update = if (nodesWithPosition.size >= 2) { - // Multiple nodes, make them all fit on the map view - val bounds = LatLngBounds.Builder() - - // Add all positions - bounds.includes(nodesWithPosition.map { it.position!! } - .map { LatLng(it.latitude, it.longitude) }) - - CameraUpdateFactory.newLatLngBounds(bounds.build(), 150) - } else { - // Only one node, just zoom in on it - val it = nodesWithPosition[0].position!! - - val cameraPos = CameraPosition.Builder().target( - LatLng(it.latitude, it.longitude) - ).zoom(9.0).build() - CameraUpdateFactory.newCameraPosition(cameraPos) - } - map.animateCamera(update, 1000) - } } } @@ -138,6 +152,11 @@ class MapFragment : ScreenFragment("Map"), Logging { override fun onResume() { super.onResume() mapView.onResume() + + mapView.getMapAsync { map -> + nodePositions.setGeoJson(getCurrentNodes()) // Update node positions + zoomToNodes(map) + } } override fun onDestroy() {