Meshtastic-Android/app/src/main/java/com/geeksville/mesh/ui/Map.kt

171 lines
5.4 KiB
Kotlin
Raw Normal View History

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-03-30 13:06:41 -07:00
import com.mapbox.mapboxsdk.camera.CameraPosition
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory
import com.mapbox.mapboxsdk.geometry.LatLng
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
2020-03-30 12:47:01 -07:00
import com.mapbox.mapboxsdk.style.layers.PropertyFactory
2020-03-30 15:00:18 -07:00
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) {
view!!.onPause()
}
override fun onActivityStarted(activity: Activity) {
view!!.onStart()
}
override fun onActivityDestroyed(activity: Activity) {
view!!.onDestroy()
}
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
view!!.onSaveInstanceState(outState)
}
override fun onActivityStopped(activity: Activity) {
view!!.onStop()
}
/**
* Called when the Activity calls [super.onCreate()][Activity.onCreate].
*/
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
}
override fun onActivityResumed(activity: Activity) {
view!!.onResume()
}
}
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
val locations = NodeDB.nodes.values.mapNotNull { node ->
val p = node.position
2020-03-30 15:00:18 -07:00
if (p != null && (p.latitude != 0.0 || p.longitude != 0.0)) {
val f = Feature.fromGeometry(
2020-03-30 12:47:01 -07:00
Point.fromLngLat(
2020-03-30 15:00:18 -07:00
p.longitude,
p.latitude
2020-03-30 12:47:01 -07:00
)
2020-03-30 11:56:59 -07:00
)
2020-03-30 15:00:18 -07:00
node.user?.let { f.addStringProperty("name", it.longName) }
f
} else
2020-03-30 12:47:01 -07:00
null
}
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-03-30 12:47:01 -07:00
PropertyFactory.iconImage(markerImageId),
PropertyFactory.iconAnchor(Property.ICON_ANCHOR_BOTTOM)
)
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)),
textJustify(TEXT_JUSTIFY_AUTO)
)
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-03-30 13:06:41 -07:00
// Center on the user's position (if we have it)
NodeDB.ourNodeInfo?.position?.let {
val cameraPos = CameraPosition.Builder().target(
LatLng(it.latitude, it.longitude)
2020-03-30 15:00:18 -07:00
).zoom(9.0).build()
2020-03-30 13:06:41 -07:00
map.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPos), 1000)
}
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()
}
}