also looking at this
feat(maps): add key-free native Android maps and shared map controls
#363
28 files
+1857
-481
Review threads live on the whole diff, not on one commit, so none are shown here - a thread's line means something in the branch's final form, and painting it into an intermediate step would put it on code it is not about.
| @@ -74,7 +74,7 @@ on: | ||
| 74 | 74 | android_suite: |
| 75 | 75 | description: Android native fixture coverage for the android-navigation scope |
| 76 | 76 | type: choice |
| 77 | options: [focused, full, system-bars] | |
| 77 | options: [focused, full, system-bars, maps] | |
| 78 | 78 | default: focused |
| 79 | 79 | selectors: |
| 80 | 80 | description: Optional XCTest selector for focused native iOS rendering or navigation |
| @@ -607,7 +607,7 @@ jobs: | ||
| 607 | 607 | api-level: ${{ fromJSON((inputs.scope != 'android-navigation' || inputs.android_suite == 'full') && '[34,35]' || inputs.android_suite == 'system-bars' && '[35]' || '[34]') }} |
| 608 | 608 | env: |
| 609 | 609 | CRAFT_ANDROID_NATIVE_SUITE: ${{ inputs.scope != 'android-navigation' && 'full' || inputs.android_suite || 'focused' }} |
| 610 | CRAFT_ANDROID_TEST_CLASS: ${{ inputs.scope == 'android-navigation' && inputs.android_suite == 'system-bars' && 'dev.craft.navigationtest.NativeNavigationTest#nativeScreenBackgroundOptionAppliesAndResets' || inputs.scope == 'android-navigation' && inputs.android_suite != 'full' && 'dev.craft.navigationtest.NativeNavigationTest#outgoingTabTapWinsOverSettlingSwipe,dev.craft.navigationtest.NativeNavigationTest#thirdTabTapWinsOverSettlingSwipe,dev.craft.navigationtest.NativeNavigationTest#cancelledSwipeTargetIsVisibleWhenSelectedLater,dev.craft.navigationtest.NativeNavigationTest#completedReverseSwipeKeepsRetainedTabVisible,dev.craft.navigationtest.NativeNavigationTest#nativePullToRefreshSurvivesViewReattachment,dev.craft.navigationtest.NativeNavigationTest#pushedScreenTabAccessoryOpensNativeRouteAndClearsOnBack,dev.craft.navigationtest.NativeFlatListTest#refreshAppearanceUsesTheScreenContrastFallback,dev.craft.navigationtest.NativeNavigationTest#nativeDatePickerKeepsFollowingControlsMounted' || '' }} | |
| 610 | CRAFT_ANDROID_TEST_CLASS: ${{ inputs.scope == 'android-navigation' && inputs.android_suite == 'maps' && 'dev.craft.navigationtest.NativeMapTest#keyFreeBasemapsKeepTheRecordedRouteAndDirectionSignsVisible,dev.craft.navigationtest.NativeMapTest#nativeMapMutationsUpdateThePinAndCameraWithoutReloadingTheStyle,dev.craft.navigationtest.NativeMapTest#showingTheLocationDotDoesNotRequestPermissionDuringRendering' || inputs.scope == 'android-navigation' && inputs.android_suite == 'system-bars' && 'dev.craft.navigationtest.NativeNavigationTest#nativeScreenBackgroundOptionAppliesAndResets' || inputs.scope == 'android-navigation' && inputs.android_suite != 'full' && 'dev.craft.navigationtest.NativeNavigationTest#outgoingTabTapWinsOverSettlingSwipe,dev.craft.navigationtest.NativeNavigationTest#thirdTabTapWinsOverSettlingSwipe,dev.craft.navigationtest.NativeNavigationTest#cancelledSwipeTargetIsVisibleWhenSelectedLater,dev.craft.navigationtest.NativeNavigationTest#completedReverseSwipeKeepsRetainedTabVisible,dev.craft.navigationtest.NativeNavigationTest#nativePullToRefreshSurvivesViewReattachment,dev.craft.navigationtest.NativeNavigationTest#pushedScreenTabAccessoryOpensNativeRouteAndClearsOnBack,dev.craft.navigationtest.NativeFlatListTest#refreshAppearanceUsesTheScreenContrastFallback,dev.craft.navigationtest.NativeNavigationTest#nativeDatePickerKeepsFollowingControlsMounted' || '' }} | |
| 611 | 611 | if: ${{ inputs.scope != 'ios-navigation' && inputs.scope != 'ios-render' }} |
| 612 | 612 | runs-on: ubuntu-latest |
| 613 | 613 | timeout-minutes: 35 |
| @@ -47,6 +47,7 @@ internal class CraftNativeMapView(context: Context) : FrameLayout(context), OnMa | ||
| 47 | 47 | bottomMargin = (8 * resources.displayMetrics.density).roundToInt() |
| 48 | 48 | }) |
| 49 | 49 | mapView.onCreate(null) |
| 50 | mapView.addOnDidFinishLoadingMapListener { if (!destroyed && initialCameraKey == null) applyPending() } | |
| 50 | 51 | mapView.getMapAsync(this) |
| 51 | 52 | } |
| 52 | 53 | |
| @@ -128,7 +129,7 @@ internal class CraftNativeMapView(context: Context) : FrameLayout(context), OnMa | ||
| 128 | 129 | val key = "region:${pendingProps.optJSONObject("initialRegion")}" |
| 129 | 130 | if (initialCameraKey != key) { moveCamera(initial, 0); initialCameraKey = key } |
| 130 | 131 | } else if (craftNativeBoolean(pendingProps.opt("fitToBounds"))) { |
| 131 | val key = "$width,$height:${pendingProps.optJSONObject("edgePadding")}:" + geometryPoints.joinToString(";") { "${it.latitude},${it.longitude}" } | |
| 132 | val key = "$width,$height:${pendingProps.optJSONObject("edgePadding")}:${pendingProps.opt("cameraPitch")}:${pendingProps.opt("cameraHeading")}:" + geometryPoints.joinToString(";") { "${it.latitude},${it.longitude}" } | |
| 132 | 133 | if (initialCameraKey != key && geometryPoints.isNotEmpty() && width > 0 && height > 0) { |
| 133 | 134 | val distinct = geometryPoints.distinctBy { it.latitude to it.longitude } |
| 134 | 135 | if (distinct.size == 1) nativeMap.moveCamera(CameraUpdateFactory.newLatLngZoom(distinct.first(), 15.0)) |
| @@ -198,7 +199,7 @@ internal class CraftNativeMapView(context: Context) : FrameLayout(context), OnMa | ||
| 198 | 199 | val events = child.optJSONObject("events") ?: JSONObject() |
| 199 | 200 | val id = craftNativeOptionalString(props.opt("id")) ?: craftNativeOptionalString(child.opt("id")) ?: "overlay-$index" |
| 200 | 201 | val points = coordinates(props.optJSONArray("geometry")) |
| 201 | val properties = JSONObject() | |
| 202 | val properties = JSONObject().put("craftOverlayId", id) | |
| 202 | 203 | val geometry = JSONObject() |
| 203 | 204 | if ((type == "mapmarker" || type == "marker") && points.isNotEmpty()) { |
| 204 | 205 | val point = points.first() |
| @@ -273,7 +274,18 @@ internal class CraftNativeMapView(context: Context) : FrameLayout(context), OnMa | ||
| 273 | 274 | } |
| 274 | 275 | |
| 275 | 276 | private fun moveCamera(bounds: LatLngBounds, padding: Int) { |
| 276 | post { if (!destroyed && width > 0 && height > 0) { map?.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, padding)); applyOrientation() } } | |
| 277 | post { | |
| 278 | val nativeMap = map | |
| 279 | if (!destroyed && nativeMap != null && width > 0 && height > 0) { | |
| 280 | val camera = nativeMap.cameraPosition | |
| 281 | val bearing = finiteNumber(pendingProps.opt("cameraHeading")) ?: camera.bearing | |
| 282 | val tilt = finiteNumber(pendingProps.opt("cameraPitch"))?.coerceIn(0.0, 60.0) ?: camera.tilt | |
| 283 | val insets = nativeMap.padding.map { it + padding }.toIntArray() | |
| 284 | val target = nativeMap.getCameraForLatLngBounds(bounds, insets, bearing, tilt) | |
| 285 | if (target == null) initialCameraKey = null | |
| 286 | else nativeMap.moveCamera(CameraUpdateFactory.newCameraPosition(target)) | |
| 287 | } | |
| 288 | } | |
| 277 | 289 | } |
| 278 | 290 | |
| 279 | 291 | private fun applyOrientation() { |