Delphi Firemonkey (Android) - Detecting Failed Sending SMS - android

when my app was using Android5, I can monitor if the SMS was sent or failed by checking the content://sms/sent and content://sms/failed. Aside from via my app, I can also see the message and its status directly from the SMS app of the phone.
Now that the good old Android5 smartphone is dead, I am upgrading to a newer smartphone base on Android8 (Oreo). However I am facing a problem of detecting the status of the sent sms.
First... I can no longer "see" the sms using the phone's default SMS app.
Second... I cant also find the sms using my app even if I scroll through all messages one by one.
This is the code I use to send the SMS:
smsManager := TJSmsManager.JavaClass.getDefault;
smsTo := StringToJString(targetstr);
smsarray := smsmanager.divideMessage(stringtojstring(bodystr));
smsManager.sendmultiparttextMessage(smsTo, nil,smsarray, nil, nil);
While this is the code I use to scroll through all my messages:
uri := StrToJURI('content://sms');
smscursor := TAndroidHelper.Activity.getContentResolver.query(uri, nil, nil,nil,nil);
id_id := smscursor.getColumnIndex(StringToJstring('_id'));
idaddress := smscursor.getColumnIndex(StringToJstring('address'));
idbody := smscursor.getColumnIndex(StringToJstring('body'));
idstatus := smscursor.getColumnIndex(StringToJstring('status'));
idthread_id := smscursor.getColumnIndex(StringToJstring('thread_id'));
idtype := smscursor.getColumnIndex(StringToJstring('type'));
smscursor.moveToFirst;
** using the repeat until loop to read each and every sms until "islast" is true **
But I cant find the sent SMS irregardless if the SMS was successfully sent or not.
I need to do this detection because the signal strength in my area is very low and around 20% of the time the sms failed, and my app should resend the intended message.
PS: I also tried searching for the SMS from the content://sms/sent and content://sms/failed but to no avail.
Anyway, my questions are:
How to make the send out sms show up inside the phone's default sms app?
How to correctly determine if the send out sms was successful or not?
I read somewhere that the method above is using the API and there is a way to use the phone's sms app instead... But I dont have any idea how to do it.
Thank you.

For the benefit of others who may be in similar situation that requires to:
(A) Able to send an SMS directly from the app without using the Phone's built-in SMS default app
(B) The SMS length can be more than the default 160 characters limit (using sendmultiparttextMessage)
(C) Able to know if the SMS was process or not (base on aResultCode of the onReceive from the PendingIntent)
Known Limitation:
Under multi-sim phone, sms is sent via the default sim
if sending to multiple phone recipients, there is no way to know which ones failed, if any. So I suggest send 1 sms to 1 recipient at a time
Doesnt detect if the intended recipient receive the sms or not as the DeliveryIntent is nil
PS: tested under Delphi Community Edition 10.4.2 and Android Oreo 8.0 and 8.1
What I did:
Create and save a unit for the BroadcastReceiver (I call it BCast.pas) from the 2nd answer from this link How read JPendingIntent in Delphi?
In your main.pas (assuming it is save as main.pas), add the following:
ADD TO PROJECT the unit created in step 1 (in my case bcast.pas)
add BCAST in uses clause
add variable for the TBroadCastReceiver (ie fbroadcast : Tbroadcastreceiver;)
make an OnReceiveBroadcast procedure
in my case:
private
{ Private declarations }
procedure OnReceiveBroadcast(aContext: JContext; aIntent: JIntent; aResultCode: integer);
then under implementation, create the code for the OnReceiveBroadcast:
procedure Tmain.OnReceiveBroadcast(aContext: JContext; aIntent: JIntent; aResultCode: integer);
begin
// ... put in the codes here for the result of the last sms send. //
// ... use the the value of AResultcode to do the checking //
// ... AResultcode=1 means ok, other number means possible error //
end;
initialize the fbroadcast as follows: (i put them under my main's onshow):
fbroadcast := TBroadcastReceiver.Create(OnReceiveBroadcast);
fbroadcast.addactions([StringToJString('xxx')]);
where xxx is any string that will be use to match the intent's action
Add in a procedure to sendsms:
procedure tmain.SendSMSAPI(target,messagestr:string);
var
smsManager: JSmsManager; // Androidapi.JNI.Telephony //
smsTo : JString; // Androidapi.JNI.JavaTypes //
PIntent : JPendingIntent; // Androidapi.JNI.App //
smsarray : jarraylist;
APIntent : JArraylist;
intent : JIntent;
begin
Intent := TJIntent.Create;
Intent.setAction(StringToJString('xxx'));
PIntent := TJPendingIntent.JavaClass.getBroadcast(TAndroidHelper.Context, 0, Intent, 0);
APIntent := Tjarraylist.create;
APIntent.add(PIntent);
smsManager:= TJSmsManager.JavaClass.getDefault;
smsTo := StringToJString(target); // Androidapi.Helpers //
smsarray := smsmanager.divideMessage(stringtojstring(messagestr));
smsManager.sendmultiparttextMessage(smsTo, nil,smsarray, APIntent, nil);
end;

Related

Send notifications when the app is not used?

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.

How to start a videocall from Delphi Android app?

I'm looking for a way to start a videocall from my Android app written in Delphi 10.3. For that I want to use Android intents to 3rd-party app (WhatsApp or Skype).
Here's what I have already tried. I'm using 123 as contacts id, expecting that even if it is not a valid id, WhatsApp should still open and warn about that:
Based on https://stackoverflow.com/a/38674897 - nothing happens
Intent := TJIntent.Create;
Intent.setAction(TJIntent.JavaClass.ACTION_VIEW);
Intent.setData(StrToJURI('content://com.android.contacts/data/123'));
Intent.setType(StringToJString('vnd.android.cursor.item/vnd.com.whatsapp.voip.call'));
Based on https://stackoverflow.com/a/46049004 - nothing happens
Intent := TJIntent.Create;
Intent.setAction(TJIntent.JavaClass.ACTION_VIEW);
Intent.setData(StrToJURI('content://com.android.contacts/data/123'));
Intent.setType(StringToJString('vnd.android.cursor.item/vnd.com.whatsapp.video.call'));
Intent.setPackage(StringToJString('com.whatsapp'));
Fix suggests to disuse SetType https://stackoverflow.com/a/28244207 - nothing happens
Intent := TJIntent.Create;
Intent.setAction(TJIntent.JavaClass.ACTION_VIEW);
Intent.setDataAndType(
StrToJURI('content://com.android.contacts/data/123'),
StringToJString('vnd.android.cursor.item/vnd.com.whatsapp.video.call'));
Intent.setPackage(StringToJString('com.whatsapp'));
Skype code sample based on https://stackoverflow.com/a/14294299 - it opens Skype, but does not open chat/call on first try. If I switch to my app while Skype is open in background, on a second try it will start videocall.
Intent := TJIntent.Create;
Intent.setAction(TJIntent.JavaClass.ACTION_VIEW);
//Intent.setData(StrToJURI('skype:username?call&video=true'));
Intent.setData(StrToJURI('skype:username'));
Trying videocall action - pops up a message "Receiver not found"
Intent := TJIntent.Create;
Intent.setAction(StringToJString('com.android.phone.videocall'));
Intent.setData(StrToJURI('skype:username'));
Intent := TJIntent.Create;
Intent.setAction(StringToJString('com.android.phone.videocall'));
Intent.setData(StrToJURI('tel:123456789'));
All intents are executed with:
if MainActivity.getPackageManager.queryIntentActivities(Intent, TJPackageManager.JavaClass.MATCH_DEFAULT_ONLY).size > 0 then
MainActivity.startActivity(Intent)
else
ShowMessage('Receiver not found');
How do I make a videocall from Android App written in Delphi?
It is not a dephi specific issue. Your code doesn't work even when its in java.(It shows receiver not found)
See the following. They are in java but I guess they can point you to the right path
android-make whatsapp call
Android: Retrieve contact name from phone number
https://developer.android.com/training/permissions/requesting#java

Barcode reading Delphi xe7, event after intent not triggering

In my app, developed with XE7 for Android/iOS, I have a form for scanning barcodes. Upon a found barcode, my app validates whether it is an acceptable barcode or not. Following tutorials here: http://www.fmxexpress.com/qr-code-scanner-source-code-for-delphi-xe5-firemonkey-on-android-and-ios/
Currently I am testing on Android and I am able to integrate scanning and reading of barcodes, but the 'onBarCode' event does not fire when returned from the shared Activity of finding the barcode. Same code worked well with previous versions of Rad Studio ( XE4, XE5, XE6) but now in XE7 it does not.
Here are some snippets of code:
...
begin
Scanner := TAndroidBarcodeScanner.Create(true);
Scanner.OnBarCode := BarcodeHandler;
Scanner.Scan;
end;
procedure TmScannerForm.BarcodeHandler(Sender: TAndroidBarcodeScanner;
BarCode: String);
begin
text1.Text := Barcode;
memo1.PasteFromClipboard;
AddBarcode(BarCode, true);
end;
AddBarCode is the even I used to validate and add barcode to a list, but I didnt include it, because that code isn't the problem - it's not even triggering. The Text1.text:=Barcode and memo1.paseFromClipboard were in their for validating the even wasn't firing too. I can confirm the barcodes are being read because if I tap and manually paste, the barcode shows.
Why is this not working on XE7 as it did in previous versions of Rad Studio?
Andrea Magni has a more elegant solution than the timer on his blog based on event handling.
I would comment to send the link but I don't have enough reputation.
The link to his blog is :
http://blog.delphiedintorni.it/2014/10/leggere-e-produrre-barcode-con-delphi.html
Maybe this can help you. The blog is in Italian though but the sources are provided and are explaining by themselves.
There is a source code fragment on http://john.whitham.me.uk/xe5/ which looks useable (based on Zxing):
intent := tjintent.Create;
intent.setAction(stringtojstring('com.google.zxing.client.android.SCAN'));
sharedactivity.startActivityForResult(intent,0);
The code in the linked article also shows how to receive the Intent result. (I don't work with Delphi on Android so I am not sure if that part uses a best practice - TTKRBarCodeScanner uses a workaround with a Timer and the Clipboard).
I would try this as an alternative to see if works.
This code works to me!
Set timer enabled to true when you run your scan code
procedure Tform.Timer1Timer(Sender: TObject);
begin
if (ClipService.GetClipboard.ToString <> '') then
begin
timer1.Enabled:=false;
zSearch.Text := ClipService.GetClipboard.ToString;
//Do what you need
end;
end;
This code to me works fine!
in andorid.BarcodeScanner
function TAndroidBarcodeScanner.HandleAppEvent(AAppEvent: TApplicationEvent;
AContext: TObject): Boolean;
var
aeBecameActive : TApplicationEvent;
begin
aeBecameActive := TApplicationEvent.BecameActive;
if FMonitorClipboard and (AAppEvent = aeBecameActive) then
begin
GetBarcodeValue;
end;
end;

Empty product list on QueryProducts in Delphi XE6 TInappPurchase on Android

I am developing inapp-Purchase in Delphi XE6.
Based on embarcadero documentation I create an InAppPurchase component as below:
FInAppPurchase := TInAppPurchase.Create(self);
{$IFDEF Android}
FInAppPurchase.ProductIDs.Add(License5And);
FInAppPurchase.ProductIDs.Add(License10And);
FInAppPurchase.ProductIDs.Add(License20And);
FInAppPurchase.ProductIDs.Add(License50And);
{$ENDIF}
{$IFDEF IOS}
FInAppPurchase.ProductIDs.Add(License5);
FInAppPurchase.ProductIDs.Add(License10);
FInAppPurchase.ProductIDs.Add(License20);
FInAppPurchase.ProductIDs.Add(License50);
{$ENDIF}
FInAppPurchase.OnSetupComplete := InAppPurchase1OnSetupComplete;
FInAppPurchase.OnConsumeCompleted := InAppPurchase1ConsumeCompleted;
FInAppPurchase.OnError := InAppPurchase1Error;
FInAppPurchase.OnProductsRequestResponse := InAppPurchase1ProductsRequestResponse;
FInAppPurchase.OnPurchaseCompleted := InAppPurchase1PurchaseCompleted;
FInAppPurchase.OnRecordTransaction := InAppPurchase1RecordTransaction;
FInAppPurchase.OnVerifyPayload := InAppPurchase1VerifyPayload;
{$IFDEF Android}
FInAppPurchase.ApplicationLicenseKey := myLicenseKeyFromGoogleDeveloperConsole;
{$ENDIF}
Then in InAppPurchase1OnSetupComplete I Called the FInAppPurchase.QueryProducts then it goes into InAppPurchase1ProductsRequestResponse and products and InavlidProductIDs both are empty. I don't know what I missed. Any help will be appreciated.
I check my products in google developer console all of them are 'Active' and as Type of 'Managed'.
p.s. Code is working perfect on ios Device.
I lost a lot of time to understand the problem.
After I studied the source code I have understand that in Android the Events are asynchronous, you must wait the "QueryProducts" result.
In order to fix this problem I created a TTimer that wait 5 second before it read "InAppPurchase.IsProductPurchased"
(I'm sorry for bad English)
It seems the app should be published for alpha or beta testing.
Instead of uploading for production I need to upload it for alpha first then publish it. after that the products are shown.
I developed the interface with the market starting from the example CapitalITrivia in Enbarcadero. I had the same problem and I read the answer that was given here.
I tried it and actually works. But this solution did not satisfy me because it depends on a delay not justified.
I realized that in InAppPurchaseSetupComplete was called the QueryProducts function and then I performed IsProductPurchased.
If I move the IsProductPurchased test in InAppPurchaseProductsRequestResponse function I get the expected result without introducing the delay.

Alarm Clock using DELPHI XE5 and Android

did someone already find the correct way to program an alarm clock using DEPHI XE 5 and Android OS?
I found this code , but it does not work/compile at all:
procedure TNotificationsForm.btnSendScheduledNotificationClick(Sender: TObject);
var
Notification: TNotification;
begin
{ verify if the service is actually supported }
if NotificationC.Supported then // compile error here
begin.Supported
Notification := NotificationC.CreateNotification; // compile error here
try
Notification.Name := 'MyNotification';
Notification.AlertBody := 'Delphi for Mobile is here!';
{ Fired in 10 second }
Notification.FireDate := Now + EncodeTime(0,0,10,0);
{ Send notification in Notification Center }
NotificationC.ScheduleNotification(Notification);
finally
Notification.DisposeOf;
end;
end
end;
The first error is that NotificationC.Supported this property does not exist
You should mention that the code is based on one of the sample applications distributed with XE5. They can be found in the Start Menu entry for XE5 under Samples, or in the default C:\Users\Public\Documents\RAD Studio\12.0\Samples\MobileCodeSnippets\Notifications folder (Windows 7).
It appears you've forgotten to drop the TNotificationCenter component (available on the Component Palette's Services page) on the form and name it NotificationC as the demos do. Once you've done that, your code compiles just fine.
When you mention that you get a "compile error" in a question here, it's important to include the error message. We can't see your screen from where we are. :-) You have that exact information right in front of you, so there's no excuse for not including it. The Messages window will even copy the exact message to the clipboard for you to paste here, if you right-click the error line.

Categories

Resources