Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrating to Mapbox API V10 #368

Merged
6 changes: 3 additions & 3 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -158,14 +158,14 @@ dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"

// For now I'm not using javalite, because I want JSON printing
implementation ('com.google.protobuf:protobuf-java:3.15.8')
implementation('com.google.protobuf:protobuf-java:3.15.8')

// For UART access
// implementation 'com.google.android.things:androidthings:1.0'
implementation 'com.github.mik3y:usb-serial-for-android:v3.0.0'
implementation 'com.github.mik3y:usb-serial-for-android:3.4.3'

// mapbox
implementation 'com.mapbox.mapboxsdk:mapbox-android-sdk:9.7.1'
implementation('com.mapbox.maps:android:10.2.0')

// mapbox specifies a really old version of okhttp3 which causes lots of API warnings. trying a newer version
implementation 'com.squareup.okhttp3:okhttp:4.9.0'
Expand Down
4 changes: 0 additions & 4 deletions app/src/main/java/com/geeksville/mesh/MeshUtilApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import com.geeksville.android.GeeksvilleApplication
import com.geeksville.android.Logging
import com.geeksville.util.Exceptions
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.mapbox.mapboxsdk.Mapbox


class MeshUtilApplication : GeeksvilleApplication() {

Expand Down Expand Up @@ -50,7 +48,5 @@ class MeshUtilApplication : GeeksvilleApplication() {
sendCrashReports() // Send the new report
}
}

Mapbox.getInstance(this, getString(R.string.mapbox_access_token))
}
}
195 changes: 79 additions & 116 deletions app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.toBitmap
import androidx.core.view.isVisible
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Observer
import com.geeksville.android.GeeksvilleApplication
Expand All @@ -17,20 +19,19 @@ import com.geeksville.util.formatAgo
import com.mapbox.geojson.Feature
import com.mapbox.geojson.FeatureCollection
import com.mapbox.geojson.Point
import com.mapbox.mapboxsdk.camera.CameraPosition
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
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.*
import com.mapbox.mapboxsdk.style.layers.SymbolLayer
import com.mapbox.mapboxsdk.style.sources.GeoJsonSource
import com.mapbox.maps.*
import com.mapbox.maps.dsl.cameraOptions
import com.mapbox.maps.extension.style.expressions.generated.Expression
import com.mapbox.maps.extension.style.layers.addLayer
import com.mapbox.maps.extension.style.layers.generated.SymbolLayer
import com.mapbox.maps.extension.style.layers.properties.generated.IconAnchor
import com.mapbox.maps.extension.style.layers.properties.generated.TextAnchor
import com.mapbox.maps.extension.style.layers.properties.generated.TextJustify
import com.mapbox.maps.extension.style.sources.addSource
import com.mapbox.maps.extension.style.sources.generated.GeoJsonSource
import com.mapbox.maps.plugin.animation.MapAnimationOptions
import com.mapbox.maps.plugin.animation.flyTo
import com.mapbox.maps.plugin.gestures.gestures


class MapFragment : ScreenFragment("Map"), Logging {
Expand All @@ -42,22 +43,21 @@ class MapFragment : ScreenFragment("Map"), Logging {
private val labelLayerId = "label-layer"
private val markerImageId = "my-marker-image"

private val nodePositions = GeoJsonSource(nodeSourceId)
private var nodePositions = GeoJsonSource(GeoJsonSource.Builder(nodeSourceId))

private val nodeLayer = SymbolLayer(nodeLayerId, nodeSourceId).withProperties(
iconImage(markerImageId),
iconAnchor(Property.ICON_ANCHOR_BOTTOM),
iconAllowOverlap(true)
)
private val nodeLayer = SymbolLayer(nodeLayerId, nodeSourceId)
.iconImage(markerImageId)
.iconAnchor(IconAnchor.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 val labelLayer = SymbolLayer(labelLayerId, nodeSourceId)
.textField(Expression.get("name"))
.textSize(12.0)
.textColor(Color.RED)
.textAnchor(TextAnchor.TOP)
//.textVariableAnchor(TextAnchor.TOP) //TODO investigate need for variable anchor vs normal anchor
.textJustify(TextJustify.AUTO)
.textAllowOverlap(true)


private fun onNodesChanged(map: MapboxMap, nodes: Collection<NodeInfo>) {
Expand Down Expand Up @@ -87,35 +87,39 @@ class MapFragment : ScreenFragment("Map"), Logging {

return FeatureCollection.fromFeatures(locations)
}



nodePositions.setGeoJson(getCurrentNodes()) // Update node positions
nodePositions.featureCollection(getCurrentNodes())
}

fun zoomToNodes(map: MapboxMap) {
private fun zoomToNodes(map: MapboxMap) {
val points: MutableList<Point> = mutableListOf()
val nodesWithPosition =
model.nodeDB.nodes.value?.values?.filter { it.validPosition != null }
if (nodesWithPosition != null && nodesWithPosition.isNotEmpty()) {
val update = if (nodesWithPosition.size >= 2) {
// Multiple nodes, make them all fit on the map view
val bounds = LatLngBounds.Builder()
val unit = if (nodesWithPosition.size >= 2) {

// Add all positions
bounds.includes(nodesWithPosition.map { it.position!! }
.map { LatLng(it.latitude, it.longitude) })

CameraUpdateFactory.newLatLngBounds(bounds.build(), 150)
// Multiple nodes, make them all fit on the map view
nodesWithPosition.forEach {
points.add(
Point.fromLngLat(
it.position!!.longitude,
it.position!!.latitude
)
)
}
map.cameraForCoordinates(points)
} 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)
points.add(Point.fromLngLat(it.longitude, it.latitude))
map.cameraForCoordinates(points)
cameraOptions {
this.zoom(9.0)
this.center(points[0])
}
}
map.animateCamera(update, 1000)
map.flyTo(
unit,
MapAnimationOptions.mapAnimationOptions { duration(1000) })
}
}

Expand All @@ -141,7 +145,7 @@ class MapFragment : ScreenFragment("Map"), Logging {
* Mapbox native code can crash painfully if you ever call a mapbox view function while the view is not actively being show
*/
private val isViewVisible: Boolean
get() = !(mapView?.isDestroyed ?: true)
get() = mapView?.isVisible == true

override fun onViewCreated(viewIn: View, savedInstanceState: Bundle?) {
super.onViewCreated(viewIn, savedInstanceState)
Expand All @@ -151,84 +155,43 @@ class MapFragment : ScreenFragment("Map"), Logging {
val vIn = viewIn.findViewById<MapView>(R.id.mapView)
mapView = vIn
mapView?.let { v ->
v.onCreate(savedInstanceState)

// Each time the pane is shown start fetching new map info (we do this here instead of
// onCreate because getMapAsync can die in native code if the view goes away)
v.getMapAsync { map ->

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 =
ContextCompat.getDrawable(
requireActivity(),
R.drawable.ic_twotone_person_pin_24
)!!

map.setStyle(Style.OUTDOORS) { style ->
style.addSource(nodePositions)
style.addImage(markerImageId, markerIcon)
style.addLayer(nodeLayer)
style.addLayer(labelLayer)
}

map.uiSettings.isRotateGesturesEnabled = false
// Provide initial positions
model.nodeDB.nodes.value?.let { nodes ->
onNodesChanged(map, nodes.values)
val map = v.getMapboxMap()
if (view != null) { // it might have gone away by now
val markerIcon =
ContextCompat.getDrawable(
requireActivity(),
R.drawable.ic_twotone_person_pin_24
)!!.toBitmap()

map.loadStyleUri(Style.OUTDOORS) {
if (it.isStyleLoaded) {
it.addSource(nodePositions)
it.addImage(markerImageId, markerIcon)
it.addLayer(nodeLayer)
it.addLayer(labelLayer)
}
}

// Any times nodes change update our map
model.nodeDB.nodes.observe(viewLifecycleOwner, Observer { nodes ->
if (isViewVisible)
onNodesChanged(map, nodes.values)
})
zoomToNodes(map)
}
}
}
}

override fun onPause() {
mapView?.onPause()
super.onPause()
}

override fun onStart() {
super.onStart()
mapView?.onStart()
}

override fun onStop() {
mapView?.onStop()
super.onStop()
}

override fun onResume() {
super.onResume()
mapView?.onResume()
}
v.gestures.rotateEnabled = false

override fun onDestroyView() {
super.onDestroyView()
mapView?.onDestroy()
}

override fun onSaveInstanceState(outState: Bundle) {
mapView?.let {
if (!it.isDestroyed)
it.onSaveInstanceState(outState)
}
super.onSaveInstanceState(outState)
}
// Provide initial positions
model.nodeDB.nodes.value?.let { nodes ->
onNodesChanged(map, nodes.values)
}
}

override fun onLowMemory() {
mapView?.let {
if (!it.isDestroyed)
it.onLowMemory()
// Any times nodes change update our map
model.nodeDB.nodes.observe(viewLifecycleOwner, Observer { nodes ->
if (isViewVisible)
onNodesChanged(map, nodes.values)
})
zoomToNodes(map)
}
}
super.onLowMemory()
}
}

Expand Down
15 changes: 3 additions & 12 deletions app/src/main/res/layout/map_view.xml
Original file line number Diff line number Diff line change
@@ -1,17 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
<com.mapbox.maps.MapView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:mapbox="http://schemas.android.com/apk/res-auto"
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/mapFrame"> <!-- tab layout requires a unique ID -->

<com.mapbox.mapboxsdk.maps.MapView
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true" android:focusable="true"
mapbox:mapbox_uiZoomGestures="true"
mapbox:mapbox_uiScrollGestures="true"></com.mapbox.mapboxsdk.maps.MapView>

</FrameLayout>
mapbox:mapbox_cameraZoom="0" />
1 change: 0 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ buildscript {

repositories {
google()
// jcenter()
mavenCentral()
}
dependencies {
Expand Down