Skip to content

Commit 29d97f7

Browse files
committed
🚧 Android Tests
1 parent e4f41fe commit 29d97f7

14 files changed

+2784
-0
lines changed

samples/Examples.Android.groupproj

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
2+
<PropertyGroup>
3+
<ProjectGuid>{C1E18000-47E5-4585-9778-A232A7BBE0BF}</ProjectGuid>
4+
</PropertyGroup>
5+
<ItemGroup>
6+
<Projects Include="android\NtfyAndroid.dproj">
7+
<Dependencies>android\ntfy-service\NtfyAndroidService.dproj</Dependencies>
8+
</Projects>
9+
<Projects Include="android\ntfy-service\NtfyAndroidService.dproj">
10+
<Dependencies/>
11+
</Projects>
12+
</ItemGroup>
13+
<ProjectExtensions>
14+
<Borland.Personality>Default.Personality.12</Borland.Personality>
15+
<Borland.ProjectType/>
16+
<BorlandProject>
17+
<Default.Personality/>
18+
</BorlandProject>
19+
</ProjectExtensions>
20+
<Target Name="NtfyAndroid" DependsOnTargets="NtfyAndroidService">
21+
<MSBuild Projects="android\NtfyAndroid.dproj"/>
22+
</Target>
23+
<Target Name="NtfyAndroid:Clean" DependsOnTargets="NtfyAndroidService:Clean">
24+
<MSBuild Projects="android\NtfyAndroid.dproj" Targets="Clean"/>
25+
</Target>
26+
<Target Name="NtfyAndroid:Make" DependsOnTargets="NtfyAndroidService:Make">
27+
<MSBuild Projects="android\NtfyAndroid.dproj" Targets="Make"/>
28+
</Target>
29+
<Target Name="NtfyAndroidService">
30+
<MSBuild Projects="android\ntfy-service\NtfyAndroidService.dproj"/>
31+
</Target>
32+
<Target Name="NtfyAndroidService:Clean">
33+
<MSBuild Projects="android\ntfy-service\NtfyAndroidService.dproj" Targets="Clean"/>
34+
</Target>
35+
<Target Name="NtfyAndroidService:Make">
36+
<MSBuild Projects="android\ntfy-service\NtfyAndroidService.dproj" Targets="Make"/>
37+
</Target>
38+
<Target Name="Build">
39+
<CallTarget Targets="NtfyAndroid;NtfyAndroidService"/>
40+
</Target>
41+
<Target Name="Clean">
42+
<CallTarget Targets="NtfyAndroid:Clean;NtfyAndroidService:Clean"/>
43+
</Target>
44+
<Target Name="Make">
45+
<CallTarget Targets="NtfyAndroid:Make;NtfyAndroidService:Make"/>
46+
</Target>
47+
<Import Project="$(BDS)\Bin\CodeGear.Group.Targets" Condition="Exists('$(BDS)\Bin\CodeGear.Group.Targets')"/>
48+
</Project>

samples/android/NtfyAndroid.dpr

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
program NtfyAndroid;
2+
3+
uses
4+
System.StartUpCopy,
5+
FMX.Forms,
6+
View.Main in 'src\View.Main.pas' {Form1},
7+
Service.Module.Ntfy in 'ntfy-service\Service.Module.Ntfy.pas' {NtfyAndroidServiceModule: TAndroidService};
8+
9+
{$R *.res}
10+
11+
begin
12+
Application.Initialize;
13+
Application.CreateForm(TForm1, Form1);
14+
Application.Run;
15+
end.

samples/android/NtfyAndroid.dproj

Lines changed: 1338 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
program NtfyAndroidService;
2+
3+
uses
4+
System.Android.ServiceApplication,
5+
Service.Module.Ntfy in 'Service.Module.Ntfy.pas' {NtfyAndroidServiceModule: TAndroidService};
6+
7+
{$R *.res}
8+
9+
begin
10+
Application.Initialize;
11+
Application.CreateForm(TNtfyAndroidServiceModule, NtfyAndroidServiceModule);
12+
Application.Run;
13+
end.

samples/android/ntfy-service/NtfyAndroidService.dproj

Lines changed: 982 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
//*******************************************************
2+
//
3+
// CodeGear Delphi Runtime Library
4+
// Copyright(c) 2014-2023 Embarcadero Technologies, Inc.
5+
// All rights reserved
6+
//
7+
//*******************************************************
8+
9+
package com.embarcadero.services;
10+
11+
import android.app.Service;
12+
import android.content.Intent;
13+
import android.content.res.Configuration;
14+
import android.os.IBinder;
15+
import android.os.Binder;
16+
17+
import com.embarcadero.rtl.ProxyService;
18+
19+
public class <%ServiceName%> extends Service {
20+
private final String baseLibraryName = "<%ServiceName%>";
21+
private String libraryName;
22+
23+
// Binder given to clients
24+
public final IBinder mBinder = new LocalBinder();
25+
26+
/**
27+
* Class used for the client Binder. Because we know this service always
28+
* runs in the same process as its clients, we don't need to deal with IPC.
29+
*/
30+
public class LocalBinder extends Binder {
31+
public long getService() {
32+
// Return the instance of the Delphi LocalService so clients can call public methods
33+
return ProxyService.getService(this, libraryName);
34+
}
35+
}
36+
37+
@Override
38+
public void onCreate() {
39+
libraryName = "lib" + baseLibraryName + ".so";
40+
super.onCreate();
41+
ProxyService.onCreate(this, libraryName);
42+
}
43+
44+
@Override
45+
public void onDestroy() {
46+
ProxyService.onDestroy(this, libraryName);
47+
super.onDestroy();
48+
}
49+
50+
@Override
51+
public int onStartCommand(Intent intent, int flags, int startId) {
52+
return ProxyService.onStartCommand(this, libraryName, intent, flags, startId);
53+
}
54+
55+
@Override
56+
public IBinder onBind(Intent intent) {
57+
return ProxyService.onBind(this, libraryName, intent);
58+
}
59+
60+
@Override
61+
public void onRebind(Intent intent) {
62+
ProxyService.onRebind(this, libraryName, intent);
63+
}
64+
65+
@Override
66+
public boolean onUnbind(Intent intent) {
67+
return ProxyService.onUnbind(this, libraryName, intent);
68+
}
69+
70+
@Override
71+
public void onConfigurationChanged(Configuration newConfig) {
72+
ProxyService.onConfigurationChanged(this, libraryName, newConfig);
73+
}
74+
75+
@Override
76+
public void onLowMemory() {
77+
ProxyService.onLowMemory(this, libraryName);
78+
}
79+
80+
@Override
81+
public void onTrimMemory(int level) {
82+
ProxyService.onTrimMemory(this, libraryName, level);
83+
}
84+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//*******************************************************
2+
//
3+
// CodeGear Delphi Runtime Library
4+
// Copyright(c) 2014-2023 Embarcadero Technologies, Inc.
5+
// All rights reserved
6+
//
7+
//*******************************************************
8+
9+
package com.embarcadero.services;
10+
11+
import java.lang.reflect.InvocationHandler;
12+
import java.lang.reflect.Method;
13+
import java.lang.reflect.Proxy;
14+
15+
import com.embarcadero.rtl.NativeDispatchHelper;
16+
17+
public class <%ServiceName%>ProxyInterface implements InvocationHandler {
18+
long pointer;
19+
20+
public Object CreateProxyClass(Class listenerClass, long pointer) throws ClassNotFoundException {
21+
this.pointer = pointer;
22+
return Proxy.newProxyInstance(listenerClass.getClassLoader(), new Class[] { listenerClass }, this);
23+
}
24+
25+
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
26+
Object obj = dispatchToNative2(method.getName(), NativeDispatchHelper.getMethodSignature(method.getReturnType(), method.getParameterTypes()), args, pointer);
27+
cleanNative(pointer);
28+
return obj;
29+
}
30+
31+
public native Object dispatchToNative2(String methodName, String methodSig, Object[] args, long pointer);
32+
33+
public native void cleanNative(long pointer);
34+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
object NtfyAndroidServiceModule: TNtfyAndroidServiceModule
2+
OnCreate = AndroidServiceCreate
3+
OnDestroy = AndroidServiceDestroy
4+
OnStartCommand = AndroidServiceStartCommand
5+
Height = 238
6+
Width = 324
7+
object NotificationCenter: TNotificationCenter
8+
Left = 144
9+
Top = 104
10+
end
11+
end
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
unit Service.Module.Ntfy;
2+
3+
interface
4+
5+
uses
6+
System.SysUtils,
7+
System.Notification,
8+
System.Classes,
9+
System.Android.Service,
10+
System.Threading,
11+
Androidapi.JNI.GraphicsContentViewText,
12+
Androidapi.JNI.App,
13+
Androidapi.JNI.Os,
14+
Androidapi.JNI.JavaTypes,
15+
Androidapi.JNI.Support,
16+
Androidapi.JNIBridge,
17+
Notify;
18+
19+
type
20+
TNtfyAndroidServiceModule = class(TAndroidService)
21+
NotificationCenter: TNotificationCenter;
22+
procedure AndroidServiceDestroy(Sender: TObject);
23+
procedure AndroidServiceCreate(Sender: TObject);
24+
function AndroidServiceStartCommand(const Sender: TObject;
25+
const Intent: JIntent; Flags, StartId: Integer): Integer;
26+
private
27+
FNotificationManager: JNotificationManager;
28+
FNotificationBuilder: Japp_NotificationCompat_Builder;
29+
FNotificationID: Integer;
30+
FNtfy: INotify;
31+
FTask: ITask;
32+
procedure DumbProcedure;
33+
procedure PushNotification(AEvent: INotifyEvent);
34+
end;
35+
36+
var
37+
NtfyAndroidServiceModule: TNtfyAndroidServiceModule;
38+
39+
implementation
40+
41+
uses
42+
System.DateUtils, Androidapi.Helpers;
43+
44+
{%CLASSGROUP 'FMX.Controls.TControl'}
45+
46+
{$R *.dfm}
47+
48+
procedure TNtfyAndroidServiceModule.AndroidServiceCreate(Sender: TObject);
49+
var
50+
Context: JContext;
51+
LChannel: JNotificationChannel;
52+
begin
53+
// Get the application context
54+
Context := TAndroidHelper.Context;
55+
// Get the notification manager and wrap it in a Delphi interface
56+
FNotificationManager := TJNotificationManager.Wrap((Context.getSystemService(TJContext.JavaClass.NOTIFICATION_SERVICE)));
57+
// Create a notification builder object
58+
FNotificationBuilder := TJapp_NotificationCompat_Builder.JavaClass.init(TAndroidHelper.Context);
59+
// Set the small icon for the notification
60+
FNotificationBuilder.setSmallIcon(TAndroidHelper.Context.getApplicationInfo.icon);
61+
// Set the title and content text of the notification
62+
FNotificationBuilder.setContentTitle(StrToJCharSequence('Ntfy for Delphi'));
63+
FNotificationBuilder.setContentText(StrToJCharSequence('Listening to incoming messages'));
64+
// Set the notification to be automatically canceled when the user taps on it
65+
FNotificationBuilder.setAutoCancel(True);
66+
// Set the ID of the notification (random number)
67+
FNotificationID := 98437;
68+
69+
// This section creates a channel to avoid bad notification exception on Android
70+
// Check if the Android version is 26 or higher
71+
if TJBuild_VERSION.JavaClass.SDK_INT >= 26 then
72+
begin
73+
// Get the notification manager again (this time for creating a notification channel)
74+
FNotificationManager := TJNotificationManager.Wrap((TAndroidHelper.Context.getSystemService(TJContext.JavaClass.NOTIFICATION_SERVICE) as ILocalObject).GetObjectID);
75+
// Create a notification channel with a custom ID, name, and importance level
76+
LChannel := TJNotificationChannel.JavaClass.init
77+
(StringToJString('ntfy-for-delphi'),
78+
StrToJCharSequence('Ntfy Subscription'),
79+
TJNotificationManager.JavaClass.IMPORTANCE_HIGH);
80+
// Set the color of the notification light to blue
81+
LChannel.setLightColor(TJColor.JavaClass.BLUE);
82+
// Set the visibility of the notification on the lock screen to private
83+
LChannel.setLockscreenVisibility(TJNotification.JavaClass.VISIBILITY_PRIVATE);
84+
// Create the notification channel
85+
FNotificationManager.createNotificationChannel(LChannel);
86+
end;
87+
88+
// If the Android version is 26 or higher, set the channel ID of the
89+
// notification builder to the custom channel ID
90+
if TJBuild_VERSION.JavaClass.SDK_INT >= 26 then
91+
begin
92+
FNotificationBuilder.setChannelId(StringToJString('ntfy-for-delphi'));
93+
end;
94+
95+
end;
96+
97+
procedure TNtfyAndroidServiceModule.AndroidServiceDestroy(Sender: TObject);
98+
begin
99+
FNotificationBuilder.setContentTitle(StrToJCharSequence('Killing'));
100+
FNotificationBuilder.build();
101+
FNotificationBuilder.Notify;
102+
end;
103+
104+
function TNtfyAndroidServiceModule.AndroidServiceStartCommand(
105+
const Sender: TObject; const Intent: JIntent; Flags,
106+
StartId: Integer): Integer;
107+
var
108+
LJNotification: JNotification;
109+
begin
110+
/// Sets the return value of the function to START_STICKY, which is
111+
/// a constant indicating that the service should be restarted if it's killed
112+
/// by the system.
113+
Result := TJService.JavaClass.START_NOT_STICKY;
114+
115+
/// Calls the build method on the FNotificationBuilder object. This
116+
/// method returns a JNotification object that can be used to display a
117+
/// notification in the Android system tray.
118+
FNotificationBuilder.build();
119+
120+
/// Assigns the JNotification object returned by the build method to the LJNotification variable.
121+
LJNotification := FNotificationBuilder.build();
122+
123+
/// Calls the startForeground method on the TJService object, which is the
124+
/// Android service that this code is running in. This method takes two
125+
/// arguments: an integer notification ID (FNotificationID), and the
126+
/// JNotification object created in the previous step (LJNotification). This
127+
/// causes the notification to be displayed in the system tray and keeps the
128+
/// service running in the foreground, which prevents it from being killed by
129+
/// the Android system.
130+
TJService.Wrap(JavaService).startForeground(FNotificationID, LJNotification);
131+
132+
PushNotification(New.Event.Title('Test').MessageContent(DateTimeToStr(Now)));
133+
// DumbProcedure;
134+
135+
PushNotification(New.Event.Title('Test').MessageContent(DateTimeToStr(Now)));
136+
137+
end;
138+
139+
procedure TNtfyAndroidServiceModule.DumbProcedure;
140+
begin
141+
// This works
142+
TThread.CreateAnonymousThread(
143+
procedure
144+
begin
145+
146+
// Ntfy.Subscribe('ntfy-android-test-delphi',
147+
// procedure (AEvent: INotifyEvent)
148+
// begin
149+
// PushNotification(AEvent)
150+
// end);
151+
152+
while True do
153+
begin
154+
TThread.Sleep(5000);
155+
PushNotification(New.Event.Title('Test')
156+
.MessageContent(DateTimeToStr(Now)));
157+
end;
158+
end).Start;
159+
end;
160+
161+
procedure TNtfyAndroidServiceModule.PushNotification(AEvent: INotifyEvent);
162+
var
163+
LNotification: TNotification;
164+
begin
165+
LNotification := NotificationCenter.CreateNotification();
166+
try
167+
LNotification.Name := AEvent.Id;
168+
LNotification.Title := AEvent.Title;
169+
LNotification.AlertBody := AEvent.MessageContent;
170+
LNotification.FireDate := IncSecond(Now, 1);
171+
NotificationCenter.ScheduleNotification(LNotification);
172+
finally
173+
LNotification.Free;
174+
end;
175+
end;
176+
177+
end.

0 commit comments

Comments
 (0)