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
10 changes: 0 additions & 10 deletions .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -105,17 +105,7 @@ jobs:
with:
xcode-version: ${{ env.XCODE_VERSION }}

- name: Cache Pods
id: pods-cache
uses: actions/cache@v4.2.4
with:
path: example/ios/Pods
key: ${{ runner.os }}-pods-${{ hashFiles('example/ios/Podfile', 'example/ios/Podfile.lock', 'example/package.json') }}
restore-keys: |
${{ runner.os }}-pods-

- name: Install cocoapods
if: steps.pods-cache.outputs.cache-hit != 'true'
working-directory: example
run: yarn ios:pods

Expand Down
30 changes: 0 additions & 30 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,6 @@
* fix CHANGELOG.md ([2f2bb2c](https://github.com/pinpong/react-native-google-maps-plus/commit/2f2bb2c617260166551abbc07dfa9a8ae27cf31e))
* merge dev into main ([a510e2a](https://github.com/pinpong/react-native-google-maps-plus/commit/a510e2a1bebabde03e319256b2a1246f10ce1b95))

## [1.2.0-dev.1](https://github.com/pinpong/react-native-google-maps-plus/compare/v1.1.0...v1.2.0-dev.1) (2025-10-09)

### ✨ Features

* add kml layer support ([4faf558](https://github.com/pinpong/react-native-google-maps-plus/commit/4faf558425831cc18a6e9c9e2d20ef0c4f42e702))
* add kml layer support ([35098bd](https://github.com/pinpong/react-native-google-maps-plus/commit/35098bd4c75b825f96f58696cbb37a4fcdebbdb8))

### 🐛 Bug Fixes

* **example:** build issues ([cee0708](https://github.com/pinpong/react-native-google-maps-plus/commit/cee0708dfdee185ee4c8bb2836abd2a3c022fc93))

### 🛠️ Other changes

* **ci:** move PR template to root for auto-apply ([03e8a84](https://github.com/pinpong/react-native-google-maps-plus/commit/03e8a8438b0d5edab80fcdf2f2c8abf3372288c2))
* **example:** beautify example app UI ([4f390ec](https://github.com/pinpong/react-native-google-maps-plus/commit/4f390ecd9ebc2f3e559913882ac56d33a30ac45b))
* **example:** beautify example app UI ([73c997c](https://github.com/pinpong/react-native-google-maps-plus/commit/73c997c69f23deeb48eb9b2be5df76a36ff0afea))
* fix CHANGELOG.md ([2f2bb2c](https://github.com/pinpong/react-native-google-maps-plus/commit/2f2bb2c617260166551abbc07dfa9a8ae27cf31e))

## [1.1.0](https://github.com/pinpong/react-native-google-maps-plus/compare/v1.0.2...v1.1.0) (2025-10-08)

### ✨ Features
Expand Down Expand Up @@ -78,18 +60,6 @@
* update to react-native 0.82.0 ([31d5ff5](https://github.com/pinpong/react-native-google-maps-plus/commit/31d5ff5157ec8357b9d699d4dcc09bda09e11afb))
* update to react-native 0.82.0 ([8c8e8ae](https://github.com/pinpong/react-native-google-maps-plus/commit/8c8e8ae1c4fcf97e04059d873461f083e4c346cf))

## [1.1.0-dev.5](https://github.com/pinpong/react-native-google-maps-plus/compare/v1.1.0-dev.4...v1.1.0-dev.5) (2025-10-08)

### 🐛 Bug Fixes

* **example:** build issues ([cee0708](https://github.com/pinpong/react-native-google-maps-plus/commit/cee0708dfdee185ee4c8bb2836abd2a3c022fc93))

### 🛠️ Other changes

* **example:** beautify example app UI ([4f390ec](https://github.com/pinpong/react-native-google-maps-plus/commit/4f390ecd9ebc2f3e559913882ac56d33a30ac45b))
* **example:** beautify example app UI ([73c997c](https://github.com/pinpong/react-native-google-maps-plus/commit/73c997c69f23deeb48eb9b2be5df76a36ff0afea))
* fix CHANGELOG.md ([2f2bb2c](https://github.com/pinpong/react-native-google-maps-plus/commit/2f2bb2c617260166551abbc07dfa9a8ae27cf31e))

## [1.1.0](https://github.com/pinpong/react-native-google-maps-plus/compare/v1.0.2...v1.1.0) (2025-10-08)

### ✨ Features
Expand Down
165 changes: 149 additions & 16 deletions android/src/main/java/com/rngooglemapsplus/GoogleMapsViewImpl.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package com.rngooglemapsplus

import android.annotation.SuppressLint
import android.graphics.Bitmap
import android.location.Location
import android.util.Base64
import android.util.Size
import android.widget.FrameLayout
import androidx.core.graphics.scale
import com.facebook.react.bridge.LifecycleEventListener
import com.facebook.react.bridge.UiThreadUtil
import com.facebook.react.uimanager.PixelUtil.dpToPx
Expand All @@ -15,6 +19,7 @@ import com.google.android.gms.maps.MapView
import com.google.android.gms.maps.model.CameraPosition
import com.google.android.gms.maps.model.Circle
import com.google.android.gms.maps.model.CircleOptions
import com.google.android.gms.maps.model.IndoorBuilding
import com.google.android.gms.maps.model.LatLng
import com.google.android.gms.maps.model.LatLngBounds
import com.google.android.gms.maps.model.MapColorScheme
Expand All @@ -28,9 +33,15 @@ import com.google.android.gms.maps.model.PolylineOptions
import com.google.android.gms.maps.model.TileOverlay
import com.google.android.gms.maps.model.TileOverlayOptions
import com.google.maps.android.data.kml.KmlLayer
import com.margelo.nitro.core.Promise
import com.rngooglemapsplus.extensions.toGooglePriority
import com.rngooglemapsplus.extensions.toLocationErrorCode
import com.rngooglemapsplus.extensions.toRNIndoorBuilding
import com.rngooglemapsplus.extensions.toRNIndoorLevel
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.FileOutputStream
import java.nio.charset.StandardCharsets

class GoogleMapsViewImpl(
Expand All @@ -47,6 +58,8 @@ class GoogleMapsViewImpl(
GoogleMap.OnPolylineClickListener,
GoogleMap.OnPolygonClickListener,
GoogleMap.OnCircleClickListener,
GoogleMap.OnMarkerDragListener,
GoogleMap.OnIndoorStateChangeListener,
LifecycleEventListener {
private var initialized = false
private var mapReady = false
Expand Down Expand Up @@ -136,12 +149,13 @@ class GoogleMapsViewImpl(
googleMap?.setOnPolygonClickListener(this@GoogleMapsViewImpl)
googleMap?.setOnCircleClickListener(this@GoogleMapsViewImpl)
googleMap?.setOnMapClickListener(this@GoogleMapsViewImpl)
googleMap?.setOnMarkerDragListener(this@GoogleMapsViewImpl)
}
initLocationCallbacks()
applyPending()
mapReady = true
onMapReady?.invoke(true)
}
mapReady = true
onMapReady?.invoke(true)
}

override fun onCameraMoveStarted(reason: Int) {
Expand Down Expand Up @@ -182,6 +196,8 @@ class GoogleMapsViewImpl(
if (cameraPosition == lastSubmittedCameraPosition) {
return
}
lastSubmittedCameraPosition = cameraPosition

val isGesture = GoogleMap.OnCameraMoveStartedListener.REASON_GESTURE == cameraMoveReason

val latDelta = bounds.northeast.latitude - bounds.southwest.latitude
Expand All @@ -201,7 +217,6 @@ class GoogleMapsViewImpl(
),
isGesture,
)
lastSubmittedCameraPosition = cameraPosition
}

override fun onCameraIdle() {
Expand Down Expand Up @@ -357,6 +372,8 @@ class GoogleMapsViewImpl(
}
}

var initialProps: RNInitialProps? = null

var uiSettings: RNMapUiSettings? = null
set(value) {
field = value
Expand Down Expand Up @@ -481,18 +498,23 @@ class GoogleMapsViewImpl(
var onLocationUpdate: ((RNLocation) -> Unit)? = null
var onLocationError: ((RNLocationErrorCode) -> Unit)? = null
var onMapPress: ((RNLatLng) -> Unit)? = null
var onMarkerPress: ((String) -> Unit)? = null
var onPolylinePress: ((String) -> Unit)? = null
var onPolygonPress: ((String) -> Unit)? = null
var onCirclePress: ((String) -> Unit)? = null
var onMarkerPress: ((String?) -> Unit)? = null
var onPolylinePress: ((String?) -> Unit)? = null
var onPolygonPress: ((String?) -> Unit)? = null
var onCirclePress: ((String?) -> Unit)? = null
var onMarkerDragStart: ((String?, RNLatLng) -> Unit)? = null
var onMarkerDrag: ((String?, RNLatLng) -> Unit)? = null
var onMarkerDragEnd: ((String?, RNLatLng) -> Unit)? = null
var onIndoorBuildingFocused: ((RNIndoorBuilding) -> Unit)? = null
var onIndoorLevelActivated: ((RNIndoorLevel) -> Unit)? = null
var onCameraChangeStart: ((RNRegion, RNCamera, Boolean) -> Unit)? = null
var onCameraChange: ((RNRegion, RNCamera, Boolean) -> Unit)? = null
var onCameraChangeComplete: ((RNRegion, RNCamera, Boolean) -> Unit)? = null

fun setCamera(
cameraPosition: CameraPosition,
animated: Boolean,
durationMS: Int,
durationMs: Int,
) {
onUi {
val current = googleMap?.cameraPosition
Expand All @@ -503,7 +525,7 @@ class GoogleMapsViewImpl(
val update = CameraUpdateFactory.newCameraPosition(cameraPosition)

if (animated) {
googleMap?.animateCamera(update, durationMS, null)
googleMap?.animateCamera(update, durationMs, null)
} else {
googleMap?.moveCamera(update)
}
Expand All @@ -514,7 +536,7 @@ class GoogleMapsViewImpl(
coordinates: Array<RNLatLng>,
padding: RNMapPadding,
animated: Boolean,
durationMS: Int,
durationMs: Int,
) {
if (coordinates.isEmpty()) {
return
Expand Down Expand Up @@ -572,13 +594,85 @@ class GoogleMapsViewImpl(
0,
)
if (animated) {
googleMap?.animateCamera(update, durationMS, null)
googleMap?.animateCamera(update, durationMs, null)
} else {
googleMap?.moveCamera(update)
}
}
}

fun setCameraBounds(bounds: LatLngBounds?) {
onUi {
googleMap?.setLatLngBoundsForCameraTarget(bounds)
}
}

fun animateToBounds(
bounds: LatLngBounds,
padding: Int,
durationMs: Int,
lockBounds: Boolean,
) {
onUi {
if (lockBounds) {
googleMap?.setLatLngBoundsForCameraTarget(bounds)
}
val update =
CameraUpdateFactory.newLatLngBounds(
bounds,
padding,
)
googleMap?.animateCamera(update, durationMs, null)
}
}

fun snapshot(
size: Size?,
format: String,
compressFormat: Bitmap.CompressFormat,
quality: Double,
resultIsFile: Boolean,
): Promise<String?> {
val promise = Promise<String?>()
onUi {
googleMap?.snapshot { bitmap ->
try {
if (bitmap == null) {
promise.resolve(null)
return@snapshot
}

val scaledBitmap =
size?.let {
bitmap.scale(it.width, it.height)
} ?: bitmap

val output = ByteArrayOutputStream()
scaledBitmap.compress(compressFormat, (quality * 100).toInt().coerceIn(0, 100), output)
val bytes = output.toByteArray()

if (resultIsFile) {
val file = File(context.cacheDir, "map_snapshot_${System.currentTimeMillis()}.$format")
FileOutputStream(file).use { it.write(bytes) }
promise.resolve(file.absolutePath)
} else {
val base64 = Base64.encodeToString(bytes, Base64.NO_WRAP)
promise.resolve("data:image/$format;base64,$base64")
}

if (scaledBitmap != bitmap) {
scaledBitmap.recycle()
}
bitmap.recycle()
} catch (e: Exception) {
promise.resolve(null)
}
}
}

return promise
}

fun addMarker(
id: String,
opts: MarkerOptions,
Expand Down Expand Up @@ -883,14 +977,14 @@ class GoogleMapsViewImpl(

fun destroyInternal() {
onUi {
locationHandler.stop()
markerBuilder.cancelAllJobs()
clearMarkers()
clearPolylines()
clearPolygons()
clearCircles()
clearHeatmaps()
clearKmlLayer()
locationHandler.stop()
googleMap?.apply {
setOnCameraMoveStartedListener(null)
setOnCameraMoveListener(null)
Expand All @@ -900,6 +994,7 @@ class GoogleMapsViewImpl(
setOnPolygonClickListener(null)
setOnCircleClickListener(null)
setOnMapClickListener(null)
setOnMarkerDragListener(null)
}
googleMap = null
mapView?.apply {
Expand All @@ -910,6 +1005,7 @@ class GoogleMapsViewImpl(
}
super.removeAllViews()
reactContext.removeLifecycleEventListener(this)
initialized = false
}
}

Expand Down Expand Up @@ -954,27 +1050,64 @@ class GoogleMapsViewImpl(
}

override fun onMarkerClick(marker: Marker): Boolean {
onMarkerPress?.invoke(marker.tag?.toString() ?: "unknown")
marker.showInfoWindow()
onMarkerPress?.invoke(marker.tag?.toString())
return true
}

override fun onPolylineClick(polyline: Polyline) {
onPolylinePress?.invoke(polyline.tag?.toString() ?: "unknown")
onPolylinePress?.invoke(polyline.tag?.toString())
}

override fun onPolygonClick(polygon: Polygon) {
onPolygonPress?.invoke(polygon.tag?.toString() ?: "unknown")
onPolygonPress?.invoke(polygon.tag?.toString())
}

override fun onCircleClick(circle: Circle) {
onCirclePress?.invoke(circle.tag?.toString() ?: "unknown")
onCirclePress?.invoke(circle.tag?.toString())
}

override fun onMapClick(coordinates: LatLng) {
onMapPress?.invoke(
RNLatLng(coordinates.latitude, coordinates.longitude),
)
}

override fun onMarkerDragStart(marker: Marker) {
onMarkerDragStart?.invoke(
marker.tag?.toString(),
RNLatLng(marker.position.latitude, marker.position.longitude),
)
}

override fun onMarkerDrag(marker: Marker) {
onMarkerDrag?.invoke(
marker.tag?.toString(),
RNLatLng(marker.position.latitude, marker.position.longitude),
)
}

override fun onMarkerDragEnd(marker: Marker) {
onMarkerDragEnd?.invoke(
marker.tag?.toString(),
RNLatLng(marker.position.latitude, marker.position.longitude),
)
}

override fun onIndoorBuildingFocused() {
val building = googleMap?.focusedBuilding ?: return
onIndoorBuildingFocused?.invoke(building.toRNIndoorBuilding())
}

override fun onIndoorLevelActivated(indoorBuilding: IndoorBuilding) {
val activeLevel = indoorBuilding.levels.getOrNull(indoorBuilding.activeLevelIndex) ?: return
onIndoorLevelActivated?.invoke(
activeLevel.toRNIndoorLevel(
indoorBuilding.activeLevelIndex,
true,
),
)
}
}

private inline fun onUi(crossinline block: () -> Unit) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,6 @@ class LocationHandler(

private fun restartLocationUpdates() {
stop()
// 4) Google Play Services checken – früh zurückmelden
val playServicesStatus =
GoogleApiAvailability
.getInstance()
Expand Down
Loading
Loading