diff --git a/README.md b/README.md index 166a3ab..0bc2209 100644 --- a/README.md +++ b/README.md @@ -396,6 +396,14 @@ mapView.onMapTapped .listen((location) => print("Touched location $location")); ``` +#### Receive indoor building & indoor level +```dart +mapView.onIndoorBuildingActivated.listen( + (indoorBuilding) => print("Activated indoor building $indoorBuilding")); +mapView.onIndoorLevelActivated.listen( + (indoorLevel) => print("Activated indoor level $indoorLevel")); +``` + #### Receive camera change updates ```dart mapView.onCameraChanged.listen((cameraPosition) => diff --git a/android/src/main/kotlin/com/apptreesoftware/mapview/MapActivity.kt b/android/src/main/kotlin/com/apptreesoftware/mapview/MapActivity.kt index 1d659af..02b7bff 100644 --- a/android/src/main/kotlin/com/apptreesoftware/mapview/MapActivity.kt +++ b/android/src/main/kotlin/com/apptreesoftware/mapview/MapActivity.kt @@ -100,6 +100,19 @@ class MapActivity : AppCompatActivity(), map.setOnInfoWindowClickListener { marker -> MapViewPlugin.infoWindowTapped(marker.tag as String) } + map.setOnIndoorStateChangeListener(object : GoogleMap.OnIndoorStateChangeListener { + override fun onIndoorBuildingFocused() { + MapViewPlugin.indoorBuildingActivated(map.focusedBuilding) + } + + override fun onIndoorLevelActivated(building: IndoorBuilding?) { + if (building == null || building.activeLevelIndex < 0) { + MapViewPlugin.indoorLevelActivated(null) + } else { + MapViewPlugin.indoorLevelActivated(building.levels.get(building.activeLevelIndex)) + } + } + }) map.moveCamera(CameraUpdateFactory.newCameraPosition( MapViewPlugin.initialCameraPosition)) MapViewPlugin.onMapReady() diff --git a/android/src/main/kotlin/com/apptreesoftware/mapview/MapViewPlugin.kt b/android/src/main/kotlin/com/apptreesoftware/mapview/MapViewPlugin.kt index 94420ff..42e4593 100644 --- a/android/src/main/kotlin/com/apptreesoftware/mapview/MapViewPlugin.kt +++ b/android/src/main/kotlin/com/apptreesoftware/mapview/MapViewPlugin.kt @@ -8,6 +8,8 @@ import android.os.Build import com.google.android.gms.common.GoogleApiAvailability import com.google.android.gms.maps.GoogleMap import com.google.android.gms.maps.model.CameraPosition +import com.google.android.gms.maps.model.IndoorBuilding +import com.google.android.gms.maps.model.IndoorLevel import com.google.android.gms.maps.model.LatLng import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel @@ -154,6 +156,38 @@ class MapViewPlugin(val activity: Activity) : MethodCallHandler { val key = registrar.lookupKeyForAsset(asset) return assetManager.openFd(key) } + + fun indoorBuildingActivated(indoorBuilding: IndoorBuilding?) { + if (indoorBuilding == null) { + this.channel.invokeMethod("indoorBuildingActivated", null) + } else { + this.channel.invokeMethod("indoorBuildingActivated", mapOf( + "underground" to indoorBuilding.isUnderground, + "defaultIndex" to indoorBuilding.defaultLevelIndex, + "levels" to mappingIndoorLevels(indoorBuilding.levels))) + } + } + + fun indoorLevelActivated(level: IndoorLevel?) { + if (level == null) { + this.channel.invokeMethod("indoorLevelActivated", null) + } else { + this.channel.invokeMethod("indoorLevelActivated", mappingIndoorLevel(level)) + } + } + + private fun mappingIndoorLevels(levels: List): List> { + val list = mutableListOf>() + levels.forEach { level -> list.add(mappingIndoorLevel(level)) } + return list + } + + private fun mappingIndoorLevel(level: IndoorLevel): Map { + return mapOf( + "name" to level.name, + "shortName" to level.shortName + ) + } } override fun onMethodCall(call: MethodCall, result: Result): Unit { diff --git a/example/lib/main.dart b/example/lib/main.dart index 1dc9fa1..91e3586 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -249,6 +249,12 @@ class _MyAppState extends State { print("Info Window Tapped for ${marker.title}"); }); compositeSubscription.add(sub); + sub = mapView.onIndoorBuildingActivated.listen( + (indoorBuilding) => print("Activated indoor building $indoorBuilding")); + compositeSubscription.add(sub); + sub = mapView.onIndoorLevelActivated.listen( + (indoorLevel) => print("Activated indoor level $indoorLevel")); + compositeSubscription.add(sub); } _handleDismiss() async { diff --git a/ios/Classes/MapViewController.h b/ios/Classes/MapViewController.h index a50c50d..830beba 100644 --- a/ios/Classes/MapViewController.h +++ b/ios/Classes/MapViewController.h @@ -8,13 +8,14 @@ #import #import #import +#import @class MapViewPlugin; @class MapAnnotation; @class MapPolyline; @class MapPolygon; -@interface MapViewController : UIViewController +@interface MapViewController : UIViewController - (id)initWithPlugin:(MapViewPlugin *)plugin navigationItems:(NSArray *)items diff --git a/ios/Classes/MapViewController.m b/ios/Classes/MapViewController.m index c20bbf8..79832fa 100644 --- a/ios/Classes/MapViewController.m +++ b/ios/Classes/MapViewController.m @@ -88,6 +88,7 @@ - (void)loadView { // Creates a marker in the center of the map. self.mapView.delegate = self; + self.mapView.indoorDisplay.delegate = self; [self loadMapOptions]; self.mapView.mapType = self.mapViewType; @@ -370,6 +371,14 @@ - (void)mapView:(GMSMapView *)mapView didDragMarker:(nonnull GMSMarker *)marker{ [self.plugin annotationDrag:marker.userData position:marker.position]; } +- (void)didChangeActiveBuilding:(GMSIndoorBuilding *)building{ + [self.plugin indoorBuildingActivated:building]; +} + +- (void)didChangeActiveLevel:(GMSIndoorLevel *)level{ + [self.plugin indoorLevelActivated:level]; +} + - (CLLocationCoordinate2D) centerLocation { return self.mapView.camera.target; } diff --git a/ios/Classes/MapViewPlugin.h b/ios/Classes/MapViewPlugin.h index b36a2be..1d64b65 100644 --- a/ios/Classes/MapViewPlugin.h +++ b/ios/Classes/MapViewPlugin.h @@ -3,6 +3,8 @@ @class MapViewController; @class GMSCameraPosition; +@class GMSIndoorBuilding; +@class GMSIndoorLevel; @interface MapViewPlugin : NSObject @@ -24,6 +26,10 @@ - (void)infoWindowTapped:(NSString *)identifier; - (void)mapTapped:(CLLocationCoordinate2D)coordinate; - (void)cameraPositionChanged:(GMSCameraPosition *)position; +- (void)indoorBuildingActivated:(GMSIndoorBuilding *)indoorBuilding; +- (void)indoorLevelActivated:(GMSIndoorLevel *)indoorLevel; +- (NSArray *)mappingIndoorLevels:(NSArray *)levels; +- (NSDictionary *)mappingIndoorLevel:(GMSIndoorLevel *)level; - (int)getMapViewType:(NSString *)mapViewTypeName; - (NSString *)getAssetPath:(NSString *)iconPath; @end diff --git a/ios/Classes/MapViewPlugin.m b/ios/Classes/MapViewPlugin.m index b9cc435..fee0986 100644 --- a/ios/Classes/MapViewPlugin.m +++ b/ios/Classes/MapViewPlugin.m @@ -284,6 +284,40 @@ - (GMSCameraPosition *)cameraPositionFromDict:(NSDictionary *)dict { return camera; } +- (void)indoorBuildingActivated:(GMSIndoorBuilding *)indoorBuilding { + NSDictionary *arg = nil; + if (indoorBuilding != nil) { + arg = @{@"underground": @(indoorBuilding.underground), + @"defaultLevelIndex": @(indoorBuilding.defaultLevelIndex), + @"levels": [self mappingIndoorLevels:indoorBuilding.levels]}; + } + [self.channel invokeMethod:@"indoorBuildingActivated" arguments:arg]; +} + +- (void)indoorLevelActivated:(GMSIndoorLevel *)indoorLevel { + NSDictionary *arg = nil; + if (indoorLevel != nil) { + arg = [self mappingIndoorLevel:indoorLevel]; + } + [self.channel invokeMethod:@"indoorLevelActivated" arguments:arg]; +} + +- (NSArray *)mappingIndoorLevels:(NSArray *)levels { + if (levels == nil) { + return nil; + } + NSMutableArray* array = [NSMutableArray array]; + for (GMSIndoorLevel *level in levels) { + [array addObject: [self mappingIndoorLevel:level]]; + } + return array; +} + +- (NSDictionary *)mappingIndoorLevel:(GMSIndoorLevel *)level { + return @{@"name": [NSString stringWithString:level.name], + @"shortName": [NSString stringWithString:level.shortName]}; +} + - (int)getMapViewType:(NSString *)mapViewTypeName { int mapType = kGMSTypeNormal; if ([@"none" isEqualToString:mapViewTypeName]) { diff --git a/lib/map_view.dart b/lib/map_view.dart index 5bf8e35..72d4216 100644 --- a/lib/map_view.dart +++ b/lib/map_view.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:flutter/services.dart'; import 'package:map_view/camera_position.dart'; +import 'package:map_view/indoor_building.dart'; import 'package:map_view/location.dart'; import 'package:map_view/map_options.dart'; import 'package:map_view/marker.dart'; @@ -45,6 +46,10 @@ class MapView { new StreamController.broadcast(); StreamController _infoWindowStreamController = new StreamController.broadcast(); + StreamController _indoorBuildingActivatedStreamController = + new StreamController.broadcast(); + StreamController _indoorLevelActivatedStreamController = + new StreamController.broadcast(); Map _annotations = {}; Map _polylines = {}; @@ -262,6 +267,12 @@ class MapView { Stream get onInfoWindowTapped => _infoWindowStreamController.stream; + Stream get onIndoorBuildingActivated => + _indoorBuildingActivatedStreamController.stream; + + Stream get onIndoorLevelActivated => + _indoorLevelActivatedStreamController.stream; + Future _handleMethod(MethodCall call) async { switch (call.method) { case "onMapReady": @@ -343,6 +354,28 @@ class MapView { case "onToolbarAction": _toolbarActionStreamController.add(call.arguments); break; + case "indoorBuildingActivated": + if (call.arguments == null) { + _indoorBuildingActivatedStreamController.add(null); + } else { + List levels = []; + for (var value in call.arguments["levels"]) { + levels.add(IndoorLevel(value["name"], value["shortName"])); + } + _indoorBuildingActivatedStreamController.add(new IndoorBuilding( + call.arguments["underground"], + call.arguments["defaultLevelIndex"], + levels)); + } + break; + case "indoorLevelActivated": + if (call.arguments == null) { + _indoorLevelActivatedStreamController.add(null); + } else { + _indoorLevelActivatedStreamController.add( + IndoorLevel(call.arguments["name"], call.arguments["shortName"])); + } + break; } return new Future.value(""); }