-
-
Notifications
You must be signed in to change notification settings - Fork 119
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
912ea92
commit 63535c8
Showing
11 changed files
with
3,379 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
unit ASC.Common; | ||
|
||
interface | ||
|
||
uses | ||
AndroidApi.JNI.GraphicsContentViewText, | ||
DW.MultiReceiver.Android; | ||
|
||
const | ||
cActionAppMessage = 'ACTION_APP_MESSAGE'; | ||
cActionServiceMessage = 'ACTION_SERVICE_MESSAGE'; | ||
cExtraMessageData = 'EXTRA_MESSAGE_DATA'; | ||
|
||
type | ||
TReceiveProc = reference to procedure(const Intent: JIntent); | ||
|
||
TLocalBroadcastReceiver = class(TMultiReceiver) | ||
private | ||
FActions: TArray<string>; | ||
FReceiveProc: TReceiveProc; | ||
protected | ||
procedure ConfigureActions; override; | ||
procedure Receive(context: JContext; intent: JIntent); override; | ||
public | ||
constructor Create(const AActions: TArray<string>; const AReceiveProc: TReceiveProc); | ||
end; | ||
|
||
TDataMessage = record | ||
DataType: Integer; | ||
Data: string; | ||
class function CreateCommandMessage(const ACommand: string): TDataMessage; static; | ||
class function CreateImageMessage(const ABase64: string): TDataMessage; static; | ||
class function CreateStringMessage(const AData: string): TDataMessage; static; | ||
procedure FromJSON(const AValue: string); | ||
function ToJSON: string; | ||
end; | ||
|
||
implementation | ||
|
||
uses | ||
System.JSON, | ||
Androidapi.Helpers, Androidapi.JNI.JavaTypes; | ||
|
||
{ TLocalBroadcastReceiver } | ||
|
||
constructor TLocalBroadcastReceiver.Create(const AActions: TArray<string>; const AReceiveProc: TReceiveProc); | ||
begin | ||
FActions := AActions; | ||
inherited Create(True); | ||
FReceiveProc := AReceiveProc; | ||
end; | ||
|
||
procedure TLocalBroadcastReceiver.ConfigureActions; | ||
var | ||
LAction: string; | ||
begin | ||
for LAction in FActions do | ||
IntentFilter.addAction(StringToJString(LAction)); | ||
end; | ||
|
||
procedure TLocalBroadcastReceiver.Receive(context: JContext; intent: JIntent); | ||
begin | ||
if Assigned(FReceiveProc) then | ||
FReceiveProc(intent); | ||
end; | ||
|
||
{ TDataMessage } | ||
|
||
class function TDataMessage.CreateCommandMessage(const ACommand: string): TDataMessage; | ||
begin | ||
Result.DataType := 2; | ||
Result.Data := ACommand; | ||
end; | ||
|
||
class function TDataMessage.CreateImageMessage(const ABase64: string): TDataMessage; | ||
begin | ||
Result.DataType := 3; | ||
Result.Data := ABase64; | ||
end; | ||
|
||
class function TDataMessage.CreateStringMessage(const AData: string): TDataMessage; | ||
begin | ||
Result.DataType := 1; | ||
Result.Data := AData; | ||
end; | ||
|
||
procedure TDataMessage.FromJSON(const AValue: string); | ||
var | ||
LJSON: TJSONValue; | ||
begin | ||
LJSON := TJSONObject.ParseJSONValue(AValue); | ||
if LJSON <> nil then | ||
try | ||
LJSON.TryGetValue('DataType', DataType); | ||
LJSON.TryGetValue('Data', Data); | ||
finally | ||
LJSON.Free; | ||
end; | ||
end; | ||
|
||
function TDataMessage.ToJSON: string; | ||
var | ||
LJSON: TJSONObject; | ||
begin | ||
LJSON := TJSONObject.Create; | ||
try | ||
LJSON.AddPair('DataType', DataType); | ||
LJSON.AddPair('Data', Data); | ||
Result := LJSON.ToJSON; | ||
finally | ||
LJSON.Free; | ||
end; | ||
end; | ||
|
||
end. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
program AndroidServiceCommsDemo; | ||
|
||
uses | ||
System.StartUpCopy, | ||
FMX.Forms, | ||
Unit1 in 'Unit1.pas' {Form1}, | ||
ASC.ServiceModule in 'Service\ASC.ServiceModule.pas' {ServiceModule: TAndroidService}; | ||
|
||
{$R *.res} | ||
|
||
begin | ||
Application.Initialize; | ||
Application.CreateForm(TForm1, Form1); | ||
Application.Run; | ||
end. |
1,586 changes: 1,586 additions & 0 deletions
1,586
Demos/AndroidServiceComms/AndroidServiceCommsDemo.dproj
Large diffs are not rendered by default.
Oops, something went wrong.
48 changes: 48 additions & 0 deletions
48
Demos/AndroidServiceComms/AndroidServiceCommsDemoGroup.groupproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | ||
<PropertyGroup> | ||
<ProjectGuid>{CD2F57EA-E050-4D85-A845-F4F206FA07DB}</ProjectGuid> | ||
</PropertyGroup> | ||
<ItemGroup> | ||
<Projects Include="AndroidServiceCommsDemo.dproj"> | ||
<Dependencies>Service\AndroidServiceCommsDemoService.dproj</Dependencies> | ||
</Projects> | ||
<Projects Include="Service\AndroidServiceCommsDemoService.dproj"> | ||
<Dependencies/> | ||
</Projects> | ||
</ItemGroup> | ||
<ProjectExtensions> | ||
<Borland.Personality>Default.Personality.12</Borland.Personality> | ||
<Borland.ProjectType/> | ||
<BorlandProject> | ||
<Default.Personality/> | ||
</BorlandProject> | ||
</ProjectExtensions> | ||
<Target Name="AndroidServiceCommsDemo" DependsOnTargets="AndroidServiceCommsDemoService"> | ||
<MSBuild Projects="AndroidServiceCommsDemo.dproj"/> | ||
</Target> | ||
<Target Name="AndroidServiceCommsDemo:Clean" DependsOnTargets="AndroidServiceCommsDemoService:Clean"> | ||
<MSBuild Projects="AndroidServiceCommsDemo.dproj" Targets="Clean"/> | ||
</Target> | ||
<Target Name="AndroidServiceCommsDemo:Make" DependsOnTargets="AndroidServiceCommsDemoService:Make"> | ||
<MSBuild Projects="AndroidServiceCommsDemo.dproj" Targets="Make"/> | ||
</Target> | ||
<Target Name="AndroidServiceCommsDemoService"> | ||
<MSBuild Projects="Service\AndroidServiceCommsDemoService.dproj"/> | ||
</Target> | ||
<Target Name="AndroidServiceCommsDemoService:Clean"> | ||
<MSBuild Projects="Service\AndroidServiceCommsDemoService.dproj" Targets="Clean"/> | ||
</Target> | ||
<Target Name="AndroidServiceCommsDemoService:Make"> | ||
<MSBuild Projects="Service\AndroidServiceCommsDemoService.dproj" Targets="Make"/> | ||
</Target> | ||
<Target Name="Build"> | ||
<CallTarget Targets="AndroidServiceCommsDemo;AndroidServiceCommsDemoService"/> | ||
</Target> | ||
<Target Name="Clean"> | ||
<CallTarget Targets="AndroidServiceCommsDemo:Clean;AndroidServiceCommsDemoService:Clean"/> | ||
</Target> | ||
<Target Name="Make"> | ||
<CallTarget Targets="AndroidServiceCommsDemo:Make;AndroidServiceCommsDemoService:Make"/> | ||
</Target> | ||
<Import Project="$(BDS)\Bin\CodeGear.Group.Targets" Condition="Exists('$(BDS)\Bin\CodeGear.Group.Targets')"/> | ||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
# Android Service Comms Demo | ||
|
||
## Description | ||
|
||
Demonstrates implementations of communication between an Android app, and a service used by the app. | ||
|
||
The demo uses two forms: | ||
|
||
1. Via service "binding", which is supported "out of the box" in Delphi, and appears in RAD Studio demos provided by Embarcadero | ||
2. Via local broadcasts | ||
|
||
Checking/unchecking the checkbox at the top of the demo determines which is used. | ||
|
||
There may be other means that can be used for communication, however the two above are simple, and work. | ||
|
||
There is a [ChatGPT conversation here](https://github.com/DelphiWorlds/HowTo/blob/main/ChatGPTConversations/AndroidBindServiceVsStartService.md) about the merits of using `bindService` (used in method 1) vs using `startService` (used in method 2) | ||
|
||
## Supported Delphi versions | ||
|
||
Delphi 12, Delphi 11.x. | ||
|
||
## Project Configuration | ||
|
||
If you are starting your own Android project that contains a service, [this is a recommended video](https://www.youtube.com/watch?v=0mD3WLK8FYc) to watch. It's from 2015, however the process is pretty much the same. The important things are that the application and service should be part of a project group, that you build the service at least once before using the `Add Service` menu item in Project Manager to add the service to the application. | ||
|
||
Communication method 2 (local broadcasts) requires the addition of the Kastri base library: `dw-kastri-base-2.0.0.jar` for Delphi 11.x and `dw-kastri-base-3.0.0.jar` for Delphi 12. These libraries are in the `Lib` folder of Kastri. Add it to the `Libraries` node under the Android platform in Project Manager. | ||
|
||
**Note**: | ||
|
||
Due to a bug in Delphi 11.3 **ONLY**, if you need to compile for Android 64-bit, you will need to either apply [this workaround](https://docs.code-kungfu.com/books/hotfix-113-alexandria/page/fix-jar-libraries-added-to-android-64-bit-platform-target-are-not-compiled) (which will apply to **all** projects), **OR** copy the jar file(s) to _another folder_, and add them to the Libraries node of the Android 64-bit target. (Adding the same `.jar` file(s) to Android 64-bit does _not_ work) | ||
|
||
## Service "binding" | ||
|
||
Service binding is achieved in the demo by creating an instance of `TLocalServiceConnection`, setting the `OnConnected` and `OnDisconnected` event handlers, and calling `BindService` (in the `ConnectButtonClick` handler). Once the `OnConnected` event occurs (handled by `ServiceConnectedHandler`) a reference to the service instance can be obtained, and its `OnSendData` event handler can be assigned. If this event is assigned, the service uses it to send data to the application. | ||
|
||
## Local Broadcasts | ||
|
||
If local broadcasts are used, the service needs to be started (via `ConnectButtonClick`). When the service starts (see `AndroidServiceStartCommand` in the service), a message is sent via local broadcast to notify the application that the service has started. Both the service and application use a local broadcast receiver to receive messages from each other. | ||
|
||
## Messages in the demo | ||
|
||
Messages are constructed using the `TDataMessage` record type (in the `ASC.Common` unit), which are serialized to/deserialized from JSON, so that the local broadcast can use a plain string in the extras of the intent being sent. | ||
|
||
Messages that are "commands" have a value of `2` for `DataType` and the `Data` is a plain string. Messages that are just a response have a value of `1`. Messages that contain image data (as a base64 string) have a value of `3` for `DataType` | ||
|
||
## Expanding beyond simple data | ||
|
||
As described above, the communication here contains very rudimentary information, but could be expanded to handle more complex data. | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
object ServiceModule: TServiceModule | ||
OnCreate = AndroidServiceCreate | ||
OnStartCommand = AndroidServiceStartCommand | ||
Height = 357 | ||
Width = 486 | ||
PixelsPerInch = 144 | ||
end |
117 changes: 117 additions & 0 deletions
117
Demos/AndroidServiceComms/Service/ASC.ServiceModule.pas
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
unit ASC.ServiceModule; | ||
|
||
interface | ||
|
||
uses | ||
System.SysUtils, | ||
System.Classes, | ||
System.Android.Service, | ||
AndroidApi.JNI.GraphicsContentViewText, | ||
Androidapi.JNI.Os, | ||
ASC.Common; | ||
|
||
type | ||
TSendDataEvent = procedure(Sender: TObject; const Data: string) of object; | ||
|
||
TServiceModule = class(TAndroidService) | ||
procedure AndroidServiceCreate(Sender: TObject); | ||
function AndroidServiceStartCommand(const Sender: TObject; const Intent: JIntent; Flags, StartId: Integer): Integer; | ||
private | ||
// FReceiver, ReceiverReceiveHandler and SendData are for when *not* using service "binding" | ||
FReceiver: TLocalBroadcastReceiver; | ||
FOnSendData: TSendDataEvent; | ||
function GetImageBase64: string; | ||
procedure ReceiverReceiveHandler(const AIntent: JIntent); | ||
procedure SendData(const AData: string); | ||
public | ||
// This method is made public only so that the application can call it direct when using service "binding" | ||
procedure ReceiveData(const AData: string); | ||
// OnSendData is for when using service "binding" | ||
property OnSendData: TSendDataEvent read FOnSendData write FOnSendData; | ||
end; | ||
|
||
var | ||
ServiceModule: TServiceModule; | ||
|
||
implementation | ||
|
||
{%CLASSGROUP 'FMX.Controls.TControl'} | ||
|
||
{$R *.dfm} | ||
|
||
uses | ||
Androidapi.Helpers, Androidapi.JNI.JavaTypes, Androidapi.JNI.App, | ||
System.Net.HttpClient, | ||
DW.Base64.Helpers, | ||
DW.Androidapi.JNI.AndroidX.LocalBroadcastManager; | ||
|
||
{ TServiceModule } | ||
|
||
procedure TServiceModule.AndroidServiceCreate(Sender: TObject); | ||
begin | ||
FReceiver := TLocalBroadcastReceiver.Create([cActionServiceMessage], ReceiverReceiveHandler); | ||
end; | ||
|
||
procedure TServiceModule.ReceiverReceiveHandler(const AIntent: JIntent); | ||
var | ||
LExtraName: JString; | ||
begin | ||
LExtraName := StringToJString(cExtraMessageData); | ||
if AIntent.hasExtra(LExtraName) then | ||
ReceiveData(JStringToString(AIntent.getStringExtra(LExtraName))); | ||
end; | ||
|
||
function TServiceModule.AndroidServiceStartCommand(const Sender: TObject; const Intent: JIntent; Flags, StartId: Integer): Integer; | ||
begin | ||
SendData(TDataMessage.CreateStringMessage('Service started').ToJSON); | ||
Result := TJService.JavaClass.START_STICKY; | ||
end; | ||
|
||
function TServiceModule.GetImageBase64: string; | ||
var | ||
LHTTP: THTTPClient; | ||
LResponse: IHTTPResponse; | ||
begin | ||
Result := ''; | ||
LHTTP := THTTPClient.Create; | ||
try | ||
LResponse := LHTTP.Get('https://upload.wikimedia.org/wikipedia/commons/thumb/1/15/Cat_August_2010-4.jpg/2880px-Cat_August_2010-4.jpg'); | ||
if LResponse.StatusCode = 200 then | ||
Result := TBase64Helper.Encode(LResponse.ContentStream); | ||
finally | ||
LHTTP.Free; | ||
end; | ||
end; | ||
|
||
procedure TServiceModule.ReceiveData(const AData: string); | ||
var | ||
LMessage: TDataMessage; | ||
begin | ||
LMessage.FromJSON(AData); | ||
case LMessage.DataType of | ||
2: | ||
begin | ||
if LMessage.Data.Equals('ping') then | ||
SendData(TDataMessage.CreateStringMessage('pong').ToJSON) | ||
else if LMessage.Data.Equals('image') then | ||
SendData(TDataMessage.CreateImageMessage(GetImageBase64).ToJSON); | ||
end; | ||
end; | ||
end; | ||
|
||
procedure TServiceModule.SendData(const AData: string); | ||
var | ||
LIntent: JIntent; | ||
begin | ||
// If FOnSendData is not assigned, then service "binding" is not being used | ||
if not Assigned(FOnSendData) then | ||
begin | ||
LIntent := TJIntent.JavaClass.init(StringToJString(cActionAppMessage)); | ||
LIntent.putExtra(StringToJString(cExtraMessageData), StringToJString(AData)); | ||
TJLocalBroadcastManager.JavaClass.getInstance(TAndroidHelper.Context).sendBroadcast(LIntent); | ||
end | ||
else | ||
FOnSendData(Self, AData); | ||
end; | ||
|
||
end. |
13 changes: 13 additions & 0 deletions
13
Demos/AndroidServiceComms/Service/AndroidServiceCommsDemoService.dpr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
program AndroidServiceCommsDemoService; | ||
|
||
uses | ||
System.Android.ServiceApplication, | ||
ASC.ServiceModule in 'ASC.ServiceModule.pas' {ServiceModule: TAndroidService}; | ||
|
||
{$R *.res} | ||
|
||
begin | ||
Application.Initialize; | ||
Application.CreateForm(TServiceModule, ServiceModule); | ||
Application.Run; | ||
end. |
Oops, something went wrong.