diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md b/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md index 8755bc89eb9..9ee2a86e4f1 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md +++ b/packages/google_maps_flutter/google_maps_flutter_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.10.0 + +* Converts Obj-C->Dart calls to Pigeon. + ## 2.9.0 * Converts additional platform calls to Pigeon. diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/FLTGoogleMapJSONConversionsConversionTests.m b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/FLTGoogleMapJSONConversionsConversionTests.m index b5a992d1efc..79da52d8198 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/FLTGoogleMapJSONConversionsConversionTests.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/FLTGoogleMapJSONConversionsConversionTests.m @@ -98,19 +98,19 @@ - (void)testHolesFromPointsArray { XCTAssertEqual(holes[1][1].coordinate.longitude, 8); } -- (void)testDictionaryFromPosition { - id mockPosition = OCMClassMock([GMSCameraPosition class]); - NSValue *locationValue = [NSValue valueWithMKCoordinate:CLLocationCoordinate2DMake(1, 2)]; - [(GMSCameraPosition *)[[mockPosition stub] andReturnValue:locationValue] target]; - [[[mockPosition stub] andReturnValue:@(2.0)] zoom]; - [[[mockPosition stub] andReturnValue:@(3.0)] bearing]; - [[[mockPosition stub] andReturnValue:@(75.0)] viewingAngle]; - NSDictionary *dictionary = [FLTGoogleMapJSONConversions dictionaryFromPosition:mockPosition]; - NSArray *targetArray = @[ @1, @2 ]; - XCTAssertEqualObjects(dictionary[@"target"], targetArray); - XCTAssertEqualObjects(dictionary[@"zoom"], @2.0); - XCTAssertEqualObjects(dictionary[@"bearing"], @3.0); - XCTAssertEqualObjects(dictionary[@"tilt"], @75.0); +- (void)testGetPigeonCameraPositionForPosition { + GMSCameraPosition *position = + [[GMSCameraPosition alloc] initWithTarget:CLLocationCoordinate2DMake(1, 2) + zoom:2.0 + bearing:3.0 + viewingAngle:75.0]; + FGMPlatformCameraPosition *pigeonPosition = FGMGetPigeonCameraPositionForPosition(position); + XCTAssertEqualWithAccuracy(pigeonPosition.target.latitude, position.target.latitude, DBL_EPSILON); + XCTAssertEqualWithAccuracy(pigeonPosition.target.longitude, position.target.longitude, + DBL_EPSILON); + XCTAssertEqualWithAccuracy(pigeonPosition.zoom, position.zoom, DBL_EPSILON); + XCTAssertEqualWithAccuracy(pigeonPosition.bearing, position.bearing, DBL_EPSILON); + XCTAssertEqualWithAccuracy(pigeonPosition.tilt, position.viewingAngle, DBL_EPSILON); } - (void)testPigeonPointForGCPoint { diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/FLTTileProviderControllerTests.m b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/FLTTileProviderControllerTests.m index d42174a906f..f4b6ba8d195 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/FLTTileProviderControllerTests.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/FLTTileProviderControllerTests.m @@ -8,18 +8,24 @@ #import +#import "messages.g.h" + @interface FLTTileProviderControllerTests : XCTestCase @end @implementation FLTTileProviderControllerTests - (void)testCallChannelOnPlatformThread { - id channel = OCMClassMock(FlutterMethodChannel.class); - FLTTileProviderController *controller = [[FLTTileProviderController alloc] init:channel - withTileOverlayIdentifier:@"foo"]; + id handler = OCMClassMock([FGMMapsCallbackApi class]); + FLTTileProviderController *controller = + [[FLTTileProviderController alloc] initWithTileOverlayIdentifier:@"foo" + callbackHandler:handler]; XCTAssertNotNil(controller); XCTestExpectation *expectation = [self expectationWithDescription:@"invokeMethod"]; - OCMStub([channel invokeMethod:[OCMArg any] arguments:[OCMArg any] result:[OCMArg any]]) + OCMStub([handler tileWithOverlayIdentifier:[OCMArg any] + location:[OCMArg any] + zoom:0 + completion:[OCMArg any]]) .andDo(^(NSInvocation *invocation) { XCTAssertTrue([[NSThread currentThread] isMainThread]); [expectation fulfill]; diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/GoogleMapsPolylinesControllerTests.m b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/GoogleMapsPolylinesControllerTests.m index 84714f17c7f..0ce0f53e00c 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/GoogleMapsPolylinesControllerTests.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/example/ios14/ios/RunnerTests/GoogleMapsPolylinesControllerTests.m @@ -41,9 +41,9 @@ - (FLTGoogleMapPolylineController *)polylineControllerWithMockedMap { NSString *identifier = polyline[@"polylineId"]; FLTGoogleMapPolylineController *polylineControllerWithMockedMap = - [[FLTGoogleMapPolylineController alloc] initPolylineWithPath:path - identifier:identifier - mapView:mapView]; + [[FLTGoogleMapPolylineController alloc] initWithPath:path + identifier:identifier + mapView:mapView]; return polylineControllerWithMockedMap; } diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.h index f6fd13cc16d..5d45ddd7be7 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.h @@ -28,6 +28,10 @@ extern FGMPlatformLatLng *FGMGetPigeonLatLngForCoordinate(CLLocationCoordinate2D extern FGMPlatformLatLngBounds *FGMGetPigeonLatLngBoundsForCoordinateBounds( GMSCoordinateBounds *bounds); +/// Converts a GMSCameraPosition to its Pigeon representation. +extern FGMPlatformCameraPosition *FGMGetPigeonCameraPositionForPosition( + GMSCameraPosition *position); + @interface FLTGoogleMapJSONConversions : NSObject + (CLLocationCoordinate2D)locationFromLatLong:(NSArray *)latlong; @@ -36,8 +40,6 @@ extern FGMPlatformLatLngBounds *FGMGetPigeonLatLngBoundsForCoordinateBounds( + (UIColor *)colorFromRGBA:(NSNumber *)data; + (NSArray *)pointsFromLatLongs:(NSArray *)data; + (NSArray *> *)holesFromPointsArray:(NSArray *)data; -+ (nullable NSDictionary *)dictionaryFromPosition: - (nullable GMSCameraPosition *)position; + (nullable GMSCameraPosition *)cameraPostionFromDictionary:(nullable NSDictionary *)channelValue; + (GMSCoordinateBounds *)coordinateBoundsFromLatLongs:(NSArray *)latlongs; + (GMSMapViewType)mapViewTypeFromTypeValue:(NSNumber *)value; diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.m index a7adf833a8c..56db3fbee0c 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapJSONConversions.m @@ -32,6 +32,13 @@ CLLocationCoordinate2D FGMGetCoordinateForPigeonLatLng(FGMPlatformLatLng *latLng southwest:FGMGetPigeonLatLngForCoordinate(bounds.southWest)]; } +FGMPlatformCameraPosition *FGMGetPigeonCameraPositionForPosition(GMSCameraPosition *position) { + return [FGMPlatformCameraPosition makeWithBearing:position.bearing + target:FGMGetPigeonLatLngForCoordinate(position.target) + tilt:position.viewingAngle + zoom:position.zoom]; +} + @implementation FLTGoogleMapJSONConversions + (CLLocationCoordinate2D)locationFromLatLong:(NSArray *)latlong { @@ -77,18 +84,6 @@ + (UIColor *)colorFromRGBA:(NSNumber *)numberColor { return holes; } -+ (nullable NSDictionary *)dictionaryFromPosition:(GMSCameraPosition *)position { - if (!position) { - return nil; - } - return @{ - @"target" : [FLTGoogleMapJSONConversions arrayFromLocation:[position target]], - @"zoom" : @([position zoom]), - @"bearing" : @([position bearing]), - @"tilt" : @([position viewingAngle]), - }; -} - + (nullable GMSCameraPosition *)cameraPostionFromDictionary:(nullable NSDictionary *)data { if (!data) { return nil; diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController.h index edc1f9197e4..e375f7df6e1 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController.h @@ -22,14 +22,14 @@ NS_ASSUME_NONNULL_BEGIN @interface FLTTileProviderController : GMSTileLayer @property(copy, nonatomic, readonly) NSString *tileOverlayIdentifier; -- (instancetype)init:(FlutterMethodChannel *)methodChannel - withTileOverlayIdentifier:(NSString *)identifier; +- (instancetype)initWithTileOverlayIdentifier:(NSString *)identifier + callbackHandler:(FGMMapsCallbackApi *)callbackHandler; @end @interface FLTTileOverlaysController : NSObject -- (instancetype)init:(FlutterMethodChannel *)methodChannel - mapView:(GMSMapView *)mapView - registrar:(NSObject *)registrar; +- (instancetype)initWithMapView:(GMSMapView *)mapView + callbackHandler:(FGMMapsCallbackApi *)callbackHandler + registrar:(NSObject *)registrar; - (void)addJSONTileOverlays:(NSArray *> *)tileOverlaysToAdd; - (void)addTileOverlays:(NSArray *)tileOverlaysToAdd; - (void)changeTileOverlays:(NSArray *)tileOverlaysToChange; diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController.m index 44dd42c4768..10523124980 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/FLTGoogleMapTileOverlayController.m @@ -89,17 +89,17 @@ - (void)interpretTileOverlayOptions:(NSDictionary *)data { @interface FLTTileProviderController () -@property(strong, nonatomic) FlutterMethodChannel *methodChannel; +@property(strong, nonatomic) FGMMapsCallbackApi *callbackHandler; @end @implementation FLTTileProviderController -- (instancetype)init:(FlutterMethodChannel *)methodChannel - withTileOverlayIdentifier:(NSString *)identifier { +- (instancetype)initWithTileOverlayIdentifier:(NSString *)identifier + callbackHandler:(FGMMapsCallbackApi *)callbackHandler { self = [super init]; if (self) { - _methodChannel = methodChannel; + _callbackHandler = callbackHandler; _tileOverlayIdentifier = identifier; } return self; @@ -135,35 +135,23 @@ - (void)requestTileForX:(NSUInteger)x zoom:(NSUInteger)zoom receiver:(id)receiver { dispatch_async(dispatch_get_main_queue(), ^{ - [self.methodChannel invokeMethod:@"tileOverlay#getTile" - arguments:@{ - @"tileOverlayId" : self.tileOverlayIdentifier, - @"x" : @(x), - @"y" : @(y), - @"zoom" : @(zoom) - } - result:^(id _Nullable result) { - UIImage *tileImage; - if ([result isKindOfClass:[NSDictionary class]]) { - FlutterStandardTypedData *typedData = (FlutterStandardTypedData *)result[@"data"]; - if (typedData == nil) { - tileImage = kGMSTileLayerNoTile; - } else { - tileImage = [self handleResultTile:[UIImage imageWithData:typedData.data]]; - } - } else { - if ([result isKindOfClass:[FlutterError class]]) { - FlutterError *error = (FlutterError *)result; - NSLog(@"Can't get tile: errorCode = %@, errorMessage = %@, details = %@", - [error code], [error message], [error details]); - } - if ([result isKindOfClass:[FlutterMethodNotImplemented class]]) { - NSLog(@"Can't get tile: notImplemented"); - } - tileImage = kGMSTileLayerNoTile; - } - [receiver receiveTileWithX:x y:y zoom:zoom image:tileImage]; - }]; + [self.callbackHandler + tileWithOverlayIdentifier:self.tileOverlayIdentifier + location:[FGMPlatformPoint makeWithX:x y:y] + zoom:zoom + completion:^(FGMPlatformTile *_Nullable tile, + FlutterError *_Nullable error) { + FlutterStandardTypedData *typedData = tile.data; + UIImage *tileImage = + typedData + ? [self handleResultTile:[UIImage imageWithData:typedData.data]] + : kGMSTileLayerNoTile; + if (error) { + NSLog(@"Can't get tile: errorCode = %@, errorMessage = %@, details = %@", + [error code], [error message], [error details]); + } + [receiver receiveTileWithX:x y:y zoom:zoom image:tileImage]; + }]; }); } @@ -173,19 +161,19 @@ @interface FLTTileOverlaysController () @property(strong, nonatomic) NSMutableDictionary *tileOverlayIdentifierToController; -@property(strong, nonatomic) FlutterMethodChannel *methodChannel; +@property(strong, nonatomic) FGMMapsCallbackApi *callbackHandler; @property(weak, nonatomic) GMSMapView *mapView; @end @implementation FLTTileOverlaysController -- (instancetype)init:(FlutterMethodChannel *)methodChannel - mapView:(GMSMapView *)mapView - registrar:(NSObject *)registrar { +- (instancetype)initWithMapView:(GMSMapView *)mapView + callbackHandler:(FGMMapsCallbackApi *)callbackHandler + registrar:(NSObject *)registrar { self = [super init]; if (self) { - _methodChannel = methodChannel; + _callbackHandler = callbackHandler; _mapView = mapView; _tileOverlayIdentifierToController = [[NSMutableDictionary alloc] init]; } @@ -196,8 +184,8 @@ - (void)addJSONTileOverlays:(NSArray *> *)tileOverl for (NSDictionary *tileOverlay in tileOverlaysToAdd) { NSString *identifier = [FLTTileOverlaysController identifierForTileOverlay:tileOverlay]; FLTTileProviderController *tileProvider = - [[FLTTileProviderController alloc] init:self.methodChannel - withTileOverlayIdentifier:identifier]; + [[FLTTileProviderController alloc] initWithTileOverlayIdentifier:identifier + callbackHandler:self.callbackHandler]; FLTGoogleMapTileOverlayController *controller = [[FLTGoogleMapTileOverlayController alloc] initWithTileLayer:tileProvider mapView:self.mapView @@ -210,8 +198,8 @@ - (void)addTileOverlays:(NSArray *)tileOverlaysToAdd { for (FGMPlatformTileOverlay *tileOverlay in tileOverlaysToAdd) { NSString *identifier = [FLTTileOverlaysController identifierForTileOverlay:tileOverlay.json]; FLTTileProviderController *tileProvider = - [[FLTTileProviderController alloc] init:self.methodChannel - withTileOverlayIdentifier:identifier]; + [[FLTTileProviderController alloc] initWithTileOverlayIdentifier:identifier + callbackHandler:self.callbackHandler]; FLTGoogleMapTileOverlayController *controller = [[FLTGoogleMapTileOverlayController alloc] initWithTileLayer:tileProvider mapView:self.mapView diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapCircleController.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapCircleController.h index d9b798d5d47..a53199f077d 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapCircleController.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapCircleController.h @@ -18,9 +18,9 @@ @end @interface FLTCirclesController : NSObject -- (instancetype)init:(FlutterMethodChannel *)methodChannel - mapView:(GMSMapView *)mapView - registrar:(NSObject *)registrar; +- (instancetype)initWithMapView:(GMSMapView *)mapView + callbackHandler:(FGMMapsCallbackApi *)callbackHandler + registrar:(NSObject *)registrar; - (void)addJSONCircles:(NSArray *> *)circlesToAdd; - (void)addCircles:(NSArray *)circlesToAdd; - (void)changeCircles:(NSArray *)circlesToChange; diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapCircleController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapCircleController.m index 1e42d91ac7d..ae4c9c2f9ba 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapCircleController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapCircleController.m @@ -105,7 +105,7 @@ - (void)interpretCircleOptions:(NSDictionary *)data { @interface FLTCirclesController () -@property(strong, nonatomic) FlutterMethodChannel *methodChannel; +@property(strong, nonatomic) FGMMapsCallbackApi *callbackHandler; @property(weak, nonatomic) GMSMapView *mapView; @property(strong, nonatomic) NSMutableDictionary *circleIdToController; @@ -113,12 +113,12 @@ @interface FLTCirclesController () @implementation FLTCirclesController -- (instancetype)init:(FlutterMethodChannel *)methodChannel - mapView:(GMSMapView *)mapView - registrar:(NSObject *)registrar { +- (instancetype)initWithMapView:(GMSMapView *)mapView + callbackHandler:(FGMMapsCallbackApi *)callbackHandler + registrar:(NSObject *)registrar { self = [super init]; if (self) { - _methodChannel = methodChannel; + _callbackHandler = callbackHandler; _mapView = mapView; _circleIdToController = [NSMutableDictionary dictionaryWithCapacity:1]; } @@ -189,7 +189,9 @@ - (void)didTapCircleWithIdentifier:(NSString *)identifier { if (!controller) { return; } - [self.methodChannel invokeMethod:@"circle#onTap" arguments:@{@"circleId" : identifier}]; + [self.callbackHandler didTapCircleWithIdentifier:identifier + completion:^(FlutterError *_Nullable _){ + }]; } + (CLLocationCoordinate2D)getPosition:(NSDictionary *)circle { diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.m index 453cd8c946b..8a69f712ba3 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapController.m @@ -112,7 +112,7 @@ @interface FGMMapInspector () @interface FLTGoogleMapController () @property(nonatomic, strong) GMSMapView *mapView; -@property(nonatomic, strong) FlutterMethodChannel *channel; +@property(nonatomic, strong) FGMMapsCallbackApi *dartCallbackHandler; @property(nonatomic, assign) BOOL trackCameraPosition; @property(nonatomic, weak) NSObject *registrar; @property(nonatomic, strong) FLTMarkersController *markersController; @@ -165,28 +165,28 @@ - (instancetype)initWithMapView:(GMSMapView *_Nonnull)mapView // TODO(cyanglaz): avoid sending message to self in the middle of the init method. // https://github.com/flutter/flutter/issues/104121 [self interpretMapOptions:args[@"options"]]; - NSString *channelName = - [NSString stringWithFormat:@"plugins.flutter.dev/google_maps_ios_%lld", viewId]; - _channel = [FlutterMethodChannel methodChannelWithName:channelName - binaryMessenger:registrar.messenger]; + NSString *pigeonSuffix = [NSString stringWithFormat:@"%lld", viewId]; + _dartCallbackHandler = [[FGMMapsCallbackApi alloc] initWithBinaryMessenger:registrar.messenger + messageChannelSuffix:pigeonSuffix]; _mapView.delegate = self; _mapView.paddingAdjustmentBehavior = kGMSMapViewPaddingAdjustmentBehaviorNever; _registrar = registrar; - _markersController = [[FLTMarkersController alloc] initWithMethodChannel:_channel - mapView:_mapView - registrar:registrar]; - _polygonsController = [[FLTPolygonsController alloc] init:_channel - mapView:_mapView - registrar:registrar]; - _polylinesController = [[FLTPolylinesController alloc] init:_channel - mapView:_mapView - registrar:registrar]; - _circlesController = [[FLTCirclesController alloc] init:_channel - mapView:_mapView - registrar:registrar]; - _tileOverlaysController = [[FLTTileOverlaysController alloc] init:_channel - mapView:_mapView - registrar:registrar]; + _markersController = [[FLTMarkersController alloc] initWithMapView:_mapView + callbackHandler:_dartCallbackHandler + registrar:registrar]; + _polygonsController = [[FLTPolygonsController alloc] initWithMapView:_mapView + callbackHandler:_dartCallbackHandler + registrar:registrar]; + _polylinesController = [[FLTPolylinesController alloc] initWithMapView:_mapView + callbackHandler:_dartCallbackHandler + registrar:registrar]; + _circlesController = [[FLTCirclesController alloc] initWithMapView:_mapView + callbackHandler:_dartCallbackHandler + registrar:registrar]; + _tileOverlaysController = + [[FLTTileOverlaysController alloc] initWithMapView:_mapView + callbackHandler:_dartCallbackHandler + registrar:registrar]; id markersToAdd = args[@"markersToAdd"]; if ([markersToAdd isKindOfClass:[NSArray class]]) { [_markersController addJSONMarkers:markersToAdd]; @@ -210,15 +210,14 @@ - (instancetype)initWithMapView:(GMSMapView *_Nonnull)mapView [_mapView addObserver:self forKeyPath:@"frame" options:0 context:nil]; - NSString *suffix = [NSString stringWithFormat:@"%lld", viewId]; _callHandler = [[FGMMapCallHandler alloc] initWithMapController:self messenger:registrar.messenger - pigeonSuffix:suffix]; - SetUpFGMMapsApiWithSuffix(registrar.messenger, _callHandler, suffix); + pigeonSuffix:pigeonSuffix]; + SetUpFGMMapsApiWithSuffix(registrar.messenger, _callHandler, pigeonSuffix); _inspector = [[FGMMapInspector alloc] initWithMapController:self messenger:registrar.messenger - pigeonSuffix:suffix]; - SetUpFGMMapsInspectorApiWithSuffix(registrar.messenger, _inspector, suffix); + pigeonSuffix:pigeonSuffix]; + SetUpFGMMapsInspectorApiWithSuffix(registrar.messenger, _inspector, pigeonSuffix); } return self; } @@ -359,20 +358,22 @@ - (NSString *)setMapStyle:(NSString *)mapStyle { #pragma mark - GMSMapViewDelegate methods - (void)mapView:(GMSMapView *)mapView willMove:(BOOL)gesture { - [self.channel invokeMethod:@"camera#onMoveStarted" arguments:@{@"isGesture" : @(gesture)}]; + [self.dartCallbackHandler didStartCameraMoveWithCompletion:^(FlutterError *_Nullable _){ + }]; } - (void)mapView:(GMSMapView *)mapView didChangeCameraPosition:(GMSCameraPosition *)position { if (self.trackCameraPosition) { - [self.channel invokeMethod:@"camera#onMove" - arguments:@{ - @"position" : [FLTGoogleMapJSONConversions dictionaryFromPosition:position] + [self.dartCallbackHandler + didMoveCameraToPosition:FGMGetPigeonCameraPositionForPosition(position) + completion:^(FlutterError *_Nullable _){ }]; } } - (void)mapView:(GMSMapView *)mapView idleAtCameraPosition:(GMSCameraPosition *)position { - [self.channel invokeMethod:@"camera#onIdle" arguments:@{}]; + [self.dartCallbackHandler didIdleCameraWithCompletion:^(FlutterError *_Nullable _){ + }]; } - (BOOL)mapView:(GMSMapView *)mapView didTapMarker:(GMSMarker *)marker { @@ -411,15 +412,15 @@ - (void)mapView:(GMSMapView *)mapView didTapOverlay:(GMSOverlay *)overlay { } - (void)mapView:(GMSMapView *)mapView didTapAtCoordinate:(CLLocationCoordinate2D)coordinate { - [self.channel - invokeMethod:@"map#onTap" - arguments:@{@"position" : [FLTGoogleMapJSONConversions arrayFromLocation:coordinate]}]; + [self.dartCallbackHandler didTapAtPosition:FGMGetPigeonLatLngForCoordinate(coordinate) + completion:^(FlutterError *_Nullable _){ + }]; } - (void)mapView:(GMSMapView *)mapView didLongPressAtCoordinate:(CLLocationCoordinate2D)coordinate { - [self.channel - invokeMethod:@"map#onLongPress" - arguments:@{@"position" : [FLTGoogleMapJSONConversions arrayFromLocation:coordinate]}]; + [self.dartCallbackHandler didLongPressAtPosition:FGMGetPigeonLatLngForCoordinate(coordinate) + completion:^(FlutterError *_Nullable _){ + }]; } - (void)interpretMapOptions:(NSDictionary *)data { diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.h index 67fe1245a93..1b87ab1bb94 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.h @@ -13,9 +13,9 @@ NS_ASSUME_NONNULL_BEGIN // Defines marker controllable by Flutter. @interface FLTGoogleMapMarkerController : NSObject @property(assign, nonatomic, readonly) BOOL consumeTapEvents; -- (instancetype)initMarkerWithPosition:(CLLocationCoordinate2D)position - identifier:(NSString *)identifier - mapView:(GMSMapView *)mapView; +- (instancetype)initWithPosition:(CLLocationCoordinate2D)position + identifier:(NSString *)identifier + mapView:(GMSMapView *)mapView; - (void)showInfoWindow; - (void)hideInfoWindow; - (BOOL)isInfoWindowShown; @@ -23,9 +23,9 @@ NS_ASSUME_NONNULL_BEGIN @end @interface FLTMarkersController : NSObject -- (instancetype)initWithMethodChannel:(FlutterMethodChannel *)methodChannel - mapView:(GMSMapView *)mapView - registrar:(NSObject *)registrar; +- (instancetype)initWithMapView:(GMSMapView *)mapView + callbackHandler:(FGMMapsCallbackApi *)callbackHandler + registrar:(NSObject *)registrar; - (void)addJSONMarkers:(NSArray *> *)markersToAdd; - (void)addMarkers:(NSArray *)markersToAdd; - (void)changeMarkers:(NSArray *)markersToChange; diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.m index 02e6a09e1c6..46ac7b24ee5 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapMarkerController.m @@ -15,9 +15,9 @@ @interface FLTGoogleMapMarkerController () @implementation FLTGoogleMapMarkerController -- (instancetype)initMarkerWithPosition:(CLLocationCoordinate2D)position - identifier:(NSString *)identifier - mapView:(GMSMapView *)mapView { +- (instancetype)initWithPosition:(CLLocationCoordinate2D)position + identifier:(NSString *)identifier + mapView:(GMSMapView *)mapView { self = [super init]; if (self) { _marker = [GMSMarker markerWithPosition:position]; @@ -431,7 +431,7 @@ + (BOOL)isScalableWithScaleFactorFromSize:(CGSize)originalSize toSize:(CGSize)ta @interface FLTMarkersController () @property(strong, nonatomic) NSMutableDictionary *markerIdentifierToController; -@property(strong, nonatomic) FlutterMethodChannel *methodChannel; +@property(strong, nonatomic) FGMMapsCallbackApi *callbackHandler; @property(weak, nonatomic) NSObject *registrar; @property(weak, nonatomic) GMSMapView *mapView; @@ -439,12 +439,12 @@ @interface FLTMarkersController () @implementation FLTMarkersController -- (instancetype)initWithMethodChannel:(FlutterMethodChannel *)methodChannel - mapView:(GMSMapView *)mapView - registrar:(NSObject *)registrar { +- (instancetype)initWithMapView:(GMSMapView *)mapView + callbackHandler:(FGMMapsCallbackApi *)callbackHandler + registrar:(NSObject *)registrar { self = [super init]; if (self) { - _methodChannel = methodChannel; + _callbackHandler = callbackHandler; _mapView = mapView; _markerIdentifierToController = [[NSMutableDictionary alloc] init]; _registrar = registrar; @@ -457,9 +457,9 @@ - (void)addJSONMarkers:(NSArray *> *)markersToAdd { CLLocationCoordinate2D position = [FLTMarkersController getPosition:marker]; NSString *identifier = marker[@"markerId"]; FLTGoogleMapMarkerController *controller = - [[FLTGoogleMapMarkerController alloc] initMarkerWithPosition:position - identifier:identifier - mapView:self.mapView]; + [[FLTGoogleMapMarkerController alloc] initWithPosition:position + identifier:identifier + mapView:self.mapView]; [controller interpretMarkerOptions:marker registrar:self.registrar screenScale:[self getScreenScale]]; @@ -472,9 +472,9 @@ - (void)addMarkers:(NSArray *)markersToAdd { CLLocationCoordinate2D position = [FLTMarkersController getPosition:marker.json]; NSString *identifier = marker.json[@"markerId"]; FLTGoogleMapMarkerController *controller = - [[FLTGoogleMapMarkerController alloc] initMarkerWithPosition:position - identifier:identifier - mapView:self.mapView]; + [[FLTGoogleMapMarkerController alloc] initWithPosition:position + identifier:identifier + mapView:self.mapView]; [controller interpretMarkerOptions:marker.json registrar:self.registrar screenScale:[self getScreenScale]]; @@ -511,7 +511,9 @@ - (BOOL)didTapMarkerWithIdentifier:(NSString *)identifier { if (!controller) { return NO; } - [self.methodChannel invokeMethod:@"marker#onTap" arguments:@{@"markerId" : identifier}]; + [self.callbackHandler didTapMarkerWithIdentifier:identifier + completion:^(FlutterError *_Nullable _){ + }]; return controller.consumeTapEvents; } @@ -524,11 +526,11 @@ - (void)didStartDraggingMarkerWithIdentifier:(NSString *)identifier if (!controller) { return; } - [self.methodChannel invokeMethod:@"marker#onDragStart" - arguments:@{ - @"markerId" : identifier, - @"position" : [FLTGoogleMapJSONConversions arrayFromLocation:location] - }]; + [self.callbackHandler + didStartDragForMarkerWithIdentifier:identifier + atPosition:FGMGetPigeonLatLngForCoordinate(location) + completion:^(FlutterError *_Nullable _){ + }]; } - (void)didDragMarkerWithIdentifier:(NSString *)identifier @@ -540,11 +542,10 @@ - (void)didDragMarkerWithIdentifier:(NSString *)identifier if (!controller) { return; } - [self.methodChannel invokeMethod:@"marker#onDrag" - arguments:@{ - @"markerId" : identifier, - @"position" : [FLTGoogleMapJSONConversions arrayFromLocation:location] - }]; + [self.callbackHandler didDragMarkerWithIdentifier:identifier + atPosition:FGMGetPigeonLatLngForCoordinate(location) + completion:^(FlutterError *_Nullable _){ + }]; } - (void)didEndDraggingMarkerWithIdentifier:(NSString *)identifier @@ -553,16 +554,17 @@ - (void)didEndDraggingMarkerWithIdentifier:(NSString *)identifier if (!controller) { return; } - [self.methodChannel invokeMethod:@"marker#onDragEnd" - arguments:@{ - @"markerId" : identifier, - @"position" : [FLTGoogleMapJSONConversions arrayFromLocation:location] - }]; + [self.callbackHandler didEndDragForMarkerWithIdentifier:identifier + atPosition:FGMGetPigeonLatLngForCoordinate(location) + completion:^(FlutterError *_Nullable _){ + }]; } - (void)didTapInfoWindowOfMarkerWithIdentifier:(NSString *)identifier { if (identifier && self.markerIdentifierToController[identifier]) { - [self.methodChannel invokeMethod:@"infoWindow#onTap" arguments:@{@"markerId" : identifier}]; + [self.callbackHandler didTapInfoWindowOfMarkerWithIdentifier:identifier + completion:^(FlutterError *_Nullable _){ + }]; } } diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolygonController.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolygonController.h index 842a6da939c..4d1b0fd5f00 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolygonController.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolygonController.h @@ -9,16 +9,16 @@ // Defines polygon controllable by Flutter. @interface FLTGoogleMapPolygonController : NSObject -- (instancetype)initPolygonWithPath:(GMSMutablePath *)path - identifier:(NSString *)identifier - mapView:(GMSMapView *)mapView; +- (instancetype)initWithPath:(GMSMutablePath *)path + identifier:(NSString *)identifier + mapView:(GMSMapView *)mapView; - (void)removePolygon; @end @interface FLTPolygonsController : NSObject -- (instancetype)init:(FlutterMethodChannel *)methodChannel - mapView:(GMSMapView *)mapView - registrar:(NSObject *)registrar; +- (instancetype)initWithMapView:(GMSMapView *)mapView + callbackHandler:(FGMMapsCallbackApi *)callbackHandler + registrar:(NSObject *)registrar; - (void)addJSONPolygons:(NSArray *> *)polygonsToAdd; - (void)addPolygons:(NSArray *)polygonsToAdd; - (void)changePolygons:(NSArray *)polygonsToChange; diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolygonController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolygonController.m index b48e67ea8a7..19380aad6d0 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolygonController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolygonController.m @@ -14,9 +14,9 @@ @interface FLTGoogleMapPolygonController () @implementation FLTGoogleMapPolygonController -- (instancetype)initPolygonWithPath:(GMSMutablePath *)path - identifier:(NSString *)identifier - mapView:(GMSMapView *)mapView { +- (instancetype)initWithPath:(GMSMutablePath *)path + identifier:(NSString *)identifier + mapView:(GMSMapView *)mapView { self = [super init]; if (self) { _polygon = [GMSPolygon polygonWithPath:path]; @@ -119,7 +119,7 @@ - (void)interpretPolygonOptions:(NSDictionary *)data @interface FLTPolygonsController () @property(strong, nonatomic) NSMutableDictionary *polygonIdentifierToController; -@property(strong, nonatomic) FlutterMethodChannel *methodChannel; +@property(strong, nonatomic) FGMMapsCallbackApi *callbackHandler; @property(weak, nonatomic) NSObject *registrar; @property(weak, nonatomic) GMSMapView *mapView; @@ -127,12 +127,12 @@ @interface FLTPolygonsController () @implementation FLTPolygonsController -- (instancetype)init:(FlutterMethodChannel *)methodChannel - mapView:(GMSMapView *)mapView - registrar:(NSObject *)registrar { +- (instancetype)initWithMapView:(GMSMapView *)mapView + callbackHandler:(FGMMapsCallbackApi *)callbackHandler + registrar:(NSObject *)registrar { self = [super init]; if (self) { - _methodChannel = methodChannel; + _callbackHandler = callbackHandler; _mapView = mapView; _polygonIdentifierToController = [NSMutableDictionary dictionaryWithCapacity:1]; _registrar = registrar; @@ -145,9 +145,9 @@ - (void)addJSONPolygons:(NSArray *> *)polygonsToAdd GMSMutablePath *path = [FLTPolygonsController getPath:polygon]; NSString *identifier = polygon[@"polygonId"]; FLTGoogleMapPolygonController *controller = - [[FLTGoogleMapPolygonController alloc] initPolygonWithPath:path - identifier:identifier - mapView:self.mapView]; + [[FLTGoogleMapPolygonController alloc] initWithPath:path + identifier:identifier + mapView:self.mapView]; [controller interpretPolygonOptions:polygon registrar:self.registrar]; self.polygonIdentifierToController[identifier] = controller; } @@ -158,9 +158,9 @@ - (void)addPolygons:(NSArray *)polygonsToAdd { GMSMutablePath *path = [FLTPolygonsController getPath:polygon.json]; NSString *identifier = polygon.json[@"polygonId"]; FLTGoogleMapPolygonController *controller = - [[FLTGoogleMapPolygonController alloc] initPolygonWithPath:path - identifier:identifier - mapView:self.mapView]; + [[FLTGoogleMapPolygonController alloc] initWithPath:path + identifier:identifier + mapView:self.mapView]; [controller interpretPolygonOptions:polygon.json registrar:self.registrar]; self.polygonIdentifierToController[identifier] = controller; } @@ -193,7 +193,9 @@ - (void)didTapPolygonWithIdentifier:(NSString *)identifier { if (!controller) { return; } - [self.methodChannel invokeMethod:@"polygon#onTap" arguments:@{@"polygonId" : identifier}]; + [self.callbackHandler didTapPolygonWithIdentifier:identifier + completion:^(FlutterError *_Nullable _){ + }]; } - (bool)hasPolygonWithIdentifier:(NSString *)identifier { diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolylineController.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolylineController.h index 06c47800084..8557be961e4 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolylineController.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolylineController.h @@ -9,9 +9,9 @@ // Defines polyline controllable by Flutter. @interface FLTGoogleMapPolylineController : NSObject -- (instancetype)initPolylineWithPath:(GMSMutablePath *)path - identifier:(NSString *)identifier - mapView:(GMSMapView *)mapView; +- (instancetype)initWithPath:(GMSMutablePath *)path + identifier:(NSString *)identifier + mapView:(GMSMapView *)mapView; - (void)removePolyline; /// Sets the pattern on polyline controller @@ -22,9 +22,9 @@ @end @interface FLTPolylinesController : NSObject -- (instancetype)init:(FlutterMethodChannel *)methodChannel - mapView:(GMSMapView *)mapView - registrar:(NSObject *)registrar; +- (instancetype)initWithMapView:(GMSMapView *)mapView + callbackHandler:(FGMMapsCallbackApi *)callbackHandler + registrar:(NSObject *)registrar; - (void)addJSONPolylines:(NSArray *> *)polylinesToAdd; - (void)addPolylines:(NSArray *)polylinesToAdd; - (void)changePolylines:(NSArray *)polylinesToChange; diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolylineController.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolylineController.m index 6f8b7b3be71..51cec3baae1 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolylineController.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/GoogleMapPolylineController.m @@ -14,9 +14,9 @@ @interface FLTGoogleMapPolylineController () @implementation FLTGoogleMapPolylineController -- (instancetype)initPolylineWithPath:(GMSMutablePath *)path - identifier:(NSString *)identifier - mapView:(GMSMapView *)mapView { +- (instancetype)initWithPath:(GMSMutablePath *)path + identifier:(NSString *)identifier + mapView:(GMSMapView *)mapView { self = [super init]; if (self) { _polyline = [GMSPolyline polylineWithPath:path]; @@ -114,7 +114,7 @@ - (void)interpretPolylineOptions:(NSDictionary *)data @interface FLTPolylinesController () @property(strong, nonatomic) NSMutableDictionary *polylineIdentifierToController; -@property(strong, nonatomic) FlutterMethodChannel *methodChannel; +@property(strong, nonatomic) FGMMapsCallbackApi *callbackHandler; @property(weak, nonatomic) NSObject *registrar; @property(weak, nonatomic) GMSMapView *mapView; @@ -123,12 +123,12 @@ @interface FLTPolylinesController () @implementation FLTPolylinesController -- (instancetype)init:(FlutterMethodChannel *)methodChannel - mapView:(GMSMapView *)mapView - registrar:(NSObject *)registrar { +- (instancetype)initWithMapView:(GMSMapView *)mapView + callbackHandler:(FGMMapsCallbackApi *)callbackHandler + registrar:(NSObject *)registrar { self = [super init]; if (self) { - _methodChannel = methodChannel; + _callbackHandler = callbackHandler; _mapView = mapView; _polylineIdentifierToController = [NSMutableDictionary dictionaryWithCapacity:1]; _registrar = registrar; @@ -141,9 +141,9 @@ - (void)addJSONPolylines:(NSArray *> *)polylinesToA GMSMutablePath *path = [FLTPolylinesController pathForPolyline:polyline]; NSString *identifier = polyline[@"polylineId"]; FLTGoogleMapPolylineController *controller = - [[FLTGoogleMapPolylineController alloc] initPolylineWithPath:path - identifier:identifier - mapView:self.mapView]; + [[FLTGoogleMapPolylineController alloc] initWithPath:path + identifier:identifier + mapView:self.mapView]; [controller interpretPolylineOptions:polyline registrar:self.registrar]; self.polylineIdentifierToController[identifier] = controller; } @@ -154,9 +154,9 @@ - (void)addPolylines:(NSArray *)polylinesToAdd { GMSMutablePath *path = [FLTPolylinesController pathForPolyline:polyline.json]; NSString *identifier = polyline.json[@"polylineId"]; FLTGoogleMapPolylineController *controller = - [[FLTGoogleMapPolylineController alloc] initPolylineWithPath:path - identifier:identifier - mapView:self.mapView]; + [[FLTGoogleMapPolylineController alloc] initWithPath:path + identifier:identifier + mapView:self.mapView]; [controller interpretPolylineOptions:polyline.json registrar:self.registrar]; self.polylineIdentifierToController[identifier] = controller; } @@ -189,7 +189,9 @@ - (void)didTapPolylineWithIdentifier:(NSString *)identifier { if (!controller) { return; } - [self.methodChannel invokeMethod:@"polyline#onTap" arguments:@{@"polylineId" : identifier}]; + [self.callbackHandler didTapPolylineWithIdentifier:identifier + completion:^(FlutterError *_Nullable _){ + }]; } - (bool)hasPolylineWithIdentifier:(NSString *)identifier { diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/messages.g.h b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/messages.g.h index 3315002fc7d..6dd112c6baa 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/messages.g.h +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/messages.g.h @@ -13,11 +13,13 @@ NS_ASSUME_NONNULL_BEGIN +@class FGMPlatformCameraPosition; @class FGMPlatformCameraUpdate; @class FGMPlatformCircle; @class FGMPlatformMarker; @class FGMPlatformPolygon; @class FGMPlatformPolyline; +@class FGMPlatformTile; @class FGMPlatformTileOverlay; @class FGMPlatformLatLng; @class FGMPlatformLatLngBounds; @@ -26,6 +28,20 @@ NS_ASSUME_NONNULL_BEGIN @class FGMPlatformTileLayer; @class FGMPlatformZoomRange; +/// Pigeon representatation of a CameraPosition. +@interface FGMPlatformCameraPosition : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithBearing:(double)bearing + target:(FGMPlatformLatLng *)target + tilt:(double)tilt + zoom:(double)zoom; +@property(nonatomic, assign) double bearing; +@property(nonatomic, strong) FGMPlatformLatLng *target; +@property(nonatomic, assign) double tilt; +@property(nonatomic, assign) double zoom; +@end + /// Pigeon representation of a CameraUpdate. @interface FGMPlatformCameraUpdate : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. @@ -81,6 +97,18 @@ NS_ASSUME_NONNULL_BEGIN @property(nonatomic, strong) id json; @end +/// Pigeon equivalent of the Tile class. +@interface FGMPlatformTile : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithWidth:(NSInteger)width + height:(NSInteger)height + data:(nullable FlutterStandardTypedData *)data; +@property(nonatomic, assign) NSInteger width; +@property(nonatomic, assign) NSInteger height; +@property(nonatomic, strong, nullable) FlutterStandardTypedData *data; +@end + /// Pigeon equivalent of the TileOverlay class. @interface FGMPlatformTileOverlay : NSObject /// `init` unavailable to enforce nonnull fields, see the `make` class method. @@ -259,6 +287,59 @@ extern void SetUpFGMMapsApiWithSuffix(id binaryMessenger NSObject *_Nullable api, NSString *messageChannelSuffix); +/// Interface for calls from the native SDK to Dart. +@interface FGMMapsCallbackApi : NSObject +- (instancetype)initWithBinaryMessenger:(id)binaryMessenger; +- (instancetype)initWithBinaryMessenger:(id)binaryMessenger + messageChannelSuffix:(nullable NSString *)messageChannelSuffix; +/// Called when the map camera starts moving. +- (void)didStartCameraMoveWithCompletion:(void (^)(FlutterError *_Nullable))completion; +/// Called when the map camera moves. +- (void)didMoveCameraToPosition:(FGMPlatformCameraPosition *)cameraPosition + completion:(void (^)(FlutterError *_Nullable))completion; +/// Called when the map camera stops moving. +- (void)didIdleCameraWithCompletion:(void (^)(FlutterError *_Nullable))completion; +/// Called when the map, not a specifc map object, is tapped. +- (void)didTapAtPosition:(FGMPlatformLatLng *)position + completion:(void (^)(FlutterError *_Nullable))completion; +/// Called when the map, not a specifc map object, is long pressed. +- (void)didLongPressAtPosition:(FGMPlatformLatLng *)position + completion:(void (^)(FlutterError *_Nullable))completion; +/// Called when a marker is tapped. +- (void)didTapMarkerWithIdentifier:(NSString *)markerId + completion:(void (^)(FlutterError *_Nullable))completion; +/// Called when a marker drag starts. +- (void)didStartDragForMarkerWithIdentifier:(NSString *)markerId + atPosition:(FGMPlatformLatLng *)position + completion:(void (^)(FlutterError *_Nullable))completion; +/// Called when a marker drag updates. +- (void)didDragMarkerWithIdentifier:(NSString *)markerId + atPosition:(FGMPlatformLatLng *)position + completion:(void (^)(FlutterError *_Nullable))completion; +/// Called when a marker drag ends. +- (void)didEndDragForMarkerWithIdentifier:(NSString *)markerId + atPosition:(FGMPlatformLatLng *)position + completion:(void (^)(FlutterError *_Nullable))completion; +/// Called when a marker's info window is tapped. +- (void)didTapInfoWindowOfMarkerWithIdentifier:(NSString *)markerId + completion:(void (^)(FlutterError *_Nullable))completion; +/// Called when a circle is tapped. +- (void)didTapCircleWithIdentifier:(NSString *)circleId + completion:(void (^)(FlutterError *_Nullable))completion; +/// Called when a polygon is tapped. +- (void)didTapPolygonWithIdentifier:(NSString *)polygonId + completion:(void (^)(FlutterError *_Nullable))completion; +/// Called when a polyline is tapped. +- (void)didTapPolylineWithIdentifier:(NSString *)polylineId + completion:(void (^)(FlutterError *_Nullable))completion; +/// Called to get data for a map tile. +- (void)tileWithOverlayIdentifier:(NSString *)tileOverlayId + location:(FGMPlatformPoint *)location + zoom:(NSInteger)zoom + completion:(void (^)(FGMPlatformTile *_Nullable, + FlutterError *_Nullable))completion; +@end + /// Inspector API only intended for use in integration tests. @protocol FGMMapsInspectorApi /// @return `nil` only when `error != nil`. diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/messages.g.m b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/messages.g.m index 843fc11ead5..4836d517b89 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/messages.g.m +++ b/packages/google_maps_flutter/google_maps_flutter_ios/ios/Classes/messages.g.m @@ -25,11 +25,26 @@ return @[ result ?: [NSNull null] ]; } +static FlutterError *createConnectionError(NSString *channelName) { + return [FlutterError + errorWithCode:@"channel-error" + message:[NSString stringWithFormat:@"%@/%@/%@", + @"Unable to establish connection on channel: '", + channelName, @"'."] + details:@""]; +} + static id GetNullableObjectAtIndex(NSArray *array, NSInteger key) { id result = array[key]; return (result == [NSNull null]) ? nil : result; } +@interface FGMPlatformCameraPosition () ++ (FGMPlatformCameraPosition *)fromList:(NSArray *)list; ++ (nullable FGMPlatformCameraPosition *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + @interface FGMPlatformCameraUpdate () + (FGMPlatformCameraUpdate *)fromList:(NSArray *)list; + (nullable FGMPlatformCameraUpdate *)nullableFromList:(NSArray *)list; @@ -60,6 +75,12 @@ + (nullable FGMPlatformPolyline *)nullableFromList:(NSArray *)list; - (NSArray *)toList; @end +@interface FGMPlatformTile () ++ (FGMPlatformTile *)fromList:(NSArray *)list; ++ (nullable FGMPlatformTile *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + @interface FGMPlatformTileOverlay () + (FGMPlatformTileOverlay *)fromList:(NSArray *)list; + (nullable FGMPlatformTileOverlay *)nullableFromList:(NSArray *)list; @@ -102,6 +123,39 @@ + (nullable FGMPlatformZoomRange *)nullableFromList:(NSArray *)list; - (NSArray *)toList; @end +@implementation FGMPlatformCameraPosition ++ (instancetype)makeWithBearing:(double)bearing + target:(FGMPlatformLatLng *)target + tilt:(double)tilt + zoom:(double)zoom { + FGMPlatformCameraPosition *pigeonResult = [[FGMPlatformCameraPosition alloc] init]; + pigeonResult.bearing = bearing; + pigeonResult.target = target; + pigeonResult.tilt = tilt; + pigeonResult.zoom = zoom; + return pigeonResult; +} ++ (FGMPlatformCameraPosition *)fromList:(NSArray *)list { + FGMPlatformCameraPosition *pigeonResult = [[FGMPlatformCameraPosition alloc] init]; + pigeonResult.bearing = [GetNullableObjectAtIndex(list, 0) doubleValue]; + pigeonResult.target = GetNullableObjectAtIndex(list, 1); + pigeonResult.tilt = [GetNullableObjectAtIndex(list, 2) doubleValue]; + pigeonResult.zoom = [GetNullableObjectAtIndex(list, 3) doubleValue]; + return pigeonResult; +} ++ (nullable FGMPlatformCameraPosition *)nullableFromList:(NSArray *)list { + return (list) ? [FGMPlatformCameraPosition fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + @(self.bearing), + self.target ?: [NSNull null], + @(self.tilt), + @(self.zoom), + ]; +} +@end + @implementation FGMPlatformCameraUpdate + (instancetype)makeWithJson:(id)json { FGMPlatformCameraUpdate *pigeonResult = [[FGMPlatformCameraUpdate alloc] init]; @@ -207,6 +261,35 @@ + (nullable FGMPlatformPolyline *)nullableFromList:(NSArray *)list { } @end +@implementation FGMPlatformTile ++ (instancetype)makeWithWidth:(NSInteger)width + height:(NSInteger)height + data:(nullable FlutterStandardTypedData *)data { + FGMPlatformTile *pigeonResult = [[FGMPlatformTile alloc] init]; + pigeonResult.width = width; + pigeonResult.height = height; + pigeonResult.data = data; + return pigeonResult; +} ++ (FGMPlatformTile *)fromList:(NSArray *)list { + FGMPlatformTile *pigeonResult = [[FGMPlatformTile alloc] init]; + pigeonResult.width = [GetNullableObjectAtIndex(list, 0) integerValue]; + pigeonResult.height = [GetNullableObjectAtIndex(list, 1) integerValue]; + pigeonResult.data = GetNullableObjectAtIndex(list, 2); + return pigeonResult; +} ++ (nullable FGMPlatformTile *)nullableFromList:(NSArray *)list { + return (list) ? [FGMPlatformTile fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + @(self.width), + @(self.height), + self.data ?: [NSNull null], + ]; +} +@end + @implementation FGMPlatformTileOverlay + (instancetype)makeWithJson:(id)json { FGMPlatformTileOverlay *pigeonResult = [[FGMPlatformTileOverlay alloc] init]; @@ -385,28 +468,32 @@ @implementation FGMMessagesPigeonCodecReader - (nullable id)readValueOfType:(UInt8)type { switch (type) { case 129: - return [FGMPlatformCameraUpdate fromList:[self readValue]]; + return [FGMPlatformCameraPosition fromList:[self readValue]]; case 130: - return [FGMPlatformCircle fromList:[self readValue]]; + return [FGMPlatformCameraUpdate fromList:[self readValue]]; case 131: - return [FGMPlatformMarker fromList:[self readValue]]; + return [FGMPlatformCircle fromList:[self readValue]]; case 132: - return [FGMPlatformPolygon fromList:[self readValue]]; + return [FGMPlatformMarker fromList:[self readValue]]; case 133: - return [FGMPlatformPolyline fromList:[self readValue]]; + return [FGMPlatformPolygon fromList:[self readValue]]; case 134: - return [FGMPlatformTileOverlay fromList:[self readValue]]; + return [FGMPlatformPolyline fromList:[self readValue]]; case 135: - return [FGMPlatformLatLng fromList:[self readValue]]; + return [FGMPlatformTile fromList:[self readValue]]; case 136: - return [FGMPlatformLatLngBounds fromList:[self readValue]]; + return [FGMPlatformTileOverlay fromList:[self readValue]]; case 137: - return [FGMPlatformMapConfiguration fromList:[self readValue]]; + return [FGMPlatformLatLng fromList:[self readValue]]; case 138: - return [FGMPlatformPoint fromList:[self readValue]]; + return [FGMPlatformLatLngBounds fromList:[self readValue]]; case 139: - return [FGMPlatformTileLayer fromList:[self readValue]]; + return [FGMPlatformMapConfiguration fromList:[self readValue]]; case 140: + return [FGMPlatformPoint fromList:[self readValue]]; + case 141: + return [FGMPlatformTileLayer fromList:[self readValue]]; + case 142: return [FGMPlatformZoomRange fromList:[self readValue]]; default: return [super readValueOfType:type]; @@ -418,42 +505,48 @@ @interface FGMMessagesPigeonCodecWriter : FlutterStandardWriter @end @implementation FGMMessagesPigeonCodecWriter - (void)writeValue:(id)value { - if ([value isKindOfClass:[FGMPlatformCameraUpdate class]]) { + if ([value isKindOfClass:[FGMPlatformCameraPosition class]]) { [self writeByte:129]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformCircle class]]) { + } else if ([value isKindOfClass:[FGMPlatformCameraUpdate class]]) { [self writeByte:130]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformMarker class]]) { + } else if ([value isKindOfClass:[FGMPlatformCircle class]]) { [self writeByte:131]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformPolygon class]]) { + } else if ([value isKindOfClass:[FGMPlatformMarker class]]) { [self writeByte:132]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformPolyline class]]) { + } else if ([value isKindOfClass:[FGMPlatformPolygon class]]) { [self writeByte:133]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformTileOverlay class]]) { + } else if ([value isKindOfClass:[FGMPlatformPolyline class]]) { [self writeByte:134]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformLatLng class]]) { + } else if ([value isKindOfClass:[FGMPlatformTile class]]) { [self writeByte:135]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformLatLngBounds class]]) { + } else if ([value isKindOfClass:[FGMPlatformTileOverlay class]]) { [self writeByte:136]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformMapConfiguration class]]) { + } else if ([value isKindOfClass:[FGMPlatformLatLng class]]) { [self writeByte:137]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformPoint class]]) { + } else if ([value isKindOfClass:[FGMPlatformLatLngBounds class]]) { [self writeByte:138]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformTileLayer class]]) { + } else if ([value isKindOfClass:[FGMPlatformMapConfiguration class]]) { [self writeByte:139]; [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[FGMPlatformZoomRange class]]) { + } else if ([value isKindOfClass:[FGMPlatformPoint class]]) { [self writeByte:140]; [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[FGMPlatformTileLayer class]]) { + [self writeByte:141]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[FGMPlatformZoomRange class]]) { + [self writeByte:142]; + [self writeValue:[value toList]]; } else { [super writeValue:value]; } @@ -1019,6 +1112,386 @@ void SetUpFGMMapsApiWithSuffix(id binaryMessenger, } } } +@interface FGMMapsCallbackApi () +@property(nonatomic, strong) NSObject *binaryMessenger; +@property(nonatomic, strong) NSString *messageChannelSuffix; +@end + +@implementation FGMMapsCallbackApi + +- (instancetype)initWithBinaryMessenger:(NSObject *)binaryMessenger { + return [self initWithBinaryMessenger:binaryMessenger messageChannelSuffix:@""]; +} +- (instancetype)initWithBinaryMessenger:(NSObject *)binaryMessenger + messageChannelSuffix:(nullable NSString *)messageChannelSuffix { + self = [self init]; + if (self) { + _binaryMessenger = binaryMessenger; + _messageChannelSuffix = [messageChannelSuffix length] == 0 + ? @"" + : [NSString stringWithFormat:@".%@", messageChannelSuffix]; + } + return self; +} +- (void)didStartCameraMoveWithCompletion:(void (^)(FlutterError *_Nullable))completion { + NSString *channelName = [NSString + stringWithFormat: + @"%@%@", + @"dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onCameraMoveStarted", + _messageChannelSuffix]; + FlutterBasicMessageChannel *channel = + [FlutterBasicMessageChannel messageChannelWithName:channelName + binaryMessenger:self.binaryMessenger + codec:FGMGetMessagesCodec()]; + [channel sendMessage:nil + reply:^(NSArray *reply) { + if (reply != nil) { + if (reply.count > 1) { + completion([FlutterError errorWithCode:reply[0] + message:reply[1] + details:reply[2]]); + } else { + completion(nil); + } + } else { + completion(createConnectionError(channelName)); + } + }]; +} +- (void)didMoveCameraToPosition:(FGMPlatformCameraPosition *)arg_cameraPosition + completion:(void (^)(FlutterError *_Nullable))completion { + NSString *channelName = [NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onCameraMove", + _messageChannelSuffix]; + FlutterBasicMessageChannel *channel = + [FlutterBasicMessageChannel messageChannelWithName:channelName + binaryMessenger:self.binaryMessenger + codec:FGMGetMessagesCodec()]; + [channel sendMessage:@[ arg_cameraPosition ?: [NSNull null] ] + reply:^(NSArray *reply) { + if (reply != nil) { + if (reply.count > 1) { + completion([FlutterError errorWithCode:reply[0] + message:reply[1] + details:reply[2]]); + } else { + completion(nil); + } + } else { + completion(createConnectionError(channelName)); + } + }]; +} +- (void)didIdleCameraWithCompletion:(void (^)(FlutterError *_Nullable))completion { + NSString *channelName = [NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onCameraIdle", + _messageChannelSuffix]; + FlutterBasicMessageChannel *channel = + [FlutterBasicMessageChannel messageChannelWithName:channelName + binaryMessenger:self.binaryMessenger + codec:FGMGetMessagesCodec()]; + [channel sendMessage:nil + reply:^(NSArray *reply) { + if (reply != nil) { + if (reply.count > 1) { + completion([FlutterError errorWithCode:reply[0] + message:reply[1] + details:reply[2]]); + } else { + completion(nil); + } + } else { + completion(createConnectionError(channelName)); + } + }]; +} +- (void)didTapAtPosition:(FGMPlatformLatLng *)arg_position + completion:(void (^)(FlutterError *_Nullable))completion { + NSString *channelName = [NSString + stringWithFormat:@"%@%@", @"dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onTap", + _messageChannelSuffix]; + FlutterBasicMessageChannel *channel = + [FlutterBasicMessageChannel messageChannelWithName:channelName + binaryMessenger:self.binaryMessenger + codec:FGMGetMessagesCodec()]; + [channel sendMessage:@[ arg_position ?: [NSNull null] ] + reply:^(NSArray *reply) { + if (reply != nil) { + if (reply.count > 1) { + completion([FlutterError errorWithCode:reply[0] + message:reply[1] + details:reply[2]]); + } else { + completion(nil); + } + } else { + completion(createConnectionError(channelName)); + } + }]; +} +- (void)didLongPressAtPosition:(FGMPlatformLatLng *)arg_position + completion:(void (^)(FlutterError *_Nullable))completion { + NSString *channelName = [NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onLongPress", + _messageChannelSuffix]; + FlutterBasicMessageChannel *channel = + [FlutterBasicMessageChannel messageChannelWithName:channelName + binaryMessenger:self.binaryMessenger + codec:FGMGetMessagesCodec()]; + [channel sendMessage:@[ arg_position ?: [NSNull null] ] + reply:^(NSArray *reply) { + if (reply != nil) { + if (reply.count > 1) { + completion([FlutterError errorWithCode:reply[0] + message:reply[1] + details:reply[2]]); + } else { + completion(nil); + } + } else { + completion(createConnectionError(channelName)); + } + }]; +} +- (void)didTapMarkerWithIdentifier:(NSString *)arg_markerId + completion:(void (^)(FlutterError *_Nullable))completion { + NSString *channelName = [NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onMarkerTap", + _messageChannelSuffix]; + FlutterBasicMessageChannel *channel = + [FlutterBasicMessageChannel messageChannelWithName:channelName + binaryMessenger:self.binaryMessenger + codec:FGMGetMessagesCodec()]; + [channel sendMessage:@[ arg_markerId ?: [NSNull null] ] + reply:^(NSArray *reply) { + if (reply != nil) { + if (reply.count > 1) { + completion([FlutterError errorWithCode:reply[0] + message:reply[1] + details:reply[2]]); + } else { + completion(nil); + } + } else { + completion(createConnectionError(channelName)); + } + }]; +} +- (void)didStartDragForMarkerWithIdentifier:(NSString *)arg_markerId + atPosition:(FGMPlatformLatLng *)arg_position + completion:(void (^)(FlutterError *_Nullable))completion { + NSString *channelName = [NSString + stringWithFormat: + @"%@%@", @"dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onMarkerDragStart", + _messageChannelSuffix]; + FlutterBasicMessageChannel *channel = + [FlutterBasicMessageChannel messageChannelWithName:channelName + binaryMessenger:self.binaryMessenger + codec:FGMGetMessagesCodec()]; + [channel sendMessage:@[ arg_markerId ?: [NSNull null], arg_position ?: [NSNull null] ] + reply:^(NSArray *reply) { + if (reply != nil) { + if (reply.count > 1) { + completion([FlutterError errorWithCode:reply[0] + message:reply[1] + details:reply[2]]); + } else { + completion(nil); + } + } else { + completion(createConnectionError(channelName)); + } + }]; +} +- (void)didDragMarkerWithIdentifier:(NSString *)arg_markerId + atPosition:(FGMPlatformLatLng *)arg_position + completion:(void (^)(FlutterError *_Nullable))completion { + NSString *channelName = [NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onMarkerDrag", + _messageChannelSuffix]; + FlutterBasicMessageChannel *channel = + [FlutterBasicMessageChannel messageChannelWithName:channelName + binaryMessenger:self.binaryMessenger + codec:FGMGetMessagesCodec()]; + [channel sendMessage:@[ arg_markerId ?: [NSNull null], arg_position ?: [NSNull null] ] + reply:^(NSArray *reply) { + if (reply != nil) { + if (reply.count > 1) { + completion([FlutterError errorWithCode:reply[0] + message:reply[1] + details:reply[2]]); + } else { + completion(nil); + } + } else { + completion(createConnectionError(channelName)); + } + }]; +} +- (void)didEndDragForMarkerWithIdentifier:(NSString *)arg_markerId + atPosition:(FGMPlatformLatLng *)arg_position + completion:(void (^)(FlutterError *_Nullable))completion { + NSString *channelName = [NSString + stringWithFormat: + @"%@%@", @"dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onMarkerDragEnd", + _messageChannelSuffix]; + FlutterBasicMessageChannel *channel = + [FlutterBasicMessageChannel messageChannelWithName:channelName + binaryMessenger:self.binaryMessenger + codec:FGMGetMessagesCodec()]; + [channel sendMessage:@[ arg_markerId ?: [NSNull null], arg_position ?: [NSNull null] ] + reply:^(NSArray *reply) { + if (reply != nil) { + if (reply.count > 1) { + completion([FlutterError errorWithCode:reply[0] + message:reply[1] + details:reply[2]]); + } else { + completion(nil); + } + } else { + completion(createConnectionError(channelName)); + } + }]; +} +- (void)didTapInfoWindowOfMarkerWithIdentifier:(NSString *)arg_markerId + completion:(void (^)(FlutterError *_Nullable))completion { + NSString *channelName = [NSString + stringWithFormat: + @"%@%@", @"dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onInfoWindowTap", + _messageChannelSuffix]; + FlutterBasicMessageChannel *channel = + [FlutterBasicMessageChannel messageChannelWithName:channelName + binaryMessenger:self.binaryMessenger + codec:FGMGetMessagesCodec()]; + [channel sendMessage:@[ arg_markerId ?: [NSNull null] ] + reply:^(NSArray *reply) { + if (reply != nil) { + if (reply.count > 1) { + completion([FlutterError errorWithCode:reply[0] + message:reply[1] + details:reply[2]]); + } else { + completion(nil); + } + } else { + completion(createConnectionError(channelName)); + } + }]; +} +- (void)didTapCircleWithIdentifier:(NSString *)arg_circleId + completion:(void (^)(FlutterError *_Nullable))completion { + NSString *channelName = [NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onCircleTap", + _messageChannelSuffix]; + FlutterBasicMessageChannel *channel = + [FlutterBasicMessageChannel messageChannelWithName:channelName + binaryMessenger:self.binaryMessenger + codec:FGMGetMessagesCodec()]; + [channel sendMessage:@[ arg_circleId ?: [NSNull null] ] + reply:^(NSArray *reply) { + if (reply != nil) { + if (reply.count > 1) { + completion([FlutterError errorWithCode:reply[0] + message:reply[1] + details:reply[2]]); + } else { + completion(nil); + } + } else { + completion(createConnectionError(channelName)); + } + }]; +} +- (void)didTapPolygonWithIdentifier:(NSString *)arg_polygonId + completion:(void (^)(FlutterError *_Nullable))completion { + NSString *channelName = [NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onPolygonTap", + _messageChannelSuffix]; + FlutterBasicMessageChannel *channel = + [FlutterBasicMessageChannel messageChannelWithName:channelName + binaryMessenger:self.binaryMessenger + codec:FGMGetMessagesCodec()]; + [channel sendMessage:@[ arg_polygonId ?: [NSNull null] ] + reply:^(NSArray *reply) { + if (reply != nil) { + if (reply.count > 1) { + completion([FlutterError errorWithCode:reply[0] + message:reply[1] + details:reply[2]]); + } else { + completion(nil); + } + } else { + completion(createConnectionError(channelName)); + } + }]; +} +- (void)didTapPolylineWithIdentifier:(NSString *)arg_polylineId + completion:(void (^)(FlutterError *_Nullable))completion { + NSString *channelName = [NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onPolylineTap", + _messageChannelSuffix]; + FlutterBasicMessageChannel *channel = + [FlutterBasicMessageChannel messageChannelWithName:channelName + binaryMessenger:self.binaryMessenger + codec:FGMGetMessagesCodec()]; + [channel sendMessage:@[ arg_polylineId ?: [NSNull null] ] + reply:^(NSArray *reply) { + if (reply != nil) { + if (reply.count > 1) { + completion([FlutterError errorWithCode:reply[0] + message:reply[1] + details:reply[2]]); + } else { + completion(nil); + } + } else { + completion(createConnectionError(channelName)); + } + }]; +} +- (void)tileWithOverlayIdentifier:(NSString *)arg_tileOverlayId + location:(FGMPlatformPoint *)arg_location + zoom:(NSInteger)arg_zoom + completion:(void (^)(FGMPlatformTile *_Nullable, + FlutterError *_Nullable))completion { + NSString *channelName = [NSString + stringWithFormat: + @"%@%@", @"dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.getTileOverlayTile", + _messageChannelSuffix]; + FlutterBasicMessageChannel *channel = + [FlutterBasicMessageChannel messageChannelWithName:channelName + binaryMessenger:self.binaryMessenger + codec:FGMGetMessagesCodec()]; + [channel sendMessage:@[ + arg_tileOverlayId ?: [NSNull null], arg_location ?: [NSNull null], @(arg_zoom) + ] + reply:^(NSArray *reply) { + if (reply != nil) { + if (reply.count > 1) { + completion(nil, [FlutterError errorWithCode:reply[0] + message:reply[1] + details:reply[2]]); + } else { + FGMPlatformTile *output = reply[0] == [NSNull null] ? nil : reply[0]; + completion(output, nil); + } + } else { + completion(nil, createConnectionError(channelName)); + } + }]; +} +@end + void SetUpFGMMapsInspectorApi(id binaryMessenger, NSObject *api) { SetUpFGMMapsInspectorApiWithSuffix(binaryMessenger, api, @""); diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_maps_flutter_ios.dart b/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_maps_flutter_ios.dart index dd8cb856f82..8e9a96d094b 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_maps_flutter_ios.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/google_maps_flutter_ios.dart @@ -55,15 +55,16 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { GoogleMapsFlutterPlatform.instance = GoogleMapsFlutterIOS(); } - // Keep a collection of id -> channel - // Every method call passes the int mapId - final Map _channels = {}; - final Map _hostMaps = {}; // A method to create MapsApi instances, which can be overridden for testing. final MapsApi Function(int mapId) _apiProvider; + /// The per-map handlers for callbacks from the host side. + @visibleForTesting + final Map hostMapHandlers = + {}; + /// Accesses the MapsApi associated to the passed mapId. MapsApi _hostApi(int mapId) { final MapsApi? api = _hostMaps[mapId]; @@ -77,17 +78,23 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { final Map> _tileOverlays = >{}; - /// Returns the channel for [mapId], creating it if it doesn't already exist. + /// Returns the handler for [mapId], creating it if it doesn't already exist. @visibleForTesting - MethodChannel ensureChannelInitialized(int mapId) { - MethodChannel? channel = _channels[mapId]; - if (channel == null) { - channel = MethodChannel('plugins.flutter.dev/google_maps_ios_$mapId'); - channel.setMethodCallHandler( - (MethodCall call) => _handleMethodCall(call, mapId)); - _channels[mapId] = channel; + HostMapMessageHandler ensureHandlerInitialized(int mapId) { + HostMapMessageHandler? handler = hostMapHandlers[mapId]; + if (handler == null) { + handler = HostMapMessageHandler( + mapId, + _mapEventStreamController, + tileOverlayProvider: (TileOverlayId tileOverlayId) { + final Map? tileOverlaysForMap = + _tileOverlays[mapId]; + return tileOverlaysForMap?[tileOverlayId]; + }, + ); + hostMapHandlers[mapId] = handler; } - return channel; + return handler; } /// Returns the API instance for [mapId], creating it if it doesn't already @@ -104,7 +111,7 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { @override Future init(int mapId) { - ensureChannelInitialized(mapId); + ensureHandlerInitialized(mapId); final MapsApi hostApi = ensureApiInitialized(mapId); return hostApi.waitForMap(); } @@ -192,111 +199,6 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { return _events(mapId).whereType(); } - Future _handleMethodCall(MethodCall call, int mapId) async { - switch (call.method) { - case 'camera#onMoveStarted': - _mapEventStreamController.add(CameraMoveStartedEvent(mapId)); - case 'camera#onMove': - final Map arguments = _getArgumentDictionary(call); - _mapEventStreamController.add(CameraMoveEvent( - mapId, - CameraPosition.fromMap(arguments['position'])!, - )); - case 'camera#onIdle': - _mapEventStreamController.add(CameraIdleEvent(mapId)); - case 'marker#onTap': - final Map arguments = _getArgumentDictionary(call); - _mapEventStreamController.add(MarkerTapEvent( - mapId, - MarkerId(arguments['markerId']! as String), - )); - case 'marker#onDragStart': - final Map arguments = _getArgumentDictionary(call); - _mapEventStreamController.add(MarkerDragStartEvent( - mapId, - LatLng.fromJson(arguments['position'])!, - MarkerId(arguments['markerId']! as String), - )); - case 'marker#onDrag': - final Map arguments = _getArgumentDictionary(call); - _mapEventStreamController.add(MarkerDragEvent( - mapId, - LatLng.fromJson(arguments['position'])!, - MarkerId(arguments['markerId']! as String), - )); - case 'marker#onDragEnd': - final Map arguments = _getArgumentDictionary(call); - _mapEventStreamController.add(MarkerDragEndEvent( - mapId, - LatLng.fromJson(arguments['position'])!, - MarkerId(arguments['markerId']! as String), - )); - case 'infoWindow#onTap': - final Map arguments = _getArgumentDictionary(call); - _mapEventStreamController.add(InfoWindowTapEvent( - mapId, - MarkerId(arguments['markerId']! as String), - )); - case 'polyline#onTap': - final Map arguments = _getArgumentDictionary(call); - _mapEventStreamController.add(PolylineTapEvent( - mapId, - PolylineId(arguments['polylineId']! as String), - )); - case 'polygon#onTap': - final Map arguments = _getArgumentDictionary(call); - _mapEventStreamController.add(PolygonTapEvent( - mapId, - PolygonId(arguments['polygonId']! as String), - )); - case 'circle#onTap': - final Map arguments = _getArgumentDictionary(call); - _mapEventStreamController.add(CircleTapEvent( - mapId, - CircleId(arguments['circleId']! as String), - )); - case 'map#onTap': - final Map arguments = _getArgumentDictionary(call); - _mapEventStreamController.add(MapTapEvent( - mapId, - LatLng.fromJson(arguments['position'])!, - )); - case 'map#onLongPress': - final Map arguments = _getArgumentDictionary(call); - _mapEventStreamController.add(MapLongPressEvent( - mapId, - LatLng.fromJson(arguments['position'])!, - )); - case 'tileOverlay#getTile': - final Map arguments = _getArgumentDictionary(call); - final Map? tileOverlaysForThisMap = - _tileOverlays[mapId]; - final String tileOverlayId = arguments['tileOverlayId']! as String; - final TileOverlay? tileOverlay = - tileOverlaysForThisMap?[TileOverlayId(tileOverlayId)]; - final TileProvider? tileProvider = tileOverlay?.tileProvider; - if (tileProvider == null) { - return TileProvider.noTile.toJson(); - } - final Tile tile = await tileProvider.getTile( - arguments['x']! as int, - arguments['y']! as int, - arguments['zoom'] as int?, - ); - return tile.toJson(); - default: - throw MissingPluginException(); - } - } - - /// Returns the arguments of [call] as typed string-keyed Map. - /// - /// This does not do any type validation, so is only safe to call if the - /// arguments are known to be a map. - Map _getArgumentDictionary(MethodCall call) { - return (call.arguments as Map).cast(); - } - @override Future updateMapConfiguration( MapConfiguration configuration, { @@ -613,10 +515,6 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { MapsInspectorApi(messageChannelSuffix: mapId.toString())); } - static LatLng _latLngFromPlatformLatLng(PlatformLatLng latLng) { - return LatLng(latLng.latitude, latLng.longitude); - } - static PlatformLatLng _platformLatLngFromLatLng(LatLng latLng) { return PlatformLatLng( latitude: latLng.latitude, longitude: latLng.longitude); @@ -633,13 +531,6 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { x: coordinate.x.toDouble(), y: coordinate.y.toDouble()); } - static LatLngBounds _latLngBoundsFromPlatformLatLngBounds( - PlatformLatLngBounds bounds) { - return LatLngBounds( - southwest: _latLngFromPlatformLatLng(bounds.southwest), - northeast: _latLngFromPlatformLatLng(bounds.northeast)); - } - static PlatformCircle _platformCircleFromCircle(Circle circle) { return PlatformCircle(json: circle.toJson()); } @@ -662,6 +553,144 @@ class GoogleMapsFlutterIOS extends GoogleMapsFlutterPlatform { } } +/// Callback handler for map events from the platform host. +@visibleForTesting +class HostMapMessageHandler implements MapsCallbackApi { + /// Creates a new handler that listens for events from map [mapId], and + /// broadcasts them to [streamController]. + HostMapMessageHandler( + this.mapId, + this.streamController, { + required this.tileOverlayProvider, + }) { + MapsCallbackApi.setUp(this, messageChannelSuffix: mapId.toString()); + } + + /// Removes the handler for native messages. + void dispose() { + MapsCallbackApi.setUp(null, messageChannelSuffix: mapId.toString()); + } + + /// The map ID this handler listens for events from. + final int mapId; + + /// The controller used to broadcast map events coming from the + /// host platform. + final StreamController> streamController; + + /// The callback to get a tile overlay for the corresponding map. + final TileOverlay? Function(TileOverlayId tileOverlayId) tileOverlayProvider; + + @override + Future getTileOverlayTile( + String tileOverlayId, + PlatformPoint location, + int zoom, + ) async { + final TileOverlay? tileOverlay = + tileOverlayProvider(TileOverlayId(tileOverlayId)); + final TileProvider? tileProvider = tileOverlay?.tileProvider; + final Tile tile = tileProvider == null + ? TileProvider.noTile + : await tileProvider.getTile( + location.x.round(), location.y.round(), zoom); + return _platformTileFromTile(tile); + } + + @override + void onCameraIdle() { + streamController.add(CameraIdleEvent(mapId)); + } + + @override + void onCameraMove(PlatformCameraPosition cameraPosition) { + streamController.add(CameraMoveEvent( + mapId, + CameraPosition( + target: _latLngFromPlatformLatLng(cameraPosition.target), + bearing: cameraPosition.bearing, + tilt: cameraPosition.tilt, + zoom: cameraPosition.zoom, + ), + )); + } + + @override + void onCameraMoveStarted() { + streamController.add(CameraMoveStartedEvent(mapId)); + } + + @override + void onCircleTap(String circleId) { + streamController.add(CircleTapEvent(mapId, CircleId(circleId))); + } + + @override + void onInfoWindowTap(String markerId) { + streamController.add(InfoWindowTapEvent(mapId, MarkerId(markerId))); + } + + @override + void onLongPress(PlatformLatLng position) { + streamController + .add(MapLongPressEvent(mapId, _latLngFromPlatformLatLng(position))); + } + + @override + void onMarkerDrag(String markerId, PlatformLatLng position) { + streamController.add(MarkerDragEvent( + mapId, _latLngFromPlatformLatLng(position), MarkerId(markerId))); + } + + @override + void onMarkerDragStart(String markerId, PlatformLatLng position) { + streamController.add(MarkerDragStartEvent( + mapId, _latLngFromPlatformLatLng(position), MarkerId(markerId))); + } + + @override + void onMarkerDragEnd(String markerId, PlatformLatLng position) { + streamController.add(MarkerDragEndEvent( + mapId, _latLngFromPlatformLatLng(position), MarkerId(markerId))); + } + + @override + void onMarkerTap(String markerId) { + streamController.add(MarkerTapEvent(mapId, MarkerId(markerId))); + } + + @override + void onPolygonTap(String polygonId) { + streamController.add(PolygonTapEvent(mapId, PolygonId(polygonId))); + } + + @override + void onPolylineTap(String polylineId) { + streamController.add(PolylineTapEvent(mapId, PolylineId(polylineId))); + } + + @override + void onTap(PlatformLatLng position) { + streamController + .add(MapTapEvent(mapId, _latLngFromPlatformLatLng(position))); + } +} + +LatLng _latLngFromPlatformLatLng(PlatformLatLng latLng) { + return LatLng(latLng.latitude, latLng.longitude); +} + +LatLngBounds _latLngBoundsFromPlatformLatLngBounds( + PlatformLatLngBounds bounds) { + return LatLngBounds( + southwest: _latLngFromPlatformLatLng(bounds.southwest), + northeast: _latLngFromPlatformLatLng(bounds.northeast)); +} + +PlatformTile _platformTileFromTile(Tile tile) { + return PlatformTile(width: tile.width, height: tile.height, data: tile.data); +} + Map _jsonForMapConfiguration(MapConfiguration config) { final EdgeInsets? padding = config.padding; return { diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/messages.g.dart b/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/messages.g.dart index b327880eb9d..be309537588 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/messages.g.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/lib/src/messages.g.dart @@ -18,6 +18,54 @@ PlatformException _createConnectionError(String channelName) { ); } +List wrapResponse( + {Object? result, PlatformException? error, bool empty = false}) { + if (empty) { + return []; + } + if (error == null) { + return [result]; + } + return [error.code, error.message, error.details]; +} + +/// Pigeon representatation of a CameraPosition. +class PlatformCameraPosition { + PlatformCameraPosition({ + required this.bearing, + required this.target, + required this.tilt, + required this.zoom, + }); + + double bearing; + + PlatformLatLng target; + + double tilt; + + double zoom; + + Object encode() { + return [ + bearing, + target, + tilt, + zoom, + ]; + } + + static PlatformCameraPosition decode(Object result) { + result as List; + return PlatformCameraPosition( + bearing: result[0]! as double, + target: result[1]! as PlatformLatLng, + tilt: result[2]! as double, + zoom: result[3]! as double, + ); + } +} + /// Pigeon representation of a CameraUpdate. class PlatformCameraUpdate { PlatformCameraUpdate({ @@ -143,6 +191,38 @@ class PlatformPolyline { } } +/// Pigeon equivalent of the Tile class. +class PlatformTile { + PlatformTile({ + required this.width, + required this.height, + this.data, + }); + + int width; + + int height; + + Uint8List? data; + + Object encode() { + return [ + width, + height, + data, + ]; + } + + static PlatformTile decode(Object result) { + result as List; + return PlatformTile( + width: result[0]! as int, + height: result[1]! as int, + data: result[2] as Uint8List?, + ); + } +} + /// Pigeon equivalent of the TileOverlay class. class PlatformTileOverlay { PlatformTileOverlay({ @@ -342,42 +422,48 @@ class _PigeonCodec extends StandardMessageCodec { const _PigeonCodec(); @override void writeValue(WriteBuffer buffer, Object? value) { - if (value is PlatformCameraUpdate) { + if (value is PlatformCameraPosition) { buffer.putUint8(129); writeValue(buffer, value.encode()); - } else if (value is PlatformCircle) { + } else if (value is PlatformCameraUpdate) { buffer.putUint8(130); writeValue(buffer, value.encode()); - } else if (value is PlatformMarker) { + } else if (value is PlatformCircle) { buffer.putUint8(131); writeValue(buffer, value.encode()); - } else if (value is PlatformPolygon) { + } else if (value is PlatformMarker) { buffer.putUint8(132); writeValue(buffer, value.encode()); - } else if (value is PlatformPolyline) { + } else if (value is PlatformPolygon) { buffer.putUint8(133); writeValue(buffer, value.encode()); - } else if (value is PlatformTileOverlay) { + } else if (value is PlatformPolyline) { buffer.putUint8(134); writeValue(buffer, value.encode()); - } else if (value is PlatformLatLng) { + } else if (value is PlatformTile) { buffer.putUint8(135); writeValue(buffer, value.encode()); - } else if (value is PlatformLatLngBounds) { + } else if (value is PlatformTileOverlay) { buffer.putUint8(136); writeValue(buffer, value.encode()); - } else if (value is PlatformMapConfiguration) { + } else if (value is PlatformLatLng) { buffer.putUint8(137); writeValue(buffer, value.encode()); - } else if (value is PlatformPoint) { + } else if (value is PlatformLatLngBounds) { buffer.putUint8(138); writeValue(buffer, value.encode()); - } else if (value is PlatformTileLayer) { + } else if (value is PlatformMapConfiguration) { buffer.putUint8(139); writeValue(buffer, value.encode()); - } else if (value is PlatformZoomRange) { + } else if (value is PlatformPoint) { buffer.putUint8(140); writeValue(buffer, value.encode()); + } else if (value is PlatformTileLayer) { + buffer.putUint8(141); + writeValue(buffer, value.encode()); + } else if (value is PlatformZoomRange) { + buffer.putUint8(142); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -387,28 +473,32 @@ class _PigeonCodec extends StandardMessageCodec { Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { case 129: - return PlatformCameraUpdate.decode(readValue(buffer)!); + return PlatformCameraPosition.decode(readValue(buffer)!); case 130: - return PlatformCircle.decode(readValue(buffer)!); + return PlatformCameraUpdate.decode(readValue(buffer)!); case 131: - return PlatformMarker.decode(readValue(buffer)!); + return PlatformCircle.decode(readValue(buffer)!); case 132: - return PlatformPolygon.decode(readValue(buffer)!); + return PlatformMarker.decode(readValue(buffer)!); case 133: - return PlatformPolyline.decode(readValue(buffer)!); + return PlatformPolygon.decode(readValue(buffer)!); case 134: - return PlatformTileOverlay.decode(readValue(buffer)!); + return PlatformPolyline.decode(readValue(buffer)!); case 135: - return PlatformLatLng.decode(readValue(buffer)!); + return PlatformTile.decode(readValue(buffer)!); case 136: - return PlatformLatLngBounds.decode(readValue(buffer)!); + return PlatformTileOverlay.decode(readValue(buffer)!); case 137: - return PlatformMapConfiguration.decode(readValue(buffer)!); + return PlatformLatLng.decode(readValue(buffer)!); case 138: - return PlatformPoint.decode(readValue(buffer)!); + return PlatformLatLngBounds.decode(readValue(buffer)!); case 139: - return PlatformTileLayer.decode(readValue(buffer)!); + return PlatformMapConfiguration.decode(readValue(buffer)!); case 140: + return PlatformPoint.decode(readValue(buffer)!); + case 141: + return PlatformTileLayer.decode(readValue(buffer)!); + case 142: return PlatformZoomRange.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -978,6 +1068,460 @@ class MapsApi { } } +/// Interface for calls from the native SDK to Dart. +abstract class MapsCallbackApi { + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); + + /// Called when the map camera starts moving. + void onCameraMoveStarted(); + + /// Called when the map camera moves. + void onCameraMove(PlatformCameraPosition cameraPosition); + + /// Called when the map camera stops moving. + void onCameraIdle(); + + /// Called when the map, not a specifc map object, is tapped. + void onTap(PlatformLatLng position); + + /// Called when the map, not a specifc map object, is long pressed. + void onLongPress(PlatformLatLng position); + + /// Called when a marker is tapped. + void onMarkerTap(String markerId); + + /// Called when a marker drag starts. + void onMarkerDragStart(String markerId, PlatformLatLng position); + + /// Called when a marker drag updates. + void onMarkerDrag(String markerId, PlatformLatLng position); + + /// Called when a marker drag ends. + void onMarkerDragEnd(String markerId, PlatformLatLng position); + + /// Called when a marker's info window is tapped. + void onInfoWindowTap(String markerId); + + /// Called when a circle is tapped. + void onCircleTap(String circleId); + + /// Called when a polygon is tapped. + void onPolygonTap(String polygonId); + + /// Called when a polyline is tapped. + void onPolylineTap(String polylineId); + + /// Called to get data for a map tile. + Future getTileOverlayTile( + String tileOverlayId, PlatformPoint location, int zoom); + + static void setUp( + MapsCallbackApi? api, { + BinaryMessenger? binaryMessenger, + String messageChannelSuffix = '', + }) { + messageChannelSuffix = + messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onCameraMoveStarted$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + try { + api.onCameraMoveStarted(); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onCameraMove$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onCameraMove was null.'); + final List args = (message as List?)!; + final PlatformCameraPosition? arg_cameraPosition = + (args[0] as PlatformCameraPosition?); + assert(arg_cameraPosition != null, + 'Argument for dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onCameraMove was null, expected non-null PlatformCameraPosition.'); + try { + api.onCameraMove(arg_cameraPosition!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onCameraIdle$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + try { + api.onCameraIdle(); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onTap$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onTap was null.'); + final List args = (message as List?)!; + final PlatformLatLng? arg_position = (args[0] as PlatformLatLng?); + assert(arg_position != null, + 'Argument for dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onTap was null, expected non-null PlatformLatLng.'); + try { + api.onTap(arg_position!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onLongPress$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onLongPress was null.'); + final List args = (message as List?)!; + final PlatformLatLng? arg_position = (args[0] as PlatformLatLng?); + assert(arg_position != null, + 'Argument for dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onLongPress was null, expected non-null PlatformLatLng.'); + try { + api.onLongPress(arg_position!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onMarkerTap$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onMarkerTap was null.'); + final List args = (message as List?)!; + final String? arg_markerId = (args[0] as String?); + assert(arg_markerId != null, + 'Argument for dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onMarkerTap was null, expected non-null String.'); + try { + api.onMarkerTap(arg_markerId!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onMarkerDragStart$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onMarkerDragStart was null.'); + final List args = (message as List?)!; + final String? arg_markerId = (args[0] as String?); + assert(arg_markerId != null, + 'Argument for dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onMarkerDragStart was null, expected non-null String.'); + final PlatformLatLng? arg_position = (args[1] as PlatformLatLng?); + assert(arg_position != null, + 'Argument for dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onMarkerDragStart was null, expected non-null PlatformLatLng.'); + try { + api.onMarkerDragStart(arg_markerId!, arg_position!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onMarkerDrag$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onMarkerDrag was null.'); + final List args = (message as List?)!; + final String? arg_markerId = (args[0] as String?); + assert(arg_markerId != null, + 'Argument for dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onMarkerDrag was null, expected non-null String.'); + final PlatformLatLng? arg_position = (args[1] as PlatformLatLng?); + assert(arg_position != null, + 'Argument for dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onMarkerDrag was null, expected non-null PlatformLatLng.'); + try { + api.onMarkerDrag(arg_markerId!, arg_position!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onMarkerDragEnd$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onMarkerDragEnd was null.'); + final List args = (message as List?)!; + final String? arg_markerId = (args[0] as String?); + assert(arg_markerId != null, + 'Argument for dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onMarkerDragEnd was null, expected non-null String.'); + final PlatformLatLng? arg_position = (args[1] as PlatformLatLng?); + assert(arg_position != null, + 'Argument for dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onMarkerDragEnd was null, expected non-null PlatformLatLng.'); + try { + api.onMarkerDragEnd(arg_markerId!, arg_position!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onInfoWindowTap$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onInfoWindowTap was null.'); + final List args = (message as List?)!; + final String? arg_markerId = (args[0] as String?); + assert(arg_markerId != null, + 'Argument for dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onInfoWindowTap was null, expected non-null String.'); + try { + api.onInfoWindowTap(arg_markerId!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onCircleTap$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onCircleTap was null.'); + final List args = (message as List?)!; + final String? arg_circleId = (args[0] as String?); + assert(arg_circleId != null, + 'Argument for dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onCircleTap was null, expected non-null String.'); + try { + api.onCircleTap(arg_circleId!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onPolygonTap$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onPolygonTap was null.'); + final List args = (message as List?)!; + final String? arg_polygonId = (args[0] as String?); + assert(arg_polygonId != null, + 'Argument for dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onPolygonTap was null, expected non-null String.'); + try { + api.onPolygonTap(arg_polygonId!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onPolylineTap$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onPolylineTap was null.'); + final List args = (message as List?)!; + final String? arg_polylineId = (args[0] as String?); + assert(arg_polylineId != null, + 'Argument for dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.onPolylineTap was null, expected non-null String.'); + try { + api.onPolylineTap(arg_polylineId!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.getTileOverlayTile$messageChannelSuffix', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.getTileOverlayTile was null.'); + final List args = (message as List?)!; + final String? arg_tileOverlayId = (args[0] as String?); + assert(arg_tileOverlayId != null, + 'Argument for dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.getTileOverlayTile was null, expected non-null String.'); + final PlatformPoint? arg_location = (args[1] as PlatformPoint?); + assert(arg_location != null, + 'Argument for dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.getTileOverlayTile was null, expected non-null PlatformPoint.'); + final int? arg_zoom = (args[2] as int?); + assert(arg_zoom != null, + 'Argument for dev.flutter.pigeon.google_maps_flutter_ios.MapsCallbackApi.getTileOverlayTile was null, expected non-null int.'); + try { + final PlatformTile output = await api.getTileOverlayTile( + arg_tileOverlayId!, arg_location!, arg_zoom!); + return wrapResponse(result: output); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + } +} + /// Inspector API only intended for use in integration tests. class MapsInspectorApi { /// Constructor for [MapsInspectorApi]. The [binaryMessenger] named argument is diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/pigeons/messages.dart b/packages/google_maps_flutter/google_maps_flutter_ios/pigeons/messages.dart index b6321bf5b4c..19db3aa8502 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/pigeons/messages.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/pigeons/messages.dart @@ -12,6 +12,21 @@ import 'package:pigeon/pigeon.dart'; copyrightHeader: 'pigeons/copyright.txt', )) +/// Pigeon representatation of a CameraPosition. +class PlatformCameraPosition { + PlatformCameraPosition({ + required this.bearing, + required this.target, + required this.tilt, + required this.zoom, + }); + + final double bearing; + final PlatformLatLng target; + final double tilt; + final double zoom; +} + /// Pigeon representation of a CameraUpdate. class PlatformCameraUpdate { PlatformCameraUpdate(this.json); @@ -75,6 +90,15 @@ class PlatformPolyline { final Object json; } +/// Pigeon equivalent of the Tile class. +class PlatformTile { + PlatformTile({required this.width, required this.height, required this.data}); + + final int width; + final int height; + final Uint8List? data; +} + /// Pigeon equivalent of the TileOverlay class. class PlatformTileOverlay { PlatformTileOverlay(this.json); @@ -263,6 +287,68 @@ abstract class MapsApi { Uint8List? takeSnapshot(); } +/// Interface for calls from the native SDK to Dart. +@FlutterApi() +abstract class MapsCallbackApi { + /// Called when the map camera starts moving. + @ObjCSelector('didStartCameraMoveWithCompletion') + void onCameraMoveStarted(); + + /// Called when the map camera moves. + @ObjCSelector('didMoveCameraToPosition:') + void onCameraMove(PlatformCameraPosition cameraPosition); + + /// Called when the map camera stops moving. + @ObjCSelector('didIdleCameraWithCompletion') + void onCameraIdle(); + + /// Called when the map, not a specifc map object, is tapped. + @ObjCSelector('didTapAtPosition:') + void onTap(PlatformLatLng position); + + /// Called when the map, not a specifc map object, is long pressed. + @ObjCSelector('didLongPressAtPosition:') + void onLongPress(PlatformLatLng position); + + /// Called when a marker is tapped. + @ObjCSelector('didTapMarkerWithIdentifier:') + void onMarkerTap(String markerId); + + /// Called when a marker drag starts. + @ObjCSelector('didStartDragForMarkerWithIdentifier:atPosition:') + void onMarkerDragStart(String markerId, PlatformLatLng position); + + /// Called when a marker drag updates. + @ObjCSelector('didDragMarkerWithIdentifier:atPosition:') + void onMarkerDrag(String markerId, PlatformLatLng position); + + /// Called when a marker drag ends. + @ObjCSelector('didEndDragForMarkerWithIdentifier:atPosition:') + void onMarkerDragEnd(String markerId, PlatformLatLng position); + + /// Called when a marker's info window is tapped. + @ObjCSelector('didTapInfoWindowOfMarkerWithIdentifier:') + void onInfoWindowTap(String markerId); + + /// Called when a circle is tapped. + @ObjCSelector('didTapCircleWithIdentifier:') + void onCircleTap(String circleId); + + /// Called when a polygon is tapped. + @ObjCSelector('didTapPolygonWithIdentifier:') + void onPolygonTap(String polygonId); + + /// Called when a polyline is tapped. + @ObjCSelector('didTapPolylineWithIdentifier:') + void onPolylineTap(String polylineId); + + /// Called to get data for a map tile. + @async + @ObjCSelector('tileWithOverlayIdentifier:location:zoom:') + PlatformTile getTileOverlayTile( + String tileOverlayId, PlatformPoint location, int zoom); +} + /// Inspector API only intended for use in integration tests. @HostApi() abstract class MapsInspectorApi { diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml b/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml index a70cc3e8f8b..d73df0ee638 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml +++ b/packages/google_maps_flutter/google_maps_flutter_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: google_maps_flutter_ios description: iOS implementation of the google_maps_flutter plugin. repository: https://github.com/flutter/packages/tree/main/packages/google_maps_flutter/google_maps_flutter_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22 -version: 2.9.0 +version: 2.10.0 environment: sdk: ^3.2.3 diff --git a/packages/google_maps_flutter/google_maps_flutter_ios/test/google_maps_flutter_ios_test.dart b/packages/google_maps_flutter/google_maps_flutter_ios/test/google_maps_flutter_ios_test.dart index eff422240c1..a033db4f509 100644 --- a/packages/google_maps_flutter/google_maps_flutter_ios/test/google_maps_flutter_ios_test.dart +++ b/packages/google_maps_flutter/google_maps_flutter_ios/test/google_maps_flutter_ios_test.dart @@ -27,15 +27,6 @@ void main() { return (maps, api); } - Future sendPlatformMessage( - int mapId, String method, Map data) async { - final ByteData byteData = - const StandardMethodCodec().encodeMethodCall(MethodCall(method, data)); - await TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger - .handlePlatformMessage('plugins.flutter.dev/google_maps_ios_$mapId', - byteData, (ByteData? data) {}); - } - test('registers instance', () async { GoogleMapsFlutterIOS.registerWith(); expect(GoogleMapsFlutterPlatform.instance, isA()); @@ -437,24 +428,15 @@ void main() { test('markers send drag event to correct streams', () async { const int mapId = 1; - final Map jsonMarkerDragStartEvent = { - 'mapId': mapId, - 'markerId': 'drag-start-marker', - 'position': [1.0, 1.0] - }; - final Map jsonMarkerDragEvent = { - 'mapId': mapId, - 'markerId': 'drag-marker', - 'position': [1.0, 1.0] - }; - final Map jsonMarkerDragEndEvent = { - 'mapId': mapId, - 'markerId': 'drag-end-marker', - 'position': [1.0, 1.0] - }; + const String dragStartId = 'drag-start-marker'; + const String dragId = 'drag-marker'; + const String dragEndId = 'drag-end-marker'; + final PlatformLatLng fakePosition = + PlatformLatLng(latitude: 1.0, longitude: 1.0); final GoogleMapsFlutterIOS maps = GoogleMapsFlutterIOS(); - maps.ensureChannelInitialized(mapId); + final HostMapMessageHandler callbackHandler = + maps.ensureHandlerInitialized(mapId); final StreamQueue markerDragStartStream = StreamQueue(maps.onMarkerDragStart(mapId: mapId)); @@ -463,17 +445,82 @@ void main() { final StreamQueue markerDragEndStream = StreamQueue(maps.onMarkerDragEnd(mapId: mapId)); - await sendPlatformMessage( - mapId, 'marker#onDragStart', jsonMarkerDragStartEvent); - await sendPlatformMessage(mapId, 'marker#onDrag', jsonMarkerDragEvent); - await sendPlatformMessage( - mapId, 'marker#onDragEnd', jsonMarkerDragEndEvent); - - expect((await markerDragStartStream.next).value.value, - equals('drag-start-marker')); - expect((await markerDragStream.next).value.value, equals('drag-marker')); - expect((await markerDragEndStream.next).value.value, - equals('drag-end-marker')); + // Simulate messages from the native side. + callbackHandler.onMarkerDragStart(dragStartId, fakePosition); + callbackHandler.onMarkerDrag(dragId, fakePosition); + callbackHandler.onMarkerDragEnd(dragEndId, fakePosition); + + expect((await markerDragStartStream.next).value.value, equals(dragStartId)); + expect((await markerDragStream.next).value.value, equals(dragId)); + expect((await markerDragEndStream.next).value.value, equals(dragEndId)); + }); + + test('markers send tap events to correct stream', () async { + const int mapId = 1; + const String objectId = 'object-id'; + + final GoogleMapsFlutterIOS maps = GoogleMapsFlutterIOS(); + final HostMapMessageHandler callbackHandler = + maps.ensureHandlerInitialized(mapId); + + final StreamQueue stream = + StreamQueue(maps.onMarkerTap(mapId: mapId)); + + // Simulate message from the native side. + callbackHandler.onMarkerTap(objectId); + + expect((await stream.next).value.value, equals(objectId)); + }); + + test('circles send tap events to correct stream', () async { + const int mapId = 1; + const String objectId = 'object-id'; + + final GoogleMapsFlutterIOS maps = GoogleMapsFlutterIOS(); + final HostMapMessageHandler callbackHandler = + maps.ensureHandlerInitialized(mapId); + + final StreamQueue stream = + StreamQueue(maps.onCircleTap(mapId: mapId)); + + // Simulate message from the native side. + callbackHandler.onCircleTap(objectId); + + expect((await stream.next).value.value, equals(objectId)); + }); + + test('polygons send tap events to correct stream', () async { + const int mapId = 1; + const String objectId = 'object-id'; + + final GoogleMapsFlutterIOS maps = GoogleMapsFlutterIOS(); + final HostMapMessageHandler callbackHandler = + maps.ensureHandlerInitialized(mapId); + + final StreamQueue stream = + StreamQueue(maps.onPolygonTap(mapId: mapId)); + + // Simulate message from the native side. + callbackHandler.onPolygonTap(objectId); + + expect((await stream.next).value.value, equals(objectId)); + }); + + test('polylines send tap events to correct stream', () async { + const int mapId = 1; + const String objectId = 'object-id'; + + final GoogleMapsFlutterIOS maps = GoogleMapsFlutterIOS(); + final HostMapMessageHandler callbackHandler = + maps.ensureHandlerInitialized(mapId); + + final StreamQueue stream = + StreamQueue(maps.onPolylineTap(mapId: mapId)); + + // Simulate message from the native side. + callbackHandler.onPolylineTap(objectId); + + expect((await stream.next).value.value, equals(objectId)); }); testWidgets('cloudMapId is passed', (WidgetTester tester) async {