erdoganozkaya 0 Posted February 9, 2020 Hello friends, How do I get Android incoming calls instantly? IFMXPhoneDialerService, I would be glad if you share an example. Thank you. Share this post Link to post
Dave Nottage 557 Posted February 11, 2020 You could extend this demo: https://github.com/Embarcadero/RADStudio10.3.3Demos/tree/master/Object Pascal/Mobile Snippets/PhoneDialer Just create a handler for the OnCallStateChanged event of FPhoneDialerService, handle TCallState.Incoming and fetch the call using GetCurrentCalls Share this post Link to post
erdoganozkaya 0 Posted February 11, 2020 Hello, to make this call. I want to receive incoming calls Thank you. Share this post Link to post
Remy Lebeau 1393 Posted February 11, 2020 (edited) 15 hours ago, erdoganozkaya said: I want to receive incoming calls Then do what Dave said to do: 18 hours ago, Dave Nottage said: Just create a handler for the OnCallStateChanged event of FPhoneDialerService, handle TCallState.Incoming and fetch the call using GetCurrentCalls In other words, add something like this to the code: procedure TPhoneDialerForm.FormCreate(Sender: TObject); begin {$IFDEF ANDROID} FCallPhonePermission := JStringToString(TJManifest_permission.JavaClass.CALL_PHONE); {$ENDIF} { test whether the PhoneDialer services are supported } if TPlatformServices.Current.SupportsPlatformService(IFMXPhoneDialerService, FPhoneDialerService) then FPhoneDialerService.OnCallStateChanged := CallStateChanged; // <-- ADD THIS! end; procedure TPhoneDialerForm.CallStateChanged(const ACallID: String; const AState: TCallState); var Calls: TCalls; Call: TCall; begin if AState = TCallState.Incoming then begin Calls := FPhoneDialerService.GetCurrentCalls; try for Call in Calls do begin if Call.GetCallID = ACallID then begin // use Call as needed ... Exit; end; end; finally for Call in Calls do Call.Free; end; end; end; Edited February 11, 2020 by Remy Lebeau Share this post Link to post
erdoganozkaya 0 Posted February 16, 2020 Thank you for your answer, could you please make a small example? 1 Share this post Link to post
Remy Lebeau 1393 Posted February 18, 2020 On 2/16/2020 at 6:15 AM, erdoganozkaya said: Thank you for your answer, could you please make a small example? Was there something wrong with the example I gave you in my previous reply? Share this post Link to post
CarioJr 2 Posted April 17, 2020 (edited) Hello, I put the example to test, it doesn't work. It seems to me that this event, CallStateChanged is not working. I already test in diferent android versions, and doesn't work. This is my code. Am i doing something whorng? For make calls it works. unit Principal; interface uses System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, System.Permissions, FMX.Types, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.StdCtrls, FMX.PhoneDialer, FMX.Platform, FMX.Edit, FMX.ListBox, FMX.Layouts, FMX.Controls.Presentation; type TFrmPrincipal = class(TForm) Layout2: TLayout; Label1: TLabel; edtPhoneNumber: TEdit; btnCall: TButton; lbInfo: TListBox; lbCalls: TListBox; Timer1: TTimer; procedure FormCreate(Sender: TObject); procedure btnCallClick(Sender: TObject); procedure Timer1Timer(Sender: TObject); private { Private declarations } FPhoneDialerService: IFMXPhoneDialerService; FCallPhonePermission: string; procedure DisplayRationale(Sender: TObject; const APermissions: TArray<string>; const APostRationaleProc: TProc); procedure MakePhoneCallPermissionRequestResult(Sender: TObject; const APermissions: TArray<string>; const AGrantResults: TArray<TPermissionStatus>); procedure CallStateChanged(const ACallID: string; const AState: TCallState); function CallStateAsString(AState: TCallState): String; public end; var FrmPrincipal: TFrmPrincipal; implementation {$R *.fmx} {$R *.LgXhdpiTb.fmx ANDROID} {$R *.LgXhdpiPh.fmx ANDROID} uses {$IFDEF ANDROID} Androidapi.Helpers, Androidapi.JNI.JavaTypes, Androidapi.JNI.Os, {$ENDIF} FMX.DialogService; procedure TFrmPrincipal.btnCallClick(Sender: TObject); begin { test whether the PhoneDialer services are supported } if FPhoneDialerService <> nil then begin { if the Telephone Number is entered in the edit box then make the call, else display an error message } if edtPhoneNumber.Text <> '' then PermissionsService.RequestPermissions([FCallPhonePermission], MakePhoneCallPermissionRequestResult, DisplayRationale) else begin ShowMessage('Please type in a telephone number.'); edtPhoneNumber.SetFocus; end; end else ShowMessage('PhoneDialer service not supported'); end; function TFrmPrincipal.CallStateAsString(AState: TCallState): String; begin case AState of TCallState.None: Result := 'None'; TCallState.Connected: Result := 'Connected'; TCallState.Incoming: Result := 'Incoming'; TCallState.Dialing: Result := 'Dialing'; TCallState.Disconnected: Result := 'Disconnected'; else Result := '<unknown>'; end; end; procedure TFrmPrincipal.CallStateChanged(const ACallID: string; const AState: TCallState); var Calls: TCalls; Call: TCall; begin if AState = TCallState.Incoming then begin Calls := FPhoneDialerService.GetCurrentCalls; try for Call in Calls do begin if Call.GetCallID = ACallID then begin ShowMessage(ACallID); Exit; end; end; finally for Call in Calls do Call.Free; end; end; end; procedure TFrmPrincipal.DisplayRationale(Sender: TObject; const APermissions: TArray<string>; const APostRationaleProc: TProc); begin // Show an explanation to the user *asynchronously* - don't block this thread waiting for the user's response! // After the user sees the explanation, invoke the post-rationale routine to request the permissions ShowMessage('The app needs to be able to support your making phone calls'); // procedure(const AResult: TModalResult) // begin // APostRationaleProc; // end) end; procedure TFrmPrincipal.FormCreate(Sender: TObject); begin {$IFDEF ANDROID} FCallPhonePermission := JStringToString(TJManifest_permission.JavaClass.CALL_PHONE); {$ENDIF} { test whether the PhoneDialer services are supported } TPlatformServices.Current.SupportsPlatformService(IFMXPhoneDialerService, FPhoneDialerService); lbInfo.clear; if TPlatformServices.Current.SupportsPlatformService(IFMXPhoneDialerService, IInterface(FPhoneDialerService)) then begin FPhoneDialerService.OnCallStateChanged := CallStateChanged; lbInfo.ItemHeight := lbInfo.ClientHeight / 4; lbInfo.Items.Add('Carrier Name: ' + FPhoneDialerService.GetCarrier.GetCarrierName); lbInfo.Items.Add('ISO Country Code: ' + FPhoneDialerService.GetCarrier.GetIsoCountryCode); lbInfo.Items.Add('Network Code: ' + FPhoneDialerService.GetCarrier.GetMobileCountryCode); lbInfo.Items.Add('Mobile Network: ' + FPhoneDialerService.GetCarrier.GetMobileNetwork); // lbInfo.Items.Add('Status: ' + btnCall.Enabled := True; end else lbInfo.Items.Add('No Phone Dialer Service'); if TPlatformServices.Current.SupportsPlatformService(IFMXPhoneDialerService, FPhoneDialerService) then FPhoneDialerService.OnCallStateChanged := CallStateChanged; end; procedure TFrmPrincipal.MakePhoneCallPermissionRequestResult(Sender: TObject; const APermissions: TArray<string>; const AGrantResults: TArray<TPermissionStatus>); begin // 1 permission involved: CALL_PHONE if (Length(AGrantResults) = 1) and (AGrantResults[0] = TPermissionStatus.Granted) then FPhoneDialerService.Call(edtPhoneNumber.Text) else ShowMessage('Cannot make a phone call because the required permission has not been granted'); end; procedure TFrmPrincipal.Timer1Timer(Sender: TObject); begin end; end. Edited April 17, 2020 by CarioJr Error in sintax. Share this post Link to post
erva 2 Posted April 17, 2020 (edited) I been struggling with this too. What i have learned is that in Android you need to have permission to READ_CALL_LOG to get phone number. But i still have trouble get CallStateChange to trigger. With this solution i managed to get CallStateChange trigger, but it doesn't work always. Need to try making it Android Service and see if it works better: stackoverflow.com/questions/61215767/delphi-android-read-phone-state-not-triggering This is my code to get all three permissions: constructor TForm1.Create(AOwner: TComponent); const PermissionAccessReadPhoneState = 'android.permission.READ_PHONE_STATE'; PermissionAccessMakeCall = 'android.permission.CALL_PHONE'; PermissionAccessReadCallLog = 'android.permission.READ_CALL_LOG'; begin inherited Create(AOwner); PermissionsService.RequestPermissions([PermissionAccessMakeCall, PermissionAccessReadPhoneState, PermissionAccessReadCallLog], procedure(const APermissions: TArray<string>; const AGrantResults: TArray<TpermissionStatus>) begin TPlatformServices.Current.SupportsPlatformService(IFMXPhoneDialerService, IInterface(PhoneDialerService)); if (Length(AgrantResults) = 3) and (AgrantResults[0] = TPermissionStatus.Granted) and (AgrantResults[1] = TPermissionStatus.Granted) and (AgrantResults[2] = TPermissionStatus.Granted) then begin ShowMessage('READ_PHONE_STATE + CALL_PHONE + READ_CALL_LOG Activated!'); end; end); if Assigned(PhoneDialerService) then begin PhoneDialerService.OnCallStateChanged := MyOnCallStateChanged; end; end; Edited April 17, 2020 by erva Added code syntax Share this post Link to post
CarioJr 2 Posted April 18, 2020 Hi, and Thanks for your help! I modified my code to seems more like yours, and stil not working. My callstatechanged not trigger. Am i missing something? unit Principal; interface uses System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, System.Permissions, FMX.Types, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.StdCtrls, FMX.PhoneDialer, FMX.Platform, FMX.Edit, FMX.ListBox, FMX.Layouts, FMX.Controls.Presentation; type TFrmPrincipal = class(TForm) Layout2: TLayout; Label1: TLabel; edtPhoneNumber: TEdit; btnCall: TButton; Timer1: TTimer; lblCallState: TLabel; // procedure FormCreate(Sender: TObject); procedure btnCallClick(Sender: TObject); procedure FormCreate(Sender: TObject); private { Private declarations } FPhoneDialerService: IFMXPhoneDialerService; FCallPhonePermission: string; procedure DisplayRationale(Sender: TObject; const APermissions: TArray<string>; const APostRationaleProc: TProc); procedure MakePhoneCallPermissionRequestResult(Sender: TObject; const APermissions: TArray<string>; const AGrantResults: TArray<TPermissionStatus>); procedure MyOnCallStateChanged(const ACallID: String; const ACallState: TCallState); public end; var FrmPrincipal: TFrmPrincipal; implementation {$R *.fmx} {$R *.LgXhdpiTb.fmx ANDROID} {$R *.LgXhdpiPh.fmx ANDROID} uses {$IFDEF ANDROID} Androidapi.Helpers, Androidapi.JNI.JavaTypes, Androidapi.JNI.Os, {$ENDIF} FMX.DialogService; procedure TFrmPrincipal.btnCallClick(Sender: TObject); begin { test whether the PhoneDialer services are supported } if FPhoneDialerService <> nil then begin { if the Telephone Number is entered in the edit box then make the call, else display an error message } if edtPhoneNumber.Text <> '' then PermissionsService.RequestPermissions([FCallPhonePermission], MakePhoneCallPermissionRequestResult, DisplayRationale) else begin ShowMessage('Please type in a telephone number.'); edtPhoneNumber.SetFocus; end; end else ShowMessage('PhoneDialer service not supported'); end; procedure TFrmPrincipal.DisplayRationale(Sender: TObject; const APermissions: TArray<string>; const APostRationaleProc: TProc); begin // Show an explanation to the user *asynchronously* - don't block this thread waiting for the user's response! // After the user sees the explanation, invoke the post-rationale routine to request the permissions ShowMessage('The app needs to be able to support your making phone calls'); // procedure(const AResult: TModalResult) // begin // APostRationaleProc; // end) end; procedure TFrmPrincipal.FormCreate(Sender: TObject); const PermissionAccessReadPhoneState = 'android.permission.READ_PHONE_STATE'; PermissionAccessMakeCall = 'android.permission.CALL_PHONE'; PermissionAccessReadCallLog = 'android.permission.READ_CALL_LOG'; begin FCallPhonePermission := PermissionAccessMakeCall; TPlatformServices.Current.SupportsPlatformService(IFMXPhoneDialerService, IInterface(FPhoneDialerService)); PermissionsService.RequestPermissions([PermissionAccessMakeCall, PermissionAccessReadPhoneState, PermissionAccessReadCallLog], procedure(const APermissions: TArray<string>; const AGrantResults: TArray<TPermissionStatus>) begin if (Length(AGrantResults) = 3) and (AGrantResults[0] = TPermissionStatus.Granted) and (AGrantResults[1] = TPermissionStatus.Granted) then begin ShowMessage('READ_PHONE_STATE + CALL_PHONE Activated + call_log!'); end; end); if Assigned(FPhoneDialerService) then begin FPhoneDialerService.OnCallStateChanged := MyOnCallStateChanged; end; end; procedure TFrmPrincipal.MakePhoneCallPermissionRequestResult(Sender: TObject; const APermissions: TArray<string>; const AGrantResults: TArray<TPermissionStatus>); begin if (Length(AGrantResults) = 1) and (AGrantResults[0] = TPermissionStatus.Granted) then FPhoneDialerService.Call(edtPhoneNumber.Text) else ShowMessage('Cannot make a phone call because the required permission has not been granted'); end; procedure TFrmPrincipal.MyOnCallStateChanged(const ACallID: String; const ACallState: TCallState); var outText: String; begin case ACallState of TCallState.None: outText := 'No calls'; TCallState.Connected: outText := 'Connected'; TCallState.Incoming: outText := 'Incoming Call'; TCallState.Dialing: outText := 'Dialing'; TCallState.Disconnected: outText := 'Disconnected'; end; lblCallState.Text := outText; end; end. Share this post Link to post
erva 2 Posted April 18, 2020 I'am having same trouble, sometimes CallStateChange works, sometimes not. I haven't figured out why. Maybe if make Android Service, it works better. haven't tried it yet. Share this post Link to post
CarioJr 2 Posted April 20, 2020 To me, never works. What android version are you using to test? Thanks. Share this post Link to post
Lars Fosdal 1792 Posted April 20, 2020 Have you setup the required permissions in the Android manifest? From https://developer.android.com/reference/android/telephony/PhoneStateListener#onCallStateChanged String: call phone number. If application does not have READ_CALL_LOG permission or carrier privileges (see TelephonyManager#hasCarrierPrivileges), an empty string will be passed as an argument. Share this post Link to post
erva 2 Posted April 20, 2020 (edited) Android 9. And in manifest is permissions. Edited April 20, 2020 by erva "And in manifest is permissions" Share this post Link to post
CarioJr 2 Posted April 29, 2020 I tried in a device with android 4.4 and it works. But is very obsolet. 1 Share this post Link to post
erva 2 Posted April 30, 2020 I abandod catching calls immediately when phone rings. Get calls info from call log, number and seconds what call last and move them database. Reading Android call logs seems to working flawless. Share this post Link to post
CarioJr 2 Posted May 4, 2020 On 4/30/2020 at 5:51 PM, erva said: Do you set the function on a timer, where do you call her? with yor tip i can read the log but, it is comming with all call logs, Can i bring just the last ones? Do you set the function on a timer, where do you call her? I can read the log but, it is comming with all call logs, Can i bring just the last ones? Share this post Link to post
CarioJr 2 Posted May 6, 2020 On 4/20/2020 at 9:05 AM, Lars Fosdal said: Have you setup the required permissions in the Android manifest? From https://developer.android.com/reference/android/telephony/PhoneStateListener#onCallStateChanged String: call phone number. If application does not have READ_CALL_LOG permission or carrier privileges (see TelephonyManager#hasCarrierPrivileges), an empty string will be passed as an argument. I tried in android studio, it works in versions below 9. But my client have a galaxy s30 with the android 10. In him The number came null, in my manifest i think i put all the permissions. Can you help me? <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.CALL_PHONE" /> <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" /> <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/> <uses-permission android:name="android.permission.READ_CALL_LOG"/> Share this post Link to post
erva 2 Posted May 7, 2020 (edited) On 5/4/2020 at 5:22 PM, CarioJr said: Do you set the function on a timer, where do you call her? I can read the log but, it is comming with all call logs, Can i bring just the last ones? I have just a "demo" app what reads call log when started. Been thinking solutions when to transfer calls to database. Maybe this kind of solution to append to DB only new calls: 1. Read call from log 2. Search from DB if call with same unix time exists in DB. 3. If not found, append call to db Been thinking if it's possible remove calls from log with code? Edited May 7, 2020 by erva Share this post Link to post
erva 2 Posted May 7, 2020 16 hours ago, CarioJr said: I tried in android studio, it works in versions below 9. But my client have a galaxy s30 with the android 10. In him The number came null, in my manifest i think i put all the permissions. Can you help me? Found this discussion, might be that in Android 10 getting call number is not possible anymore for security reasons? https://support.google.com/android/thread/38640488?hl=en Share this post Link to post
erva 2 Posted May 7, 2020 (edited) 16 hours ago, CarioJr said: I tried in android studio, it works in versions below 9. But my client have a galaxy s30 with the android 10. In him The number came null Can you get caller name from log? I have Android 9 and some day we have to move to Android 10. If phone number is not showing anymore, must think other solutions to link call to your customers in CRM. Edited May 7, 2020 by erva Share this post Link to post
CarioJr 2 Posted May 7, 2020 5 hours ago, erva said: Can you get caller name from log? I have Android 9 and some day we have to move to Android 10. If phone number is not showing anymore, must think other solutions to link call to your customers in CRM. I didin't try. For me only matter the number, my client wants to know who is calling and the relationated data in my software during the call. Now I finaly get the number! But unfortunately not on delphi! Share this post Link to post
erva 2 Posted May 7, 2020 Thinking if can't get number in Android 10 and beyond that, solution might be adding in DB with client record same name what's in phones address book. We need to track existing clients calls and how long they take, one person takes care about 50 clients so that might be working solution. Share this post Link to post