androidgoogle-mapsdelphifiremonkey

How to hide "Navigation" and "GPS pointer" buttons after clicking a marker on Android Google Maps in FireMonkey Framework


The "Navigation" and "GPS pointer" buttons are being shown at the right bottom of the component after clicking on a marker. The customer would like to prevent these buttons from being shown in their app.

The application is being build with Delphi 12.1 and the default TMapView component available in the FireMonkey Framework is being used.

Marker on Google maps clicked and buttons are being shown at the right bottom

We found that it is possible to disable this MapToolBar via map.getUiSettings().setMapToolbarEnabled(false);

Android google maps marker disable navigation option

But we could not find anyway to do this with the default TMapView.

After searching in the FireMonkey Framework code, we found the JGoogleMap interface in Androidapi.JNI.PlayServices.Maps unit. This interface has the required methods as described above. So next step is to find where this interface is being used. An instance of this interface is internally used in the TAndroidMapView implementation. We assume that an instance of this class is being used in the MapView component. But no possibility to access this instance from outside the class implementation.

We tried using Rtti to access this instance but that did not work

class function TFrameAndroidViewMap.DisableMapToolBarRtti(const MapView: TMapView): Boolean;
begin
  Result := False;

  var RttiContext := TRttiContext.Create;
  try
    var MapViewControlRttiType := RttiContext.GetType(MapView.ClassType);

    var FieldMapView: TRttiField;
    if not TryGetRttiFieldByName(MapViewControlRttiType, 'FMapView', FieldMapView) then
      Exit;

    var FieldMapViewValue := FieldMapView.GetValue(MapView);

    var MapViewInstance: TMapViewBase;
    if not FieldMapViewValue.TryAsType<TMapViewBase>(MapViewInstance) then
      Exit;

    var MapViewInstanceClassType := MapViewInstance.ClassType;
    var MapViewInstanceClassName := MapViewInstance.ClassName;
    var MapViewInstanceRttiType := RttiContext.GetType(MapViewInstanceClassType);

    var FieldGoogleMap: TRttiField;
    if not TryGetRttiFieldByName(MapViewInstanceRttiType, 'FGoogleMap', FieldGoogleMap) then
      Exit;

    var GoogleMapFieldValue := FieldGoogleMap.GetValue(MapViewInstance);

    var GoogleMapField: JGoogleMap;
    if GoogleMapFieldValue.TryAsType<JGoogleMap>(GoogleMapField) then
    begin
      GoogleMapField.getUiSettings().setMapToolbarEnabled(False);
      Result := True;
    end;
  finally
    RttiContext.Free;
  end;
end;

Solution

  • You actually can access the underlying Android references, however not the JGoogleMap reference, at least not directly. You can however access the JMapView reference (actually, JMapViewWithGestures, but it's just a descendant of JMapView) by using the Supports method on the TMapView. From there, the getMapAsync method can be called to obtain the JGoogleMap reference. Long story short - here's an example:

    unit Unit1;
    
    interface
    
    uses
      System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
      FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Maps,
      Androidapi.JNI.PlayServices.Maps;
    
    type
      TForm1 = class(TForm)
        MapView1: TMapView;
        procedure FormCreate(Sender: TObject);
      private
        FMap: JGoogleMap;
        FMapReadyCallback: JOnMapReadyCallback;
        procedure MapReady(AMap: JGoogleMap);
      public
        { Public declarations }
      end;
    
    var
      Form1: TForm1;
    
    implementation
    
    {$R *.fmx}
    
    uses
      Androidapi.JNIBridge;
    
    type
      TMapReadyCallback = class(TJavaLocal, JOnMapReadyCallback)
      private
        FCallback: TProc<JGoogleMap>;
      public
        { JOnMapReadyCallback }
        procedure onMapReady(googleMap: JGoogleMap); cdecl;
      public
        constructor Create(const ACallback: TProc<JGoogleMap>);
      end;
    
    { TMapReadyCallback }
    
    constructor TMapReadyCallback.Create(const ACallback: TProc<JGoogleMap>);
    begin
      inherited Create;
      FCallback := ACallback;
    end;
    
    procedure TMapReadyCallback.onMapReady(googleMap: JGoogleMap);
    begin
      FCallback(googleMap);
    end;
    
    { TForm1 }
    
    procedure TForm1.FormCreate(Sender: TObject);
    var
      LMapView: JMapView;
    begin
      if Supports(MapView1, JMapView, LMapView) then
      begin
        FMapReadyCallback := TMapReadyCallback.Create(MapReady);
        LMapView.getMapAsync(FMapReadyCallback);
      end;
    end;
    
    procedure TForm1.MapReady(AMap: JGoogleMap);
    begin
      FMap := AMap;
      FMap.getUiSettings.setMapToolbarEnabled(False);
    end;
    
    end.
    

    Explanation:

    getMapAsync as the name implies, is an asynchronous call that takes a reference to a callback where the onMapReady is called when the JGoogleMap reference becomes available. The TMapReadyCallback class implements JOnMapReadyCallback that has this method. It in turn calls the callback method MapReady which was passed to the class in the constructor.

    In MapReady you now have access to the JGoogleMap reference and can call the methods on it, as per the example.