Облачные сообщения Google в Delphi XE5?

У меня есть приложение для Android, которое я собираюсь портировать в Delphi, но я не вижу способа взаимодействия с GCM. Я думаю, мне, возможно, придется запустить GCMBaseIntentService в java и взаимодействовать с общим объектом delphi?

Кроме того, я ищу способ сделать push-уведомления в приложении для Android Delphi Xe5.

Вы используете интерфейс Java с Delphi, используя JNI. JNI позволяет вам звонить в обоих направлениях: Java в Delphi или Delphi на Java. Таким образом, вы можете расширить свои приложения Delphi с помощью Java-classов .

Для реализации того, что вы хотите на Android, сделать это в Java будет более простым путем, поскольку некоторые доступные примеры делают именно то, что вы имеете в виду. Просто взгляните:

  • Внедрение Push Notification / Google Cloud Messaging для Android
  • Android * Клиентское приложение с использованием облачной службы оповещения
  • Github Gist – GCMIntentService.java

Чтобы взаимодействовать с Java JNI и Delphi, вы можете следить за подробными шагами, обеспечивая плавную связь между интерфейсом и передним концом приложения.

Если вы решите повторно использовать часть кода, не забудьте дать авторам соответствующие отзывы.

Я получил GCM, работая с Delphi, и я сделал образец компонента, чтобы заботиться о регистрации и получении сообщений GCM.

ПРИМЕЧАНИЕ . Это всего лишь грубый тестовый код, я не использую его в каком-либо реальном приложении (пока). Пожалуйста, не стесняйтесь изменять и улучшать, и если вы найдете ошибки, отправьте их обратно.

Большое спасибо Брайану Лонгу и его статье об услугах Android .

Получите идентификатор отправителя GCM (это ваш номер проекта из консоли gcm) и ваш идентификатор API GCM (создайте ключ для серверного приложения в консоли GCM), они вам понадобятся (см. Рисунки внизу).

Прежде всего, вам понадобится модифицированный файл classes.dex. Вы можете создать это, запустив файл bat bat Java в архиве, или вы можете использовать тот, который уже скомпилирован мной (также включен в архив).

Вы должны ДОБАВИТЬ новые classы.dex для развертывания Android и UNCHECK the embarcadero one:

развертывание classes.dex

Затем вам нужно отредактировать свой AndroidManifest.template.xml и добавить сразу после <%uses-permission%> :

 <%uses-permission%>  

и сразу после android:theme="%theme%">

        

В своем заявлении объявите блок gcmnotification:

 uses gcmnotification; 

а затем в вашей форме объявите переменную типа TGCMNotification и процедуру, которую вы связываете с событием TGCMNotification.OnReceiveGCMNotification:

 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; 

Поместите в SenderID свой номер проекта GCM. Чтобы зарегистрировать ваше приложение с помощью GCM, вызовите DoRegister:

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

Если DoRegister возвращает true (успешно зарегистрирован), gcmn.RegistrationID будет иметь уникальный идентификатор, необходимый для отправки сообщений на это устройство.

И вы получите сообщения в процедуре события:

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

.. и это ВСЕ, что вам нужно для получения. Круто, да? 🙂

Чтобы отправить, просто используйте 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; 

Затем я отправлю нужные вам единицы, просто сохранит их в одном месте с вашим приложением (или просто скачайте все это из ЗДЕСЬ ).

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; gcm: JGoogleCloudMessaging; begin if FAlreadyRegistered then result := true else begin if CheckPlayServicesSupport then begin gcm := GetGCMInstance; p := TJavaObjectArray.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) 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. 

Вот модифицированный файл AndroidManifest.template.xml

      <%uses-permission%>                      

И полный источник для тестового приложения (он отправит и получит сообщение GCM):

 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. 

GCMReceiver.java, который вам нужно скомпилировать и добавить в class.dex, это:

 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); } } 

И ЗДЕСЬ архив zip с источником.

Если у вас возникли проблемы с его работой, возможно, что-то не настроено прямо на консоли GCM.

Вот что вам нужно от консоли GCM:

Номер проекта (вы используете это при регистрации в GCM, поместите его в TGCMNotification.SenderID перед вызовом DoRegister).

Номер проекта

API ID вы будете использовать это для отправки сообщений зарегистрированным устройствам.

ИД API

Я думаю, что создание моста для Java может быть хорошей идеей. Взгляните на этот пост о том, как это сделать. И здесь вы можете найти учебное пособие по внедрению GCM на стороне клиента на Java.

Я рад видеть, что дельфи развивается и адаптируется к текущим потребностям. Сообщение сделало меня любопытством, поэтому я немного огляделся, и я наткнулся на эти ресурсы:

сообщение форума на embarcadero, которое рекомендует использовать datasnap для решения проблемы связи между GCM и стороной delphi.

Надеюсь, это поможет кому-то.

  • BitmapFactory.decodeResource возвращает измененный битмап в Android 2.2 и неизменный Bitmap в Android 1.6
  • Проблемы с импортом проекта в Android Studio в отношении ActionBarSherlock
  • Изменение библиотеки OpenSSL в приложении Android для HttpClient
  • Не удалось открыть Android 6.0: EACCES (Permission denied)
  • Как я могу обрезать bitmap для ImageView?
  • Android-аудио FFT для получения определенной величины частоты с использованием аудиозаписей
  • Есть ли на установке событие в android?
  • Отладка Android InetAddress.isReachable
  • Что делает оператор «+ =» в Java?
  • Какой самый простой способ получить текущий день недели в Android?
  • Печатать массив без скобок и запятых
  • Давайте будем гением компьютера.