25

I've got an android app that I am thinking about porting to Delphi but I can't see a way to interface with GCM. I am thinking I would possibly have to run the GCMBaseIntentService in java and interface with the delphi shared object?

Alternatively, I am looking for a way to do push notifications in a Delphi Xe5 android app.

Avanz
  • 7,466
  • 23
  • 54
FerretDriver
  • 381
  • 4
  • 11
  • I'm posting the registration information to some processing scripts on a web server. When I need to communicate to GCM I have a server side process which handles that which is in Delphi. – FerretDriver Sep 17 '13 at 17:28

4 Answers4

20

You interface Java with Delphi using JNI. The JNI allows you to call in both directions: Java to Delphi or Delphi to Java. So you can extend your Delphi applications with Java classes.

For implementing what you want on Android, doing it in Java will be the easier path to follow, as some available examples do exactly what you have in mind. Just have a look:

To interface Java JNI and Delphi, you can follow detailed steps, thus allowing a smooth communication between front end and back end of your application.

If you decide to re-use some of the code, remember to give appropriate credit to the authors.

Avanz
  • 7,466
  • 23
  • 54
  • A little too broad, but I will add an upvote and when the bounty period expires, the top voted answer should get half of it. – RichardTheKiwi Jan 27 '14 at 22:32
  • That smooth communication article seems to be about 13 years old. I hope there are more "native" ways to do it in Delphi XE5. – TLama Jan 29 '14 at 23:09
  • 1
    @TLama - for a more trendy "native" flavour, have a look on Embarcadero alternative: http://www.embarcadero.com/press-releases/embarcadero-launches-rad-studio-xe5-with-android-and-ios-support – Avanz Jan 30 '14 at 02:43
16

I got GCM working with Delphi and I made a sample component to take care of registering and receiving the GCM messages.

NOTE: This is just a rough test code, I'm not using it in any real application (yet). Please fell free to modify and improve, and if you find bugs, please post back.

Big thanks to Brian Long and his article on Android Services.

Get your GCM sender ID (it's your Project Number from gcm console) and your GCM API id (create a key for server application in GCM console), you'll need them (see the pictures at the bottom).

First of all, you need a modified classes.dex file. You can create this by running the bat file Java dir in the archive, or you can use the one that is already compiled by me (also included in the archive).

You have to ADD the new classes.dex to your Android Deployment and UNCHECK the embarcadero one:

classes.dex deployment

Then, you need to edit your AndroidManifest.template.xml and add right after <%uses-permission%>:

<%uses-permission%>
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />

and right after android:theme="%theme%">

    <receiver
        android:name="com.ioan.delphi.GCMReceiver"
        android:permission="com.google.android.c2dm.permission.SEND" >
        <intent-filter>
            <action android:name="com.google.android.c2dm.intent.RECEIVE" />
            <category android:name="%package%" />
        </intent-filter>
    </receiver>

In your application, declare the gcmnotification unit:

uses
  gcmnotification;

and then in your form declare a variable of the TGCMNotification type and a procedure that you will link to the TGCMNotification.OnReceiveGCMNotification event:

type
  TForm8 = class(TForm)
    //....
  private
    { Private declarations }
  public
    { Public declarations }
    gcmn: TGCMNotification;
    procedure OnNotification(Sender: TObject; ANotification: TGCMNotificationMessage);
  end;


procedure TForm8.FormCreate(Sender: TObject);
begin
   gcmn := TGCMNotification.Create(self);
   gcmn.OnReceiveGCMNotification := OnNotification;
end;

Put in the SenderID your GCM Project Number. To register you APP with GCM, call DoRegister:

procedure TForm8.Button1Click(Sender: TObject);
begin
  gcmn.SenderID := YOUR_GCM_SENDERID;
  if gcmn.DoRegister then
    Toast('Successfully registered with GCM.');
end;

If the DoRegister returns true (successfully registered), gcmn.RegistrationID will have the unique ID you need for sending messages to this device.

And you'll receive messages in event procedure:

procedure TForm8.OnNotification(Sender: TObject;  ANotification: TGCMNotificationMessage);
begin
  Memo1.Lines.Add('Received: ' + ANotification.Body);
end;

.. and that's ALL you need it for Receiving. Cool, huh? :-)

To send, just use TIdHttp:

procedure TForm8.Button2Click(Sender: TObject);
const
  sendUrl = 'https://android.googleapis.com/gcm/send';
var
  Params: TStringList;
  AuthHeader: STring;
  idHTTP: TIDHTTP;
  SSLIOHandler: TIdSSLIOHandlerSocketOpenSSL;
begin
  idHTTP := TIDHTTP.Create(nil);
  try
    SslIOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
    idHTTP.IOHandler := SSLIOHandler;
    idHTTP.HTTPOptions := [];
    Params := TStringList.Create;
    try
      Params.Add('registration_id='+ gcmn.RegistrationID);
      Params.Values['data.message'] := 'test: ' + FormatDateTime('yy-mm-dd hh:nn:ss', Now);
      idHTTP.Request.Host := sendUrl;
      AuthHeader := 'Authorization: key=' + YOUR_API_ID;
      idHTTP.Request.CustomHeaders.Add(AuthHeader);
      IdHTTP.Request.ContentType := 'application/x-www-form-urlencoded;charset=UTF-8';
      Memo1.Lines.Add('Send result: ' + idHTTP.Post(sendUrl, Params));
    finally
      Params.Free;
    end;
  finally
    FreeAndNil(idHTTP);
  end;
end;

Next I'm going to post the units you need, just save them in the same place with your application (or just download the whole thing from HERE).

gcmnotification.pas

unit gcmnotification;

interface
{$IFDEF ANDROID}
uses
  System.SysUtils,
  System.Classes,
  FMX.Helpers.Android,
  Androidapi.JNI.PlayServices,
  Androidapi.JNI.GraphicsContentViewText,
  Androidapi.JNIBridge,
  Androidapi.JNI.JavaTypes;

type
  TGCMNotificationMessageKind = (nmMESSAGE_TYPE_MESSAGE, nmMESSAGE_TYPE_DELETED, nmMESSAGE_TYPE_SEND_ERROR);

  { Discription of notification for Notification Center }
  TGCMNotificationMessage = class (TPersistent)
  private
    FKind: TGCMNotificationMessageKind;
    FSender: string;
    FWhat: integer;
    FBody: string;
  protected
    procedure AssignTo(Dest: TPersistent); override;
  public
    { Unique identificator for determenation notification in Notification list }
    property Kind: TGCMNotificationMessageKind read FKind write FKind;
    property Sender: string read FSender write FSender;
    property What: integer read FWhat write FWhat;
    property Body: string read FBody write FBody;
    constructor Create;
  end;

  TOnReceiveGCMNotification = procedure (Sender: TObject; ANotification: TGCMNotificationMessage) of object;

  TGCMNotification = class(TComponent)
  strict private
    { Private declarations }
    FRegistrationID: string;
    FSenderID: string;
    FOnReceiveGCMNotification: TOnReceiveGCMNotification;
    FReceiver: JBroadcastReceiver;
    FAlreadyRegistered: boolean;
    function CheckPlayServicesSupport: boolean;
  protected
    { Protected declarations }
  public
    { Public declarations }
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    function DoRegister: boolean;
    function GetGCMInstance: JGoogleCloudMessaging;
  published
    { Published declarations }
    property SenderID: string read FSenderID write FSenderID;
    property RegistrationID: string read FRegistrationID write FRegistrationID;
    property OnReceiveGCMNotification: TOnReceiveGCMNotification read FOnReceiveGCMNotification write FOnReceiveGCMNotification;
  end;

{$ENDIF}
implementation
{$IFDEF ANDROID}
uses
  uGCMReceiver;


{ TGCMNotification }
function TGCMNotification.CheckPlayServicesSupport: boolean;
var
  resultCode: integer;
begin
  resultCode := TJGooglePlayServicesUtil.JavaClass.isGooglePlayServicesAvailable(SharedActivity);
  result := (resultCode = TJConnectionResult.JavaClass.SUCCESS);
end;

constructor TGCMNotification.Create(AOwner: TComponent);
var
  Filter: JIntentFilter;
begin
  inherited;
  Filter := TJIntentFilter.Create;
  FReceiver := TJGCMReceiver.Create(Self);
  SharedActivity.registerReceiver(FReceiver, Filter);
  FAlreadyRegistered := false;
end;

destructor TGCMNotification.Destroy;
begin
  SharedActivity.unregisterReceiver(FReceiver);
  FReceiver := nil;
  inherited;
end;

function TGCMNotification.DoRegister: boolean;
var
  p: TJavaObjectArray<JString>;
  gcm: JGoogleCloudMessaging;
begin
  if FAlreadyRegistered then
    result := true
  else
  begin
    if CheckPlayServicesSupport then
    begin
      gcm := GetGCMInstance;
      p := TJavaObjectArray<JString>.Create(1);
      p.Items[0] := StringToJString(FSenderID);
      FRegistrationID := JStringToString(gcm.register(p));
      FAlreadyRegistered := (FRegistrationID <> '');
      result := FAlreadyRegistered;
    end
    else
      result := false;
  end;
end;

function TGCMNotification.GetGCMInstance: JGoogleCloudMessaging;
begin
  result := TJGoogleCloudMessaging.JavaClass.getInstance(SharedActivity.getApplicationContext);
end;

{ TGCMNotificationMessage }

procedure TGCMNotificationMessage.AssignTo(Dest: TPersistent);
var
  DestNotification: TGCMNotificationMessage;
begin
  if Dest is TGCMNotificationMessage then
  begin
    DestNotification := Dest as TGCMNotificationMessage;
    DestNotification.Kind := Kind;
    DestNotification.What := What;
    DestNotification.Sender := Sender;
    DestNotification.Body := Body;
  end
  else
    inherited AssignTo(Dest);
end;

constructor TGCMNotificationMessage.Create;
begin
  Body := '';
end;
{$ENDIF}
end.

uGCMReceiver.pas

unit uGCMReceiver;

interface
{$IFDEF ANDROID}
uses
  FMX.Types,
  Androidapi.JNIBridge,
  Androidapi.JNI.GraphicsContentViewText,
  gcmnotification;

type
  JGCMReceiverClass = interface(JBroadcastReceiverClass)
  ['{9D967671-9CD8-483A-98C8-161071CE7B64}']
    {Methods}
  end;

  [JavaSignature('com/ioan/delphi/GCMReceiver')]
  JGCMReceiver = interface(JBroadcastReceiver)
  ['{4B30D537-5221-4451-893D-7916ED11CE1F}']
    {Methods}
  end;


  TJGCMReceiver = class(TJavaGenericImport<JGCMReceiverClass, JGCMReceiver>)
  private
    FOwningComponent: TGCMNotification;
  protected
    constructor _Create(AOwner: TGCMNotification);
  public
    class function Create(AOwner: TGCMNotification): JGCMReceiver;
    procedure OnReceive(Context: JContext; ReceivedIntent: JIntent);
  end;
{$ENDIF}
implementation
{$IFDEF ANDROID}
uses
  System.Classes,
  System.SysUtils,
  FMX.Helpers.Android,
  Androidapi.NativeActivity,
  Androidapi.JNI,
  Androidapi.JNI.JavaTypes,
  Androidapi.JNI.Os,
  Androidapi.JNI.PlayServices;

{$REGION 'JNI setup code and callback'}
var
  GCMReceiver: TJGCMReceiver;
  ARNContext: JContext;
  ARNReceivedIntent: JIntent;

procedure GCMReceiverOnReceiveThreadSwitcher;
begin
  Log.d('+gcmReceiverOnReceiveThreadSwitcher');
  Log.d('Thread: Main: %.8x, Current: %.8x, Java:%.8d (%2:.8x)',
    [MainThreadID, TThread.CurrentThread.ThreadID,
    TJThread.JavaClass.CurrentThread.getId]);
  GCMReceiver.OnReceive(ARNContext,ARNReceivedIntent );
  Log.d('-gcmReceiverOnReceiveThreadSwitcher');
end;

//This is called from the Java activity's onReceiveNative() method
procedure GCMReceiverOnReceiveNative(PEnv: PJNIEnv; This: JNIObject; JNIContext, JNIReceivedIntent: JNIObject); cdecl;
begin
  Log.d('+gcmReceiverOnReceiveNative');
  Log.d('Thread: Main: %.8x, Current: %.8x, Java:%.8d (%2:.8x)',
    [MainThreadID, TThread.CurrentThread.ThreadID,
    TJThread.JavaClass.CurrentThread.getId]);
  ARNContext := TJContext.Wrap(JNIContext);
  ARNReceivedIntent := TJIntent.Wrap(JNIReceivedIntent);
  Log.d('Calling Synchronize');
  TThread.Synchronize(nil, GCMReceiverOnReceiveThreadSwitcher);
  Log.d('Synchronize is over');
  Log.d('-gcmReceiverOnReceiveNative');
end;

procedure RegisterDelphiNativeMethods;
var
  PEnv: PJNIEnv;
  ReceiverClass: JNIClass;
  NativeMethod: JNINativeMethod;
begin
  Log.d('Starting the GCMReceiver JNI stuff');

  PEnv := TJNIResolver.GetJNIEnv;

  Log.d('Registering interop methods');

  NativeMethod.Name := 'gcmReceiverOnReceiveNative';
  NativeMethod.Signature := '(Landroid/content/Context;Landroid/content/Intent;)V';
  NativeMethod.FnPtr := @GCMReceiverOnReceiveNative;

  ReceiverClass := TJNIResolver.GetJavaClassID('com.ioan.delphi.GCMReceiver');

  PEnv^.RegisterNatives(PEnv, ReceiverClass, @NativeMethod, 1);

  PEnv^.DeleteLocalRef(PEnv, ReceiverClass);
end;
{$ENDREGION}

{ TActivityReceiver }

constructor TJGCMReceiver._Create(AOwner: TGCMNotification);
begin
  inherited;
  FOwningComponent := AOwner;
  Log.d('TJGCMReceiver._Create constructor');
end;

class function TJGCMReceiver.Create(AOwner: TGCMNotification): JGCMReceiver;
begin
  Log.d('TJGCMReceiver.Create class function');
  Result := inherited Create;
  GCMReceiver := TJGCMReceiver._Create(AOwner);
end;

procedure TJGCMReceiver.OnReceive(Context: JContext;  ReceivedIntent: JIntent);
var
  extras: JBundle;
  gcm: JGoogleCloudMessaging;
  messageType: JString;
  noti: TGCMNotificationMessage;
begin
  if Assigned(FOwningComponent.OnReceiveGCMNotification) then
  begin
    noti := TGCMNotificationMessage.Create;
    try
      Log.d('Received a message!');
      extras := ReceivedIntent.getExtras();
      gcm := FOwningComponent.GetGCMInstance;
      // The getMessageType() intent parameter must be the intent you received
      // in your BroadcastReceiver.
      messageType := gcm.getMessageType(ReceivedIntent);
      if not extras.isEmpty() then
      begin
          {*
           * Filter messages based on message type. Since it is likely that GCM will be
           * extended in the future with new message types, just ignore any message types you're
           * not interested in, or that you don't recognize.
           *}
          if TJGoogleCloudMessaging.JavaClass.MESSAGE_TYPE_SEND_ERROR.equals(messageType) then
          begin
            // It's an error.
            noti.Kind := TGCMNotificationMessageKind.nmMESSAGE_TYPE_SEND_ERROR;
            FOwningComponent.OnReceiveGCMNotification(Self, noti);
          end
          else
          if TJGoogleCloudMessaging.JavaClass.MESSAGE_TYPE_DELETED.equals(messageType) then
          begin
            // Deleted messages on the server.
            noti.Kind := TGCMNotificationMessageKind.nmMESSAGE_TYPE_DELETED;
            FOwningComponent.OnReceiveGCMNotification(Self, noti);
          end
          else
          if TJGoogleCloudMessaging.JavaClass.MESSAGE_TYPE_MESSAGE.equals(messageType) then
          begin
            // It's a regular GCM message, do some work.
            noti.Kind := TGCMNotificationMessageKind.nmMESSAGE_TYPE_MESSAGE;
            noti.Sender := JStringToString(extras.getString(StringToJString('sender')));
            noti.What := StrToIntDef(JStringToString(extras.getString(StringToJString('what'))), 0);
            noti.Body := JStringToString(extras.getString(StringToJString('message')));
            FOwningComponent.OnReceiveGCMNotification(Self, noti);
          end;
      end;
    finally
      noti.Free;
    end;
  end;
end;

initialization
  RegisterDelphiNativeMethods
{$ENDIF}
end.

Here is the modified AndroidManifest.template.xml

<?xml version="1.0" encoding="utf-8"?>
<!-- BEGIN_INCLUDE(manifest) -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="%package%"
        android:versionCode="%versionCode%"
        android:versionName="%versionName%">

    <!-- This is the platform API where NativeActivity was introduced. -->
    <uses-sdk android:minSdkVersion="%minSdkVersion%" />
<%uses-permission%>
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />

    <application android:persistent="%persistent%" 
        android:restoreAnyVersion="%restoreAnyVersion%" 
        android:label="%label%" 
        android:installLocation="%installLocation%" 
        android:debuggable="%debuggable%" 
        android:largeHeap="%largeHeap%"
        android:icon="%icon%"
        android:theme="%theme%">
        <receiver
            android:name="com.ioan.delphi.GCMReceiver"
            android:permission="com.google.android.c2dm.permission.SEND" >
            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
                <category android:name="%package%" />
            </intent-filter>
        </receiver>
        <!-- Our activity is a subclass of the built-in NativeActivity framework class.
             This will take care of integrating with our NDK code. -->
        <activity android:name="com.embarcadero.firemonkey.FMXNativeActivity"
                android:label="%activityLabel%"
                android:configChanges="orientation|keyboardHidden">
            <!-- Tell NativeActivity the name of our .so -->
            <meta-data android:name="android.app.lib_name"
                android:value="%libNameValue%" />
            <intent-filter>  
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter> 
        </activity>
        <receiver android:name="com.embarcadero.firemonkey.notifications.FMXNotificationAlarm" />
    </application>
</manifest>   
<!-- END_INCLUDE(manifest) -->

And the full source for the test application (it will send and receive a GCM message):

unit testgcmmain;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
  FMX.StdCtrls, FMX.Layouts, FMX.Memo, IdBaseComponent, IdComponent, IdTCPConnection,
  IdTCPClient, IdHTTP, IdIOHandler, IdIOHandlerSocket, IdIOHandlerStack, IdSSL, IdSSLOpenSSL,
  gcmnotification;

type
  TForm8 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    gcmn: TGCMNotification;
    procedure OnNotification(Sender: TObject; ANotification: TGCMNotificationMessage);
  end;

const
  YOUR_GCM_SENDERID = '1234567890';
  YOUR_API_ID = 'abc1234567890';

var
  Form8: TForm8;

implementation

{$R *.fmx}

procedure TForm8.FormCreate(Sender: TObject);
begin
   gcmn := TGCMNotification.Create(self);
   gcmn.OnReceiveGCMNotification := OnNotification;
end;

procedure TForm8.FormDestroy(Sender: TObject);
begin
  FreeAndNil(gcmn);
end;

procedure TForm8.Button1Click(Sender: TObject);
begin
  // register with the GCM 
  gcmn.SenderID := YOUR_GCM_SENDERID;
  if gcmn.DoRegister then
    Memo1.Lines.Add('Successfully registered with GCM.');
end;

procedure TForm8.OnNotification(Sender: TObject;  ANotification: TGCMNotificationMessage);
begin
  // you just received a message!
  if ANotification.Kind = TGCMNotificationMessageKind.nmMESSAGE_TYPE_MESSAGE then
    Memo1.Lines.Add('Received: ' + ANotification.Body);
end;

// send a message
procedure TForm8.Button2Click(Sender: TObject);
const
  sendUrl = 'https://android.googleapis.com/gcm/send';
var
  Params: TStringList;
  AuthHeader: STring;
  idHTTP: TIDHTTP;
  SSLIOHandler: TIdSSLIOHandlerSocketOpenSSL;
begin
  idHTTP := TIDHTTP.Create(nil);
  try
    SslIOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
    idHTTP.IOHandler := SSLIOHandler;
    idHTTP.HTTPOptions := [];
    Params := TStringList.Create;
    try
      Params.Add('registration_id='+ gcmn.RegistrationID);
      // you can send the data with a payload, in my example the server will accept 
      // data.message = the message you want to send
      // data.sender = some sender info
      // data.what = an integer  (aka "message type")
      // you can put any payload in the data, data.score, data.blabla... 
      // just make  sure you modify the code in my component to handle it 
      Params.Values['data.message'] := 'test: ' + FormatDateTime('yy-mm-dd hh:nn:ss', Now);
      idHTTP.Request.Host := sendUrl;
      AuthHeader := 'Authorization: key=' + YOUR_API_ID;
      idHTTP.Request.CustomHeaders.Add(AuthHeader);
      IdHTTP.Request.ContentType := 'application/x-www-form-urlencoded;charset=UTF-8';
      Memo1.Lines.Add('Send result: ' + idHTTP.Post(sendUrl, Params));
    finally
      Params.Free;
    end;
  finally
    FreeAndNil(idHTTP);
  end;
end;

end.

The GCMReceiver.java that you need to compile and add it to the classes.dex is:

package com.ioan.delphi;

import android.content.BroadcastReceiver;
import android.content.Intent;
import android.content.Context;
import android.util.Log;

public class GCMReceiver extends BroadcastReceiver
{
    static final String TAG = "GCMReceiver";

    public native void gcmReceiverOnReceiveNative(Context context, Intent receivedIntent);

    @Override
    public void onReceive(Context context, Intent receivedIntent)
    {
        Log.d(TAG, "onReceive");
        gcmReceiverOnReceiveNative(context, receivedIntent);
    }
}

And HERE is the zip archive with the source.

If you have trouble making it work, it's probably something not configured right in you GCM console.

Here is what you need from you GCM console:

Project number (you use this when you register with GCM, place it into TGCMNotification.SenderID before calling DoRegister).

Project number

API ID you'll use this to send messages to the registered devices.

API ID

ioan ghip
  • 1,202
  • 2
  • 16
  • 27
  • may I suggest you to use quotes around path parameters in your batch file? The var %PROJ_DIR% (*from where the script is executing*) can contain spaces. – naXa stands with Ukraine Mar 29 '14 at 20:17
  • @Touregsys I don't have xe6, but if you describe your problem maybe I can help. – ioan ghip Sep 30 '14 at 18:45
  • @ioan app worked on real device. but on emulator app doesnt open. another problem i can receive message from gcm but if i touch the tmemo or tedit the app close. I dont understand why –  Oct 01 '14 at 10:59
  • Is there any way to get activity context from java code? – liquid_code Dec 16 '15 at 08:59
  • 1
    Anyone get this running in Delphi 10? I see an XE 8 example but running it in Seattle doesnt seem to work. – FerretDriver Feb 24 '16 at 20:11
  • @FerretDriver I found this helpful (http://delphi.org/2014/12/mobile-push-notifications-without-a-baas/) but I'm still running into a problem with the GCMReciever.Jar (I'm getting a message about a Service tag missing the attribute "name"). Also in Seattle. Same error? – BIBD Feb 24 '16 at 21:46
  • 1
    @BIBD I got GCM running in Delphi (I already had a android app with GCM so I had very little setup), it is much simpler than the above. See my answer here, I link to the original answer with the one modification needed to get it to work. : http://stackoverflow.com/questions/35590533/gcm-in-delphi-seattle-without-baas/ – FerretDriver Feb 25 '16 at 02:27
0

I think that creating a bridge to Java could be a good idea. Take a look at this post about how to do it. And here you can find a tutorial for implementing client side GCM in Java.

Ilya Gazman
  • 31,250
  • 24
  • 137
  • 216
0

I am glad to see delphi is evolving and adapting to current needs. The post made me curios so I looked around a little bit so I came across these resources:

forum post on embarcadero that recommends using datasnap to solve the communication issue between GCM and the delphi side.

I hope this will help somebody.

Olimpiu POP
  • 5,001
  • 4
  • 34
  • 49