Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 14 additions & 8 deletions .github/workflows/prepare_dev_for_next_release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,21 +47,27 @@ jobs:
git push origin main

sync-dev:
name: Merge main into dev
name: Sync dev with main
runs-on: ubuntu-latest
needs: update-pods
if: ${{ always() && github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.head_branch == 'main' && startsWith(github.event.workflow_run.head_commit.message, 'release:') }}

steps:
- name: Checkout
- name: Checkout dev
uses: actions/checkout@v5.0.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0
ref: dev

- name: Fetch all branches
run: git fetch --all

- name: Fast-forward dev with main
- name: Reset dev to main
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"

git fetch origin main dev

git checkout dev
git merge origin/main --ff-only
git push origin dev
git reset --hard origin/main

git push --force-with-lease origin dev
4 changes: 2 additions & 2 deletions .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@ jobs:
- name: Generate nitrogen code
run: yarn nitrogen

- name: Install ktlint & swiftlint
- name: Install ktlint & swiftformat
run: |
brew install ktlint
brew install swiftlint
brew install swiftformat

- name: Lint files
run: yarn lint
Expand Down
1 change: 1 addition & 0 deletions .yarnrc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ nmHoistingLimits: workspaces

nodeLinker: node-modules
npmMinimalAgeGate: 2880 # 2 days in minutes
defaultSemverRangePrefix: ''

yarnPath: .yarn/releases/yarn-4.10.3.cjs
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
[![API Docs](https://img.shields.io/static/v1?label=typedoc&message=docs&color=informational)](https://pinpong.github.io/react-native-google-maps-plus)
![React Native](https://img.shields.io/badge/react--native-%3E%3D0.82.0-61dafb.svg?logo=react)

React Native wrapper for Android & iOS Google Maps SDK.
React Native wrapper for Android & iOS Google Maps SDK with Street View support

## Documentation

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.rngooglemapsplus

import android.content.Context
import android.view.MotionEvent
import android.widget.FrameLayout

abstract class GestureAwareFrameLayout(
context: Context,
) : FrameLayout(context) {
private var parentTouchInterceptDisallowed = false

protected abstract val panGestureEnabled: Boolean
protected abstract val multiTouchGestureEnabled: Boolean
protected open val gesturesSupported: Boolean = true

protected fun setParentTouchInterceptDisallowed(blocked: Boolean) {
if (parentTouchInterceptDisallowed == blocked) return
parentTouchInterceptDisallowed = blocked
var p = parent
while (p != null) {
p.requestDisallowInterceptTouchEvent(blocked)
p = p.parent
}
}

override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
if (!gesturesSupported) return super.dispatchTouchEvent(ev)

val anyGestureEnabled = panGestureEnabled || multiTouchGestureEnabled
if (!anyGestureEnabled) return super.dispatchTouchEvent(ev)

when (ev.actionMasked) {
MotionEvent.ACTION_DOWN,
MotionEvent.ACTION_MOVE,
MotionEvent.ACTION_POINTER_DOWN,
-> {
val pointers = ev.pointerCount
val shouldBlockParent = pointers >= (if (panGestureEnabled) 1 else 2)
setParentTouchInterceptDisallowed(shouldBlockParent)
}

MotionEvent.ACTION_POINTER_UP -> {
val pointers = ev.pointerCount - 1
val shouldBlockParent = pointers >= (if (panGestureEnabled) 1 else 2)
setParentTouchInterceptDisallowed(shouldBlockParent)
}

MotionEvent.ACTION_UP,
MotionEvent.ACTION_CANCEL,
-> {
setParentTouchInterceptDisallowed(false)
}
}

return super.dispatchTouchEvent(ev)
}
}
165 changes: 69 additions & 96 deletions android/src/main/java/com/rngooglemapsplus/GoogleMapsViewImpl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ import android.content.res.Configuration
import android.graphics.Bitmap
import android.location.Location
import android.util.Size
import android.view.MotionEvent
import android.view.View
import android.widget.FrameLayout
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.findViewTreeLifecycleOwner
import com.facebook.react.uimanager.PixelUtil.dpToPx
Expand Down Expand Up @@ -63,7 +61,7 @@ class GoogleMapsViewImpl(
private val playServiceHandler: PlayServicesHandler,
private val markerBuilder: MapMarkerBuilder,
private val mapErrorHandler: MapErrorHandler,
) : FrameLayout(reactContext),
) : GestureAwareFrameLayout(reactContext),
GoogleMap.OnCameraMoveStartedListener,
GoogleMap.OnCameraMoveListener,
GoogleMap.OnCameraIdleListener,
Expand All @@ -81,8 +79,10 @@ class GoogleMapsViewImpl(
GoogleMap.OnInfoWindowLongClickListener,
GoogleMap.OnMyLocationClickListener,
GoogleMap.OnMyLocationButtonClickListener,
GoogleMap.InfoWindowAdapter {
private var lifecycleObserver: MapLifecycleEventObserver? = null
GoogleMap.OnMapLoadedCallback,
GoogleMap.InfoWindowAdapter,
ComponentCallbacks2 {
private var lifecycleObserver: ViewLifecycleEventObserver? = null
private var lifecycle: Lifecycle? = null

private var mapViewInitialized = false
Expand All @@ -107,75 +107,11 @@ class GoogleMapsViewImpl(
private val kmlLayersById = mutableMapOf<String, KmlLayer>()
private val urlTileOverlaysById = mutableMapOf<String, TileOverlay>()

private var parentTouchInterceptDisallowed = false
private var cameraMoveReason = -1

val componentCallbacks =
object : ComponentCallbacks2 {
override fun onConfigurationChanged(newConfig: Configuration) {}

override fun onLowMemory() {
mapView?.onLowMemory()
markerBuilder.clearIconCache()
}

override fun onTrimMemory(level: Int) {
mapView?.onLowMemory()
markerBuilder.cancelAllJobs()
}
}

private fun setParentTouchInterceptDisallowed(blocked: Boolean) {
if (parentTouchInterceptDisallowed == blocked) return
parentTouchInterceptDisallowed = blocked
var p = parent
while (p != null) {
p.requestDisallowInterceptTouchEvent(blocked)
p = p.parent
}
}

override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
if (googleMapsOptions.liteMode == true) return super.dispatchTouchEvent(ev)

val panEnabled = uiSettings?.scrollEnabled == true
val zoomEnabled = uiSettings?.zoomGesturesEnabled == true
val rotateEnabled = uiSettings?.rotateEnabled == true
val tiltEnabled = uiSettings?.tiltEnabled == true

val multiTouchEnabled = zoomEnabled || rotateEnabled || tiltEnabled
val anyMapGestureEnabled = panEnabled || multiTouchEnabled
if (!anyMapGestureEnabled) return super.dispatchTouchEvent(ev)

when (ev.actionMasked) {
MotionEvent.ACTION_DOWN,
MotionEvent.ACTION_MOVE,
MotionEvent.ACTION_POINTER_DOWN,
-> {
val pointers = ev.pointerCount
val shouldBlockParent = pointers >= (if (panEnabled) 1 else 2)
setParentTouchInterceptDisallowed(shouldBlockParent)
}

MotionEvent.ACTION_POINTER_UP -> {
val pointers = ev.pointerCount - 1
val shouldBlockParent = pointers >= (if (panEnabled) 1 else 2)
setParentTouchInterceptDisallowed(shouldBlockParent)
}

MotionEvent.ACTION_UP,
MotionEvent.ACTION_CANCEL,
-> {
setParentTouchInterceptDisallowed(false)
}
}

return super.dispatchTouchEvent(ev)
}

init {
MapsInitializer.initialize(reactContext)
reactContext.registerComponentCallbacks(componentCallbacks)
reactContext.registerComponentCallbacks(this)
}

fun initMapView() =
Expand All @@ -196,42 +132,58 @@ class GoogleMapsViewImpl(

mapView =
MapView(reactContext, googleMapsOptions).also {
lifecycleObserver = MapLifecycleEventObserver(it, locationHandler)
lifecycleObserver =
ViewLifecycleEventObserver(
locationHandler = locationHandler,
onCreateView = it::onCreate,
onStartView = it::onStart,
onResumeView = it::onResume,
onPauseView = it::onPause,
onStopView = it::onStop,
onDestroyView = it::onDestroy,
)
super.addView(it)
it.getMapAsync { map ->
if (destroyed) return@getMapAsync
googleMap = map
googleMap?.setLocationSource(locationHandler)
googleMap?.setOnMapLoadedCallback {
googleMap?.setOnCameraMoveStartedListener(this@GoogleMapsViewImpl)
googleMap?.setOnCameraMoveListener(this@GoogleMapsViewImpl)
googleMap?.setOnCameraIdleListener(this@GoogleMapsViewImpl)
googleMap?.setOnMarkerClickListener(this@GoogleMapsViewImpl)
googleMap?.setOnPolylineClickListener(this@GoogleMapsViewImpl)
googleMap?.setOnPolygonClickListener(this@GoogleMapsViewImpl)
googleMap?.setOnCircleClickListener(this@GoogleMapsViewImpl)
googleMap?.setOnMapClickListener(this@GoogleMapsViewImpl)
googleMap?.setOnMapLongClickListener(this@GoogleMapsViewImpl)
googleMap?.setOnPoiClickListener(this@GoogleMapsViewImpl)
googleMap?.setOnMarkerDragListener(this@GoogleMapsViewImpl)
googleMap?.setOnInfoWindowClickListener(this@GoogleMapsViewImpl)
googleMap?.setOnInfoWindowCloseListener(this@GoogleMapsViewImpl)
googleMap?.setOnInfoWindowLongClickListener(this@GoogleMapsViewImpl)
googleMap?.setOnMyLocationClickListener(this@GoogleMapsViewImpl)
googleMap?.setOnMyLocationButtonClickListener(this@GoogleMapsViewImpl)
googleMap?.setInfoWindowAdapter(this@GoogleMapsViewImpl)
mapViewLoaded = true
onMapLoaded?.invoke(
map.projection.visibleRegion.toRnRegion(),
map.cameraPosition.toRnCamera(),
)
}
googleMap?.setOnMapLoadedCallback(this@GoogleMapsViewImpl)
applyProps()
initLocationCallbacks()
onMapReady?.invoke(true)
}
}
}

override fun onMapLoaded() =
onUi {
googleMap?.setOnCameraMoveStartedListener(this@GoogleMapsViewImpl)
googleMap?.setOnCameraMoveListener(this@GoogleMapsViewImpl)
googleMap?.setOnCameraIdleListener(this@GoogleMapsViewImpl)
googleMap?.setOnMarkerClickListener(this@GoogleMapsViewImpl)
googleMap?.setOnPolylineClickListener(this@GoogleMapsViewImpl)
googleMap?.setOnPolygonClickListener(this@GoogleMapsViewImpl)
googleMap?.setOnCircleClickListener(this@GoogleMapsViewImpl)
googleMap?.setOnMapClickListener(this@GoogleMapsViewImpl)
googleMap?.setOnMapLongClickListener(this@GoogleMapsViewImpl)
googleMap?.setOnPoiClickListener(this@GoogleMapsViewImpl)
googleMap?.setOnIndoorStateChangeListener(this@GoogleMapsViewImpl)
googleMap?.setOnMarkerDragListener(this@GoogleMapsViewImpl)
googleMap?.setOnInfoWindowClickListener(this@GoogleMapsViewImpl)
googleMap?.setOnInfoWindowCloseListener(this@GoogleMapsViewImpl)
googleMap?.setOnInfoWindowLongClickListener(this@GoogleMapsViewImpl)
googleMap?.setOnMyLocationClickListener(this@GoogleMapsViewImpl)
googleMap?.setOnMyLocationButtonClickListener(this@GoogleMapsViewImpl)
googleMap?.setInfoWindowAdapter(this@GoogleMapsViewImpl)
mapViewLoaded = true
googleMap?.let { map ->
onMapLoaded?.invoke(
map.projection.visibleRegion.toRnRegion(),
map.cameraPosition.toRnCamera(),
)
}
}

override fun onCameraMoveStarted(reason: Int) =
onUi {
if (!mapViewLoaded) return@onUi
Expand Down Expand Up @@ -873,6 +825,7 @@ class GoogleMapsViewImpl(
clearKmlLayer()
clearUrlTileOverlays()
googleMap?.apply {
setOnMapLoadedCallback(null)
setOnCameraMoveStartedListener(null)
setOnCameraMoveListener(null)
setOnCameraIdleListener(null)
Expand Down Expand Up @@ -901,7 +854,7 @@ class GoogleMapsViewImpl(
mapView?.removeAllViews()
mapView = null
super.removeAllViews()
reactContext.unregisterComponentCallbacks(componentCallbacks)
reactContext.unregisterComponentCallbacks(this)
}

override fun requestLayout() {
Expand Down Expand Up @@ -932,6 +885,26 @@ class GoogleMapsViewImpl(
super.onDetachedFromWindow()
}

override val gesturesSupported get() = googleMapsOptions.liteMode != true
override val panGestureEnabled get() = uiSettings?.scrollEnabled == true
override val multiTouchGestureEnabled
get() =
(uiSettings?.zoomGesturesEnabled == true) ||
(uiSettings?.rotateEnabled == true) ||
(uiSettings?.tiltEnabled == true)

override fun onConfigurationChanged(newConfig: Configuration) {}

override fun onLowMemory() {
mapView?.onLowMemory()
markerBuilder.clearIconCache()
}

override fun onTrimMemory(level: Int) {
mapView?.onLowMemory()
markerBuilder.cancelAllJobs()
}

override fun onMarkerClick(marker: Marker): Boolean {
onUi {
onMarkerPress?.invoke(marker.idTag)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@ class MapMarkerBuilder(
private suspend fun renderBitmap(
iconSvg: RNMarkerSvg,
markerId: String,
): RenderBitmapResult? {
): RenderBitmapResult {
val wPx =
iconSvg.width
.dpToPx()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.rngooglemapsplus;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.google.android.gms.maps.StreetViewPanorama;
import com.google.android.gms.maps.model.StreetViewPanoramaLocation;

public interface OnStreetViewPanoramaChangeListenerNullSafe
extends StreetViewPanorama.OnStreetViewPanoramaChangeListener {

@Override
default void onStreetViewPanoramaChange(@NonNull StreetViewPanoramaLocation location) {
onStreetViewPanoramaChangeNullable(location);
}

void onStreetViewPanoramaChangeNullable(@Nullable StreetViewPanoramaLocation location);
}
Loading
Loading