Skip to content

Commit 64073d4

Browse files
committed
feat: add indoor building focus and level activation listener support
1 parent 2f039c4 commit 64073d4

File tree

13 files changed

+200
-3
lines changed

13 files changed

+200
-3
lines changed

android/src/main/java/com/rngooglemapsplus/GoogleMapsViewImpl.kt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import com.google.android.gms.maps.MapView
1515
import com.google.android.gms.maps.model.CameraPosition
1616
import com.google.android.gms.maps.model.Circle
1717
import com.google.android.gms.maps.model.CircleOptions
18+
import com.google.android.gms.maps.model.IndoorBuilding
1819
import com.google.android.gms.maps.model.LatLng
1920
import com.google.android.gms.maps.model.LatLngBounds
2021
import com.google.android.gms.maps.model.MapColorScheme
@@ -30,6 +31,8 @@ import com.google.android.gms.maps.model.TileOverlayOptions
3031
import com.google.maps.android.data.kml.KmlLayer
3132
import com.rngooglemapsplus.extensions.toGooglePriority
3233
import com.rngooglemapsplus.extensions.toLocationErrorCode
34+
import com.rngooglemapsplus.extensions.toRNIndoorBuilding
35+
import com.rngooglemapsplus.extensions.toRNIndoorLevel
3336
import java.io.ByteArrayInputStream
3437
import java.nio.charset.StandardCharsets
3538

@@ -48,6 +51,7 @@ class GoogleMapsViewImpl(
4851
GoogleMap.OnPolygonClickListener,
4952
GoogleMap.OnCircleClickListener,
5053
GoogleMap.OnMarkerDragListener,
54+
GoogleMap.OnIndoorStateChangeListener,
5155
LifecycleEventListener {
5256
private var initialized = false
5357
private var mapReady = false
@@ -490,6 +494,8 @@ class GoogleMapsViewImpl(
490494
var onMarkerDragStart: ((String?, RNLatLng) -> Unit)? = null
491495
var onMarkerDrag: ((String?, RNLatLng) -> Unit)? = null
492496
var onMarkerDragEnd: ((String?, RNLatLng) -> Unit)? = null
497+
var onIndoorBuildingFocused: ((RNIndoorBuilding) -> Unit)? = null
498+
var onIndoorLevelActivated: ((RNIndoorLevel) -> Unit)? = null
493499
var onCameraChangeStart: ((RNRegion, RNCamera, Boolean) -> Unit)? = null
494500
var onCameraChange: ((RNRegion, RNCamera, Boolean) -> Unit)? = null
495501
var onCameraChangeComplete: ((RNRegion, RNCamera, Boolean) -> Unit)? = null
@@ -1003,6 +1009,21 @@ class GoogleMapsViewImpl(
10031009
RNLatLng(marker.position.latitude, marker.position.longitude),
10041010
)
10051011
}
1012+
1013+
override fun onIndoorBuildingFocused() {
1014+
val building = googleMap?.focusedBuilding ?: return
1015+
onIndoorBuildingFocused?.invoke(building.toRNIndoorBuilding())
1016+
}
1017+
1018+
override fun onIndoorLevelActivated(indoorBuilding: IndoorBuilding) {
1019+
val activeLevel = indoorBuilding.levels.getOrNull(indoorBuilding.activeLevelIndex) ?: return
1020+
onIndoorLevelActivated?.invoke(
1021+
activeLevel.toRNIndoorLevel(
1022+
indoorBuilding.activeLevelIndex,
1023+
true,
1024+
),
1025+
)
1026+
}
10061027
}
10071028

10081029
private inline fun onUi(crossinline block: () -> Unit) {

android/src/main/java/com/rngooglemapsplus/RNGoogleMapsPlusView.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,16 @@ class RNGoogleMapsPlusView(
315315
view.onMarkerDragEnd = cb
316316
}
317317

318+
override var onIndoorBuildingFocused: ((RNIndoorBuilding) -> Unit)? = null
319+
set(cb) {
320+
view.onIndoorBuildingFocused = cb
321+
}
322+
323+
override var onIndoorLevelActivated: ((RNIndoorLevel) -> Unit)? = null
324+
set(cb) {
325+
view.onIndoorLevelActivated = cb
326+
}
327+
318328
override var onCameraChangeStart: ((RNRegion, RNCamera, Boolean) -> Unit)? = null
319329
set(cb) {
320330
view.onCameraChangeStart = cb
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.rngooglemapsplus.extensions
2+
3+
import com.google.android.gms.maps.model.IndoorBuilding
4+
import com.google.android.gms.maps.model.IndoorLevel
5+
import com.rngooglemapsplus.RNIndoorBuilding
6+
import com.rngooglemapsplus.RNIndoorLevel
7+
8+
fun IndoorBuilding.toRNIndoorBuilding(): RNIndoorBuilding {
9+
val mappedLevels =
10+
levels
11+
.mapIndexed { index, level ->
12+
val active = index == activeLevelIndex
13+
level.toRNIndoorLevel(index, active)
14+
}.toTypedArray()
15+
16+
return RNIndoorBuilding(
17+
activeLevelIndex = activeLevelIndex.toDouble(),
18+
defaultLevelIndex = defaultLevelIndex.toDouble(),
19+
levels = mappedLevels,
20+
underground = isUnderground,
21+
)
22+
}
23+
24+
fun IndoorLevel.toRNIndoorLevel(
25+
index: Int,
26+
active: Boolean,
27+
): RNIndoorLevel =
28+
RNIndoorLevel(
29+
index = index.toDouble(),
30+
name = name,
31+
shortName = shortName,
32+
active = active,
33+
)

example/src/App.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import StressTestScreen from './screens/StressTestScreen';
2020
import { GestureHandlerRootView } from 'react-native-gesture-handler';
2121
import { useColorScheme } from 'react-native';
2222
import BlankScreen from './screens/BlankScreen';
23+
import IndoorLevelMapScreen from './screens/IndoorLevelMapScreen';
2324
import type { RootStackParamList } from './types/navigation';
2425

2526
const Stack = createStackNavigator<RootStackParamList>();
@@ -94,6 +95,11 @@ export default function App() {
9495
component={CustomStyleScreen}
9596
options={{ title: 'Custom Map Style' }}
9697
/>
98+
<Stack.Screen
99+
name="IndoorLevelMap"
100+
component={IndoorLevelMapScreen}
101+
options={{ title: 'Indoor level map' }}
102+
/>
97103
<Stack.Screen
98104
name="StressTest"
99105
component={StressTestScreen}

example/src/components/MapWrapper.tsx

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import React, { useMemo } from 'react';
22
import { StyleSheet, useColorScheme, View } from 'react-native';
3-
import { GoogleMapsView } from 'react-native-google-maps-plus';
3+
import {
4+
GoogleMapsView,
5+
type RNIndoorBuilding,
6+
type RNIndoorLevel,
7+
} from 'react-native-google-maps-plus';
48
import type {
59
GoogleMapsViewRef,
610
RNGoogleMapsPlusViewProps,
@@ -143,6 +147,18 @@ export default function MapWrapper(props: Props) {
143147
console.log('Marker drag end', id, latLng),
144148
}
145149
)}
150+
onIndoorBuildingFocused={callback(
151+
props.onIndoorBuildingFocused ?? {
152+
f: (building: RNIndoorBuilding) =>
153+
console.log('Indoor building focused', building),
154+
}
155+
)}
156+
onIndoorLevelActivated={callback(
157+
props.onIndoorLevelActivated ?? {
158+
f: (level: RNIndoorLevel) =>
159+
console.log('Indoor level activated', level),
160+
}
161+
)}
146162
onCameraChangeStart={callback(
147163
props.onCameraChangeStart ?? {
148164
f: (r: RNRegion, cam: RNCamera, g: boolean) =>

example/src/screens/HomeScreen.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const screens = [
1414
{ name: 'KmlLayer', title: 'KML Layer' },
1515
{ name: 'Location', title: 'Location & Permissions' },
1616
{ name: 'CustomStyle', title: 'Custom Map Style' },
17+
{ name: 'IndoorLevelMap', title: 'Indoor level map' },
1718
{ name: 'StressTest', title: 'Stress Test' },
1819
];
1920

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import React, { useRef } from 'react';
2+
import MapWrapper from '../components/MapWrapper';
3+
import type { GoogleMapsViewRef } from 'react-native-google-maps-plus';
4+
import ControlPanel from '../components/ControlPanel';
5+
6+
export default function IndoorLevelMapScreen() {
7+
const mapRef = useRef<GoogleMapsViewRef | null>(null);
8+
return (
9+
<MapWrapper
10+
mapRef={mapRef}
11+
initialProps={{
12+
camera: {
13+
center: {
14+
latitude: 37.617596832174385,
15+
longitude: -122.38253440707922,
16+
},
17+
zoom: 18,
18+
},
19+
}}
20+
indoorEnabled={true}
21+
buildingEnabled={true}
22+
>
23+
<ControlPanel mapRef={mapRef} buttons={[]} />
24+
</MapWrapper>
25+
);
26+
}

example/src/types/navigation.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export type RootStackParamList = {
1010
KmlLayer: undefined;
1111
Location: undefined;
1212
CustomStyle: undefined;
13+
IndoorLevelMap: undefined;
1314
StressTest: undefined;
1415
};
1516

ios/GoogleMapViewImpl.swift

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import GoogleMaps
33
import GoogleMapsUtils
44
import UIKit
55

6-
final class GoogleMapsViewImpl: UIView, GMSMapViewDelegate {
6+
final class GoogleMapsViewImpl: UIView, GMSMapViewDelegate,
7+
GMSIndoorDisplayDelegate {
78

89
private let locationHandler: LocationHandler
910
private let markerBuilder: MapMarkerBuilder
@@ -136,7 +137,10 @@ final class GoogleMapsViewImpl: UIView, GMSMapViewDelegate {
136137
myLocationEnabled.map { mapView?.isMyLocationEnabled = $0 }
137138
buildingEnabled.map { mapView?.isBuildingsEnabled = $0 }
138139
trafficEnabled.map { mapView?.isTrafficEnabled = $0 }
139-
indoorEnabled.map { mapView?.isIndoorEnabled = $0 }
140+
indoorEnabled.map {
141+
mapView?.isIndoorEnabled = $0
142+
mapView?.indoorDisplay.delegate = $0 == true ? self : nil
143+
}
140144
customMapStyle.map { mapView?.mapStyle = $0 }
141145
mapType.map { mapView?.mapType = $0 }
142146
userInterfaceStyle.map { mapView?.overrideUserInterfaceStyle = $0 }
@@ -236,6 +240,7 @@ final class GoogleMapsViewImpl: UIView, GMSMapViewDelegate {
236240
var indoorEnabled: Bool? {
237241
didSet {
238242
mapView?.isIndoorEnabled = indoorEnabled ?? false
243+
mapView?.indoorDisplay.delegate = indoorEnabled == true ? self : nil
239244
}
240245
}
241246

@@ -304,6 +309,8 @@ final class GoogleMapsViewImpl: UIView, GMSMapViewDelegate {
304309
var onMarkerDragStart: ((String?, RNLatLng) -> Void)?
305310
var onMarkerDrag: ((String?, RNLatLng) -> Void)?
306311
var onMarkerDragEnd: ((String?, RNLatLng) -> Void)?
312+
var onIndoorBuildingFocused: ((RNIndoorBuilding) -> Void)?
313+
var onIndoorLevelActivated: ((RNIndoorLevel) -> Void)?
307314
var onCameraChangeStart: ((RNRegion, RNCamera, Bool) -> Void)?
308315
var onCameraChange: ((RNRegion, RNCamera, Bool) -> Void)?
309316
var onCameraChangeComplete: ((RNRegion, RNCamera, Bool) -> Void)?
@@ -766,4 +773,23 @@ final class GoogleMapsViewImpl: UIView, GMSMapViewDelegate {
766773
RNLatLng(marker.position.latitude, marker.position.longitude)
767774
)
768775
}
776+
777+
func didChangeActiveBuilding(_ building: GMSIndoorBuilding?) {
778+
guard let display = mapView?.indoorDisplay, let building else { return }
779+
onIndoorBuildingFocused?(building.toRNIndoorBuilding(from: display))
780+
}
781+
782+
func didChangeActiveLevel(_ level: GMSIndoorLevel?) {
783+
guard
784+
let display = mapView?.indoorDisplay,
785+
let building = display.activeBuilding,
786+
let level,
787+
let index = building.levels.firstIndex(where: {
788+
$0.name == level.name && $0.shortName == level.shortName
789+
})
790+
else { return }
791+
792+
onIndoorLevelActivated?(level.toRNIndoorLevel(index: index, active: true))
793+
}
794+
769795
}

ios/RNGoogleMapsPlusView.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,12 @@ final class RNGoogleMapsPlusView: HybridRNGoogleMapsPlusViewSpec {
319319
var onMarkerDragEnd: ((String?, RNLatLng) -> Void)? {
320320
didSet { impl.onMarkerDragEnd = onMarkerDragEnd }
321321
}
322+
var onIndoorBuildingFocused: ((RNIndoorBuilding) -> Void)? {
323+
didSet { impl.onIndoorBuildingFocused = onIndoorBuildingFocused }
324+
}
325+
var onIndoorLevelActivated: ((RNIndoorLevel) -> Void)? {
326+
didSet { impl.onIndoorLevelActivated = onIndoorLevelActivated }
327+
}
322328
var onCameraChangeStart: ((RNRegion, RNCamera, Bool) -> Void)? {
323329
didSet { impl.onCameraChangeStart = onCameraChangeStart }
324330
}

0 commit comments

Comments
 (0)