|
| 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