diff --git a/app/build.gradle b/app/build.gradle index a9cccafc4..796d04a2e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -127,6 +127,9 @@ dependencies { implementation 'androidx.appcompat:appcompat:1.4.1' implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.fragment:fragment-ktx:1.4.0' + implementation('com.mapbox.maps:android:10.2.0') { + exclude group: 'com.mapbox.mapboxsdk', module: 'mapbox-android-telemetry' + } implementation 'androidx.cardview:cardview:1.0.0' implementation 'androidx.recyclerview:recyclerview:1.2.1' implementation 'androidx.constraintlayout:constraintlayout:2.1.3' @@ -158,11 +161,11 @@ 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' diff --git a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt index f40c78b3d..3d7850dc0 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt @@ -6,6 +6,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.core.content.ContextCompat +import androidx.core.view.isVisible import androidx.fragment.app.activityViewModels import androidx.lifecycle.Observer import com.geeksville.android.GeeksvilleApplication @@ -17,20 +18,17 @@ 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.mapboxsdk.style.layers.PropertyFactory.textAllowOverlap +import com.mapbox.maps.MapView +import com.mapbox.maps.CameraOptions +import com.mapbox.maps.MapboxMap +import com.mapbox.maps.Style +import com.mapbox.maps.extension.style.expressions.generated.Expression +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.generated.GeoJsonSource class MapFragment : ScreenFragment("Map"), Logging { @@ -42,22 +40,19 @@ class MapFragment : ScreenFragment("Map"), Logging { private val labelLayerId = "label-layer" private val markerImageId = "my-marker-image" - private val nodePositions = GeoJsonSource(nodeSourceId) + private val 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) - 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() + .textColor(Color.RED) + .textVariableAnchor(arrayListOf(TextAnchor.TOP.toString())) + .textJustify(TextJustify.AUTO) + .textAllowOverlap(true) private fun onNodesChanged(map: MapboxMap, nodes: Collection) { @@ -89,33 +84,34 @@ class MapFragment : ScreenFragment("Map"), Logging { } - - nodePositions.setGeoJson(getCurrentNodes()) // Update node positions + // nodePositions.setGeoJson(getCurrentNodes()) // Update node positions } fun zoomToNodes(map: MapboxMap) { 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() - - // 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) +// 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) +// } +// } } } @@ -141,7 +137,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) @@ -151,50 +147,44 @@ class MapFragment : ScreenFragment("Map"), Logging { val vIn = viewIn.findViewById(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 - )!! + val map = v.getMapboxMap() + 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.loadStyleUri(Style.OUTDOORS) +// 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) - } + + // map.uiSettings.isRotateGesturesEnabled = false + // Provide initial positions + model.nodeDB.nodes.value?.let { nodes -> + onNodesChanged(map, nodes.values) } - - // Any times nodes change update our map - model.nodeDB.nodes.observe(viewLifecycleOwner, Observer { nodes -> - if (isViewVisible) - onNodesChanged(map, nodes.values) - }) - zoomToNodes(map) } + + // 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() @@ -205,11 +195,6 @@ class MapFragment : ScreenFragment("Map"), Logging { super.onStop() } - override fun onResume() { - super.onResume() - mapView?.onResume() - } - override fun onDestroyView() { super.onDestroyView() mapView?.onDestroy() @@ -217,18 +202,18 @@ class MapFragment : ScreenFragment("Map"), Logging { override fun onSaveInstanceState(outState: Bundle) { mapView?.let { - if (!it.isDestroyed) - it.onSaveInstanceState(outState) + super.onSaveInstanceState(outState) } - super.onSaveInstanceState(outState) } override fun onLowMemory() { - mapView?.let { - if (!it.isDestroyed) - it.onLowMemory() - } super.onLowMemory() + mapView?.onLowMemory() + } + + override fun onDestroy() { + super.onDestroy() + mapView?.onDestroy() } } diff --git a/app/src/main/res/layout/map_view.xml b/app/src/main/res/layout/map_view.xml index 608b01154..5dda7c296 100644 --- a/app/src/main/res/layout/map_view.xml +++ b/app/src/main/res/layout/map_view.xml @@ -2,16 +2,17 @@ + android:layout_height="match_parent"> - + android:clickable="true" + android:focusable="true" + mapbox:mapbox_uiScrollGestures="true" + mapbox:mapbox_uiZoomGestures="true"> \ No newline at end of file diff --git a/build.gradle b/build.gradle index c15e31f78..0fe1b0c83 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,6 @@ buildscript { repositories { google() - // jcenter() mavenCentral() } dependencies { @@ -34,30 +33,6 @@ buildscript { } } -allprojects { - repositories { - maven { - // Per https://docs.mapbox.com/android/maps/guides/install/ we now need to signin to download mapbox lib - url 'https://api.mapbox.com/downloads/v2/releases/maven' - authentication { - basic(BasicAuthentication) - } - credentials { - // Do not change the username below. - // This should always be `mapbox` (not your username). - username = 'mapbox' - // Use the secret token you stored in gradle.properties as the password - password = project.properties['MAPBOX_DOWNLOADS_TOKEN'] ?: "" - } - } - google() - mavenCentral() - // jcenter() - maven { url 'https://jitpack.io' } - //maven { url "https://plugins.gradle.org/m2/" } - } -} - task clean(type: Delete) { delete rootProject.buildDir } diff --git a/settings.gradle b/settings.gradle index 7477052ea..5b7a0b4bd 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,5 +1,29 @@ include ':app' -rootProject.name='Mesh Util' +rootProject.name = 'Mesh Util' include ':geeksville-androidlib' project(':geeksville-androidlib').projectDir = new File('geeksville-androidlib') + + +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS) + repositories { + google() + mavenCentral() + // jcenter() // Warning: this repository is going to shut down soon + maven { + url 'https://api.mapbox.com/downloads/v2/releases/maven' + authentication { + basic(BasicAuthentication) + } + credentials { + // Do not change the username below. + // This should always be `mapbox` (not your username). + username = "mapbox" + // Use the secret token you stored in gradle.properties as the password + password = System.properties["MAPBOX_DOWNLOADS_TOKEN"] + } + } + maven { url 'https://jitpack.io' } + } +} \ No newline at end of file