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
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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.module.model.ReactModuleInfoProvider
import com.facebook.react.uimanager.ViewManager
import com.rngooglemapsplus.RNGoogleMapsPlusPackage.AppContextHolder.context
import com.rngooglemapsplus.views.HybridRNGoogleMapsPlusStreetViewManager
import com.rngooglemapsplus.views.HybridRNGoogleMapsPlusViewManager

class RNGoogleMapsPlusPackage : BaseReactPackage() {
Expand All @@ -20,6 +21,7 @@ class RNGoogleMapsPlusPackage : BaseReactPackage() {
context = reactContext
return listOf(
HybridRNGoogleMapsPlusViewManager(),
HybridRNGoogleMapsPlusStreetViewManager(),
)
}

Expand Down
Loading
Loading