Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
I developed an android application where there is a service working in the background that job is to send notification about all rendezvous of the day.
In the main Application onCreat even i created the service form :
Application.CreateForm(TTservice, Tservice);
and i created a Tswitchbutton to start the service and to stop it :
if switchnotificationenable.IsChecked then
var
Fservice : TLocalServiceConnection;
begin
Fservice := TLocalServiceConnection.Create;
Fservice.StartService('TheService');
end;
and another code in the service's event OnstartCommande :
Result := TJService.JavaClass.START_REDELIVER_INTENT;
when debugging a violation Access pointed at the code:
Fservice.StartService('TheService');
the error normally means that what i'm calling doesn't exist i was able to access a timer that is on my service form, so i have no idea what can be the problem.
i hope you guys can help me find the solution.
i'm using Delphi XE10.2 tokyo with an sqlite database,
have a good day everyone !
Update
I found out that when i remove the file.jar of the service from the project the violation access error is gone but the service doesn't start.
-----UPDATE-----
after finding out that i cannot work with a TTimer to create my push notifications, I'm thinking about making a iTask implemented with a loop that check every 30seconds with sleep function if the system time is equal to the time given in the main application by the user
procedure TDM.AndroidServiceCreate(Sender: TObject);
var
ntf : Jnotification;
today : Ttime;
mynotification : Tnotification;
notiftime : TTime;
T : iTask;
TheINIFIle : string;
config : Tstringlist;
begin
// Loading the database path and the time given by the user in the main apps who are saved in A Ini file
TheINIFILE := TPath.Combine(TPath.GetSharedDocumentsPath ,'NotificationParams.ini');
if FileExists(TheINIFILE) then
begin
config := Tstringlist.Create;
config.loadfromfile(TheINIFILE);
notiftime := strtotime(config.Values['TimeNotification']);
TimerConnection.Database := config.Values['DatabasePath'];
TimerConnection.Connect;
// creating the iTask and giving it code that create notifications everytime the system time is equal to the notification time
if TimerCOnnection.Connected = true then
begin
T := TTask.Run(procedure
begin
while Checking = true do
begin
sleep(30000);
today := time;
with QueryForNotification do
begin
SQL.Clear;
SQL.Add('select client.entreprise, RDV.date_ntf, RDV.date_rdv, Client.num_C from client, Rdv where client.num_c = rdv.num_c and rdv.Date_Ntf = date(''now'')');
Open;
First;
while not eof do
if sametext(fieldvalues['date_ntf'], FormatDateTime('yyyy-mm-dd', now)) then
begin
MyNotification := TheNotifications.CreateNotification;
try
if sametext(formatdatetime('HH-MM',NotifTime),formatdatetime('HH-MM',today)) then
begin
MyNotification.AlertBody := 'Le RDV avec ' + FieldValues['entreprise'] + ' vous attend!' ;
TheNotifications.ScheduleNotification(MyNotification);
end;
finally
next;
MyNotification.DisposeOf;
end;
end;
end;
end;
end);
end;
end;
end;
Related
A client of mine had the question of whether it is possible to send a push notification in the employee app if they have not entered their hours by a certain time.
Is it possible to create a service for Android and iOS apps that checks every hour if the hours of the day have been entered in the database, and if not then send a push notification?
I don't know where to start, and if it is possible. But if other apps can do it, it should be possible with this app as well.
As #SilverWarior Suggests I have created two pieces of code one makes the notifications when the app goes to the background then when the app goes to the foreground I clear Tnotificationcenter. I've used the appevent function to trigger the enterdbackground and willbecomeforground.
The Appevent code looks like this:
function TForm1.AppEvent(AAppEvent: TApplicationEvent; AContext: TObject): Boolean;
begin
case AAppEvent of
TApplicationEvent.EnteredBackground:
begin
Gethourregistration(FEmployeeNr,NOW);
if dm.MthourregistrationControl.RecordCount<1 then
begin
var Notification := NotificationCenter1.CreateNotification;
try
Notification.Name := 'message1';
Notification.AlertBody := 'You have not yet entered todays hours!';
Notification.FireDate :=Date+encodetime(19,0,0,0);
{ Send notification in Notification Center }
NotificationCenter1.ScheduleNotification(Notification);
Notification.Name := 'message2';
Notification.AlertBody := 'You have not yet entered todays hours!';
Notification.FireDate :=Date+encodetime(21,0,0,0);
{ Send notification in Notification Center }
NotificationCenter1.ScheduleNotification(Notification);
finally
Notification.Free;
end;
end;
end;
TApplicationEvent.WillBecomeForeground:
Notificationcenter1.CancelAll;
end;
end;
I use the OnCreate event for asking permission for sending notifications and trigger the AppEvent.
procedure TForm1.FormCreate(Sender: TObject);
var
AppEventSvc: IFMXApplicationEventService;
begin
if NotificationCenter1.AuthorizationStatus <> TAuthorizationStatus.Authorized then
begin
FPendingAction := Action;
NotificationCenter1.RequestPermission;
end;
if TPlatformServices.Current.SupportsPlatformService(IFMXApplicationEventService, IInterface(AppEventSvc)) then
begin
AppEventSvc.SetApplicationEventHandler(AppEvent);
end;
end;
I have tested this code and for me, this works perfectly.
First of all, I know a little bit about restarting an app. but that's when it is for windows. In this case, I need to make this for an app that is from android. I couldn't find a solution for it that works in Delphi. Just found this from #Mihai Limbășan I quote:
Perhaps you should think outside the box. Instead of futzing with the
mutex/instance logic, you could simply create another executable that
waits for your app to close then starts it again. As an added bonus,
you can later use this mechanism to, for example, update some of your
main app's binaries. It's also much easier to run it elevated instead
of maintaining different integrity levels inside the same app, etc.
But have no idea how this works or even where to start...
Every tip, code sample, or maybe other solution to restart an app will be appreciated.
EDIT
after some questions here are some pieces of code from the procedure.
First.
after you choose for example the language 'English and push the button save this happens
Inifile := TIniFile.Create(fPath);
try
Inifile.WriteString('Instelling','ip',edit5.text);
Inifile.WriteString('Instelling','user',edit6.text);
Inifile.WriteString('Instelling','pixels',edit3.text);
Inifile.WriteInteger('Instelling','language',Combobox2.ItemIndex);
fGebruiker := Edit6.Text;
fFotoformaat := StrToInt(edit3.Text);
finally
FDConnection1.Params.Values['server']:=edit5.Text;
FDConnection1.Connected := True;
inifile.free;
End;
with this code, I fill an inifile with data as you see also the item index of the Combobox for the language.
on this point i restart the app manually so the right language is chosen by this code:
procedure TfmMain.FormShow(Sender: TObject);
VAR
param : string;
inifile : tInifile;
begin
if (System.SysUtils.fileexists(fPath)) then
Begin
begin
Inifile := TIniFile.Create(fPath);
try
if not (Inifile.ReadString('Instelling','ip','default')='default') and not (Inifile.ReadString('Instelling','Gebruiker','default')='default')then
try
edit5.text := Inifile.ReadString('Instelling','ip','default');
edit6.text := Inifile.ReadString('Instelling','user','default');
Edit3.text := Inifile.ReadString('Instelling','pixel','400');
combobox2.ItemIndex := IniFile.ReadInteger('Instelling','language',1);
fpixel:= StrToInt(edit3.Text);
fuser:=edit6.text;
FDConnection1.Params.Values['server']:=edit5.Text;
taal := 'NL';
//find language settings
if combobox2.ItemIndex=0 then
begin
language:= 'NL'
end;
if combobox2.ItemIndex=1 then
begin
language:= 'ENG';
end;
if language='ENG' then
begin
vertalerENG.vertaler('ENG');
end;
end;
end;
end;
end;
the VertalerENG is a function that is fired if the language parameter is ENG and change all of the captions to English.
the problem is that nothing is changed till i restart the app.
If you want restart app programmatically,
This code work fine for me and you can set time elapse before restart app
uses
Androidapi.Helpers,Androidapi.JNI.GraphicsContentViewText,Androidapi.JNI.App,
System.DateUtils;
...
procedure RestartApp;
{$IFDEF ANDROID}
var LPM : JPackageManager;
LIntent_Start : JIntent;
LPendingIntent : JPendingIntent;
LMS : Int64;
{$ENDIF}
begin
{$IFDEF ANDROID}
LPM := TAndroidHelper.Context.getPackageManager();
LIntent_Start := LPM.getLaunchIntentForPackage(
TAndroidHelper.Context.getPackageName()
);
LIntent_Start.addFlags( TJIntent.JavaClass.FLAG_ACTIVITY_CLEAR_TOP );
LPendingIntent := TJPendingIntent.JavaClass.getActivity(
TAndroidHelper.Context,
223344 {RequestCode},
LIntent_Start,
TJPendingIntent.JavaClass.FLAG_CANCEL_CURRENT
);
LMS := DateTimeToUnix( Now, False {InputIsUTC} ) * 1000;
TAndroidHelper.AlarmManager.&set(
TJAlarmManager.JavaClass.RTC,
LMS + 10000,
LPendingIntent
);
// TAndroidHelper.Activity.finish();
Application.Terminate;
{$ENDIF }
end;
If changing the language is your only concern then i would suggest changing the locale of the application. You only need to restart the activity if you're using all the strings correctly from strings.xml
You can see it here how to change the locale of application programatically.
Change app language programmatically in Android
I'm trying to make an application with local service, with an example from
http://docwiki.embarcadero.com/RADStudio/Seattle/en/Creating_Android_Services
But in my case, my service just won't fire a notification after started (nor can I be sure that the service is running). Is there any way to make sure my service is running? Since when I tried to check on device's System - Apps - Running even my app is not listed. It's like my app just died (or sleep) after it lost focus/switch to other app
Below is my simple service & notification code
function TAndroidServiceDM.AndroidServiceStartCommand(const Sender: TObject;
const Intent: JIntent; Flags, StartId: Integer): Integer;
var TheNotif: TNotification;
begin
TheNotif := Notif.CreateNotification;
try
TheNotif.Title := 'Notif Title';
TheNotif.Name := 'NotifServiceStart';
TheNotif.AlertBody := 'I''m alive!!!!';
TheNotif.FireDate := Now;
Notif.PresentNotification(TheNotif);
finally
TheNotif.Free;
end;
Result := TJService.JavaClass.START_STICKY;
end;
and this is my caller app code
procedure TfmMain.FormCreate(Sender: TObject);
var FServiceConn: TLocalServiceConnection;
begin
FServiceConn := TLocalServiceConnection.Create;
FServiceConn.StartService('unMainServiceLocation');
FServiceConn.BindService('unMainServiceLocation');
end;
procedure TfmMain.NotifReceiveLocalNotification(Sender: TObject;
ANotification: TNotification);
begin
Text1.Text :=
'Title : ' + ANotification.Title + #13#10 +
'Name : ' + ANotification.Name + #13#10 +
'Alert : ' + ANotification.AlertBody ;
end;
I tried to put a button on app and do the same thing (send notification from app) and when I pressed the button, my Text1 component shows the right thing, and the notification did appear, but not when starting my app (should've been started by the service). Service name suppose to be right since when I change the service name it got forced stopped (segmentation fault 11)
Please kindly give advice. Thanks
After having put this lines in other event (like a button click), it works fine:
FServiceConn.StartService('unMainServiceLocation');
FServiceConn.BindService('unMainServiceLocation');
The scope of your variable var FServiceConn: TLocalServiceConnection; is limited to FormCreate, meaning it will not be available outside the FormCreate procedure. Place the variable up it in the private section of the form. Don't forget to clean up on closing / destroying the form.
I just made an app with Delphi XE6 that receives push notifications with kinvey based on this example
When the application is running and I send a push the PushEvent handler receives it well, but when the application is closed and I press the notification it only opens my app.
Can I know which notification was pressed and get parameters from it?
Thanks in advance.
Edit:
I get a little bit closer, in my FormCreate ask for Extras:
procedure TForm1.FormCreate(Sender: TObject);
var
LIntent: JIntent;
LExtras: JBundle;
LExtrasArray: TJavaObjectArray<AndroidApi.JNI.JavaTypes.JObject>;
begin
LIntent := SharedActivity.getIntent;
try
if LIntent <> nil then
begin
LExtras := LIntent.getExtras;
if LExtras <> nil then
begin
//Now try to get the data
LExtrasArray := LExtras.KeySet.toArray;
for I := 0 to LExtrasArray.Length - 1 do
Memo1.Lines.Add(JStringToString(LExtrasArray.Items[I].toString));
end;
end;
finally
LIntent := nil;
end;
end;
With this code I get "gcm" in my memo.
So, when the notification fires my app I get this Extra available.
Now the problem is how I get info about that extra?
I tried LExtras.getString(StringToJString('message')) but this writes '' instead of the push message
Sarina DuPont answer me in her Blog
PushEvents component has a property StartupNotification for this purpose
procedure TMainForm.FormShow(Sender: TObject);
begin
if Assigned(PushEvents.StartupNotification) then
//Do something here!
//for example
//Memo.Text := PushEvents.StartupNotification.Message;
end;
If this has already been visited here on SO, please point me to it, cause I cant seem to find it. Having said that:
Using the standard delphi application events, as well as the Mobile app lifecycle events handling , i am trying to find the best spot to read and write a INI file?
as I test, i created a demo app with a button which increments a count variable and displays it in a show message
procedure TfrmMain.Button1Click(Sender: TObject);
begin
inc(Count);
ShowMessage(IntToStr(Count));
end;
In the main form's OnCreate even, I read the inifile
procedure TfrmMain.FormCreate(Sender: TObject);
var
Ini: TIniFile;
begin
Ini := TIniFile.Create( TPath.GetDocumentsPath + PathDelim + 'fortysixtozero.ini' );
try
Count := Ini.ReadInteger( 'Main', 'Count', 0 );
finally
Ini.Free;
end;
end;
Now, knowing that a mobile app can have different states, i am wondering where the best place is to write the ini file?
Best states to save the application state or store settings is "aeEnteredBackground". I used the delphi FMX event here. You should also check the "aeWillBecomeInactive" and "aeWillTerminate" events, but the first one is the most relavant. The application enters background when another application is opened or yours is closed (they are not terminated right away).
Check this article.
The code to listen for events looks like this:
function TfMain.HandleAppEvent(AAppEvent: TApplicationEvent; AContext: TObject): Boolean;
begin
case AAppEvent of
aeFinishedLaunching: ;
aeBecameActive: ;
aeWillBecomeInactive: ;
aeEnteredBackground: ;
aeWillBecomeForeground: ;
aeWillTerminate: ;
aeLowMemory: ;
aeTimeChange: ;
aeOpenURL: ;
end;
Result := True;
end;
To attach the listener you use the platform services:
if TPlatformServices.Current.SupportsPlatformService(IFMXApplicationEventService, IInterface(SvcEvents)) then
SvcEvents.SetApplicationEventHandler(HandleAppEvent);
Just add "FMX.Platform" to your uses clause.
In Delphi XE7, there is an "OnSaveState" event for forms. This is the preferred place to save application data since it will execute when an iOS app goes into the "background" state. The documentation is quite helpful... search for "save state".
Here is my code in the main form's OnCreate handler:
procedure TMainWindow.FormCreate( Sender : TObject );
var
IniFile : TIniFile;
Metric : BOOLEAN;
IniFileName : STRING;
Reader : TBinaryReader;
begin
fInitializing := True;
SaveState.StoragePath := TPath.GetLibraryPath;
if SaveState.Stream.Size > 0 then begin
Reader := TBinaryReader.Create( SaveState.Stream );
try
Metric := Reader.ReadBoolean;
vMetricUnits.IsChecked := Metric;
SetSliderLimits( Metric );
Temperature := Reader.ReadDouble;
Dewpoint := Reader.ReadDouble;
Humidity := Reader.ReadDouble;
WindSpeed := Reader.ReadDouble;
finally
Reader.Free;
end;
end
else begin
Metric := False;
vMetricUnits.IsChecked := Metric;
SetSliderLimits( Metric );
Temperature := 70;
Dewpoint := 70;
Humidity := 100;
WindSpeed := 0;
end;
SetMetricUnits( cMetricUnits );
fInitializing := False;
WriteTrackbarCaptions;
CalculateTemperatures;
end;
And here is the code in the form's OnSaveState handler:
procedure TMainWindow.FormSaveState( Sender : TObject );
var
Writer : TBinaryWriter;
begin
SaveState.Stream.Clear;
Writer := TBinaryWriter.Create( SaveState.Stream );
try
Writer.Write( cMetricUnits );
Writer.Write( Temperature );
Writer.Write( Dewpoint );
Writer.Write( Humidity );
Writer.Write( WindSpeed );
finally
Writer.Free;
end;
end;
I've tested this on both the iPad and in Windows and it works on both platforms. Doing it this way completely avoids the use of the .ini file, however it does create a somewhat oddly named .tmp file in the Windows version. I assume that an equivalent file is also created on the iPad.