2020-03-11 14:45:49 -07:00
|
|
|
package com.geeksville.mesh.ui
|
|
|
|
|
|
2020-03-30 11:56:59 -07:00
|
|
|
import android.app.Activity
|
|
|
|
|
import android.app.Application
|
2020-03-30 15:00:18 -07:00
|
|
|
import android.graphics.Color
|
2020-03-30 11:56:59 -07:00
|
|
|
import android.os.Bundle
|
2020-03-11 14:45:49 -07:00
|
|
|
import androidx.compose.Composable
|
2020-03-30 11:56:59 -07:00
|
|
|
import androidx.compose.onCommit
|
2020-03-11 14:45:49 -07:00
|
|
|
import androidx.ui.core.ContextAmbient
|
2020-03-30 10:26:16 -07:00
|
|
|
import androidx.ui.fakeandroidview.AndroidView
|
2020-03-11 14:45:49 -07:00
|
|
|
import androidx.ui.material.MaterialTheme
|
|
|
|
|
import androidx.ui.tooling.preview.Preview
|
2020-03-30 10:26:16 -07:00
|
|
|
import com.geeksville.android.Logging
|
2020-03-11 18:13:44 -07:00
|
|
|
import com.geeksville.mesh.R
|
2020-03-30 12:47:01 -07:00
|
|
|
import com.geeksville.mesh.model.NodeDB
|
2020-03-30 10:26:16 -07:00
|
|
|
import com.geeksville.mesh.model.UIState
|
2020-03-30 12:47:01 -07:00
|
|
|
import com.mapbox.geojson.Feature
|
|
|
|
|
import com.mapbox.geojson.FeatureCollection
|
|
|
|
|
import com.mapbox.geojson.Point
|
2020-04-05 21:59:13 -07:00
|
|
|
import com.mapbox.mapboxsdk.camera.CameraPosition
|
2020-03-30 13:06:41 -07:00
|
|
|
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory
|
|
|
|
|
import com.mapbox.mapboxsdk.geometry.LatLng
|
2020-04-05 12:33:23 -07:00
|
|
|
import com.mapbox.mapboxsdk.geometry.LatLngBounds
|
2020-03-30 10:26:16 -07:00
|
|
|
import com.mapbox.mapboxsdk.maps.MapView
|
2020-03-30 11:56:59 -07:00
|
|
|
import com.mapbox.mapboxsdk.maps.Style
|
2020-03-30 15:00:18 -07:00
|
|
|
import com.mapbox.mapboxsdk.style.expressions.Expression
|
2020-03-30 12:47:01 -07:00
|
|
|
import com.mapbox.mapboxsdk.style.layers.Property
|
2020-03-30 15:00:18 -07:00
|
|
|
import com.mapbox.mapboxsdk.style.layers.Property.TEXT_ANCHOR_TOP
|
|
|
|
|
import com.mapbox.mapboxsdk.style.layers.Property.TEXT_JUSTIFY_AUTO
|
|
|
|
|
import com.mapbox.mapboxsdk.style.layers.PropertyFactory.*
|
2020-03-30 12:47:01 -07:00
|
|
|
import com.mapbox.mapboxsdk.style.layers.SymbolLayer
|
|
|
|
|
import com.mapbox.mapboxsdk.style.sources.GeoJsonSource
|
|
|
|
|
|
2020-03-11 14:45:49 -07:00
|
|
|
|
2020-03-30 10:26:16 -07:00
|
|
|
object mapLog : Logging
|
2020-03-11 14:45:49 -07:00
|
|
|
|
2020-03-30 11:56:59 -07:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* mapbox requires this, until compose has a nicer way of doing it, do it here
|
|
|
|
|
*/
|
|
|
|
|
private val mapLifecycleCallbacks = object : Application.ActivityLifecycleCallbacks {
|
|
|
|
|
var view: MapView? = null
|
|
|
|
|
|
|
|
|
|
override fun onActivityPaused(activity: Activity) {
|
2020-03-31 08:01:15 -07:00
|
|
|
view?.onPause()
|
2020-03-30 11:56:59 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fun onActivityStarted(activity: Activity) {
|
2020-03-31 08:01:15 -07:00
|
|
|
view?.onStart()
|
2020-03-30 11:56:59 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fun onActivityDestroyed(activity: Activity) {
|
2020-03-31 08:01:15 -07:00
|
|
|
view?.onDestroy()
|
2020-03-30 11:56:59 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
|
2020-03-31 08:01:15 -07:00
|
|
|
view?.onSaveInstanceState(outState)
|
2020-03-30 11:56:59 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fun onActivityStopped(activity: Activity) {
|
2020-03-31 08:01:15 -07:00
|
|
|
view?.onStop()
|
2020-03-30 11:56:59 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Called when the Activity calls [super.onCreate()][Activity.onCreate].
|
|
|
|
|
*/
|
|
|
|
|
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fun onActivityResumed(activity: Activity) {
|
2020-03-31 08:01:15 -07:00
|
|
|
view?.onResume()
|
2020-03-30 11:56:59 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2020-03-11 14:45:49 -07:00
|
|
|
@Composable
|
|
|
|
|
fun MapContent() {
|
2020-03-11 18:13:44 -07:00
|
|
|
analyticsScreen(name = "map")
|
2020-03-11 14:45:49 -07:00
|
|
|
|
|
|
|
|
val typography = MaterialTheme.typography()
|
|
|
|
|
val context = ContextAmbient.current
|
|
|
|
|
|
2020-03-30 11:56:59 -07:00
|
|
|
onCommit(AppStatus.currentScreen) {
|
|
|
|
|
onDispose {
|
|
|
|
|
// We no longer care about activity lifecycle
|
|
|
|
|
(context.applicationContext as Application).unregisterActivityLifecycleCallbacks(
|
|
|
|
|
mapLifecycleCallbacks
|
|
|
|
|
)
|
|
|
|
|
mapLifecycleCallbacks.view = null
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-30 12:47:01 -07:00
|
|
|
// Find all nodes with valid locations
|
2020-04-05 12:33:23 -07:00
|
|
|
val nodesWithPosition = NodeDB.nodes.values.filter { it.validPosition != null }
|
|
|
|
|
val locations = nodesWithPosition.map { node ->
|
|
|
|
|
val p = node.position!!
|
|
|
|
|
mapLog.debug("Showing on map: $node")
|
|
|
|
|
|
|
|
|
|
val f = Feature.fromGeometry(
|
|
|
|
|
Point.fromLngLat(
|
|
|
|
|
p.longitude,
|
|
|
|
|
p.latitude
|
2020-03-30 11:56:59 -07:00
|
|
|
)
|
2020-04-05 12:33:23 -07:00
|
|
|
)
|
|
|
|
|
node.user?.let {
|
|
|
|
|
f.addStringProperty("name", it.longName)
|
|
|
|
|
}
|
|
|
|
|
f
|
2020-03-30 12:47:01 -07:00
|
|
|
}
|
|
|
|
|
val nodeSourceId = "node-positions"
|
|
|
|
|
val nodeLayerId = "node-layer"
|
2020-03-30 15:00:18 -07:00
|
|
|
val labelLayerId = "label-layer"
|
2020-03-30 12:47:01 -07:00
|
|
|
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 = context.getDrawable(R.drawable.ic_twotone_person_pin_24)!!
|
|
|
|
|
|
2020-03-30 15:00:18 -07:00
|
|
|
val nodeLayer = SymbolLayer(nodeLayerId, nodeSourceId).withProperties(
|
2020-04-05 12:33:23 -07:00
|
|
|
iconImage(markerImageId),
|
|
|
|
|
iconAnchor(Property.ICON_ANCHOR_BOTTOM),
|
|
|
|
|
iconAllowOverlap(true)
|
2020-03-30 12:47:01 -07:00
|
|
|
)
|
|
|
|
|
|
2020-03-30 15:00:18 -07:00
|
|
|
val labelLayer = SymbolLayer(labelLayerId, nodeSourceId).withProperties(
|
|
|
|
|
textField(Expression.get("name")),
|
|
|
|
|
textSize(12f),
|
|
|
|
|
textColor(Color.RED),
|
|
|
|
|
textVariableAnchor(arrayOf(TEXT_ANCHOR_TOP)),
|
2020-04-05 12:33:23 -07:00
|
|
|
textJustify(TEXT_JUSTIFY_AUTO),
|
|
|
|
|
textAllowOverlap(true)
|
2020-03-30 15:00:18 -07:00
|
|
|
)
|
|
|
|
|
|
2020-03-30 12:47:01 -07:00
|
|
|
AndroidView(R.layout.map_view) { view ->
|
|
|
|
|
view as MapView
|
|
|
|
|
view.onCreate(UIState.savedInstanceState)
|
|
|
|
|
|
|
|
|
|
mapLifecycleCallbacks.view = view
|
|
|
|
|
(context.applicationContext as Application).registerActivityLifecycleCallbacks(
|
|
|
|
|
mapLifecycleCallbacks
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
view.getMapAsync { map ->
|
|
|
|
|
map.setStyle(Style.OUTDOORS) { style ->
|
|
|
|
|
style.addSource(nodePositions)
|
2020-03-30 15:00:18 -07:00
|
|
|
style.addImage(markerImageId, markerIcon)
|
2020-03-30 12:47:01 -07:00
|
|
|
style.addLayer(nodeLayer)
|
2020-03-30 15:00:18 -07:00
|
|
|
style.addLayer(labelLayer)
|
2020-03-30 10:26:16 -07:00
|
|
|
}
|
2020-03-30 13:06:41 -07:00
|
|
|
|
2020-03-30 15:00:18 -07:00
|
|
|
//map.uiSettings.isScrollGesturesEnabled = true
|
|
|
|
|
//map.uiSettings.isZoomGesturesEnabled = true
|
|
|
|
|
|
2020-04-05 12:33:23 -07:00
|
|
|
if (nodesWithPosition.isNotEmpty()) {
|
2020-04-05 21:59:13 -07:00
|
|
|
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(), 100)
|
|
|
|
|
} 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)
|
2020-03-30 13:06:41 -07:00
|
|
|
}
|
2020-03-30 10:26:16 -07:00
|
|
|
}
|
2020-03-11 14:45:49 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Preview
|
|
|
|
|
@Composable
|
|
|
|
|
fun previewMap() {
|
|
|
|
|
// another bug? It seems modaldrawerlayout not yet supported in preview
|
|
|
|
|
MaterialTheme(colors = palette) {
|
|
|
|
|
MapContent()
|
|
|
|
|
}
|
|
|
|
|
}
|