Jump to content
erdoganozkaya

Receiving incoming calls

Recommended Posts

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
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 by Remy Lebeau

Share this post


Link to post
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

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 by CarioJr
Error in sintax.

Share this post


Link to post

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 by erva
Added code syntax

Share this post


Link to post

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

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

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

Android 9. And in manifest is permissions.

 

Edited by erva
"And in manifest is permissions"

Share this post


Link to post

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

Share this post


Link to post
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
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 by erva

Share this post


Link to post
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

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

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×