如何在Delphi中检查和挂断/拒绝Android上的传入/传出呼叫?

时间:2015-11-09 12:50:12

标签: android delphi phone-call delphi-10-seattle

在Delphi中以编程方式检查和挂断/拒绝来自Android的传入/传出呼叫是否有任何解决方案?

1 个答案:

答案 0 :(得分:5)

首先,您必须拥有自己的BroadcastReceiver单元 - 您可以从here下载。

unit CSBroadcastReceiver;

interface

uses
  System.Classes
  ,System.SysUtils
  {$IFDEF ANDROID}
  ,Androidapi.JNI.Embarcadero
  ,Androidapi.JNI.GraphicsContentViewText
  ,Androidapi.Helpers
  ,Androidapi.JNIBridge
  ,Androidapi.JNI.JavaTypes
  ,Androidapi.JNI.App
  {$ENDIF}
  ;

type

  {$IFNDEF ANDROID}
  JIntent = class
  end;
  JContext = class
  end;
  {$ENDIF}

  TCSBroadcastReceiver= class;
  TOnReceive = procedure (csContext: JContext; csIntent: JIntent) of object;

  {$IFDEF ANDROID}
  TCSListener = class(TJavaLocal, JFMXBroadcastReceiverListener)
    private
      FOwner: TCSBroadcastReceiver;
    public
      constructor Create(AOwner: TCSBroadcastReceiver);
      procedure OnReceive(csContext: JContext; csIntent: JIntent); cdecl;
  end;
  {$ENDIF}


  TCSBroadcastReceiver = class(TComponent)
    private
      {$IFDEF ANDROID}
      FReceiver: JBroadcastReceiver;
      FListener : TCSListener;
      {$ENDIF}
      FOnReceive: TOnReceive;
      FItems: TStringList;
      function GetItem(const csIndex: Integer): String;

    public
      constructor Create(AOwner: TComponent); override;
      destructor  Destroy; override;
      procedure SendBroadcast(csValue: String);
      procedure Add(csValue: String);
      procedure Delete(csIndex: Integer);
      procedure Clear;
      procedure setResultData(data: JString);
      function Remove(const csValue: String): Integer;
      function First: String;
      function Last: String;
      function HasPermission(const csPermission: string): Boolean;
      procedure RegisterReceive;
      property Item[const csIndex: Integer]: string read GetItem; default;
      property Items: TStringList read FItems write FItems;
    published
      property OnReceive: TOnReceive read FOnReceive write FOnReceive;
  end;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('Classicsoft', [TCSBroadcastReceiver]);
end;

{ TCSBroadcastReceiver }

procedure TCSBroadcastReceiver.setResultData(data: Jstring);
begin
  FReceiver.setResultData(data);
end;

procedure TCSBroadcastReceiver.Add(csValue: String);
{$IFDEF ANDROID}
var
  Filter: JIntentFilter;
{$ENDIF}
begin
  {$IFDEF ANDROID}
  if (FListener = nil) or (FReceiver = nil) then
  begin
    Raise Exception.Create('First use RegisterReceive!');
    Exit;
  end;
  {$ENDIF}

  if FItems <> nil then
    if FItems.IndexOf(csValue) = -1 then
    begin
    {$IFDEF ANDROID}
      filter := TJIntentFilter.Create;
      filter.addAction(StringToJString(csValue));
      TAndroidHelper.Context.registerReceiver(FReceiver, filter);
    {$ENDIF}
      FItems.Add(csValue);
    end;
end;

procedure TCSBroadcastReceiver.Clear;
begin
  FItems.Clear;
end;

constructor TCSBroadcastReceiver.Create(AOwner: TComponent);
begin
  inherited;
  FItems := TStringList.Create;
end;

procedure TCSBroadcastReceiver.Delete(csIndex: Integer);
begin
  if FItems <> nil then
  begin
    FItems.Delete(csIndex);
    {$IFDEF ANDROID}
      TAndroidHelper.Activity.UnregisterReceiver(FReceiver);
      RegisterReceive;
    {$ENDIF}
  end;
end;

destructor TCSBroadcastReceiver.Destroy;
begin
  FItems.Free;
  {$IFDEF ANDROID}
  if FReceiver <> nil  then
    TAndroidHelper.Activity.UnregisterReceiver(FReceiver);
  {$ENDIF}
  inherited;
end;

function TCSBroadcastReceiver.First: String;
begin
  Result := FItems[0];
end;

function TCSBroadcastReceiver.GetItem(const csIndex: Integer): String;
begin
  Result := FItems[csIndex];
end;

function TCSBroadcastReceiver.HasPermission(const csPermission: string): Boolean;
{$IFDEF ANDROID}
begin
  Result := TAndroidHelper.Activity.checkCallingOrSelfPermission(StringToJString(csPermission)) = TJPackageManager.JavaClass.PERMISSION_GRANTED;
{$ELSE}
begin
  Result := False;
{$ENDIF}
end;

function TCSBroadcastReceiver.Last: String;
begin
  Result := FItems[FItems.Count];
end;

procedure TCSBroadcastReceiver.RegisterReceive;
{$IFDEF ANDROID}
var
  I: Integer;
begin
  if FListener = nil then
    FListener := TCSListener.Create(Self);
  if FReceiver = nil then
    FReceiver := TJFMXBroadcastReceiver.JavaClass.init(FListener);
  if FItems <> nil then
    if FItems.Count > 0 then
      for I := 0 to FItems.Count -1 do
        Add(FItems[I]);
{$ELSE}
begin
{$ENDIF}
end;

function TCSBroadcastReceiver.Remove(const csValue: String): Integer;
begin
  Result := FItems.IndexOf(csValue);
  if Result > -1 then
    FItems.Delete(Result);
end;

procedure TCSBroadcastReceiver.SendBroadcast(csValue: String);
{$IFDEF ANDROID}
var
  Inx: JIntent;
begin
  Inx := TJIntent.Create;
  Inx.setAction(StringToJString(csValue));
  TAndroidHelper.Context.sendBroadcast(Inx);
{$ELSE}
begin
{$ENDIF}
end;

{$IFDEF ANDROID}
constructor TCSListener.Create(AOwner: TCSBroadcastReceiver);
begin
  inherited Create;
  FOwner := AOwner;
end;

procedure TCSListener.OnReceive(csContext: JContext; csIntent: JIntent);
begin
  if Assigned(FOwner.OnReceive) then
    FOwner.onReceive(csContext, csIntent);
end;

{$ENDIF}

end.

其次,您必须创建自己的JMethod JLang_Class定义 - 您可以从here下载它。

unit Androidapi.JNI.JavaTypes.Own;

interface

uses
  Androidapi.JNI.JavaTypes,
  Androidapi.JNIBridge;

type
  JOwnMethod = interface;//java.lang.reflect.Method
  JOwnLang_Class = interface;//java.lang.Class

  JOwnMethodClass = interface(JObjectClass)
    ['{C995BD27-1D77-48E5-B478-EB8E9E607020}']
  end;

  [JavaSignature('java/lang/reflect/Method')]
  JOwnMethod = interface(JObject)
    ['{ED1B0770-0BD6-4D4A-B801-9D18AB92C834}']
    procedure setAccessible(flag: Boolean); cdecl; overload;

    function equals(other: JObject): Boolean; cdecl;
    function getAnnotation(annotationType: JOwnLang_Class): JAnnotation; cdecl;
    function getAnnotations: TJavaObjectArray<JAnnotation>; cdecl;
    function getDeclaredAnnotations: TJavaObjectArray<JAnnotation>; cdecl;
    function getDeclaringClass: JOwnLang_Class; cdecl;
    function getDefaultValue: JObject; cdecl;
    function getExceptionTypes: TJavaObjectArray<JOwnLang_Class>; cdecl;
    function getGenericExceptionTypes: TJavaObjectArray<Jreflect_Type>; cdecl;
    function getGenericParameterTypes: TJavaObjectArray<Jreflect_Type>; cdecl;
    function getGenericReturnType: Jreflect_Type; cdecl;
    function getModifiers: Integer; cdecl;
    function getName: JString; cdecl;
    function getParameterAnnotations: TJavaObjectBiArray<JAnnotation>; cdecl;
    function getParameterTypes: TJavaObjectArray<JOwnLang_Class>; cdecl;
    function getReturnType: JOwnLang_Class; cdecl;
    function getTypeParameters: TJavaObjectArray<JTypeVariable>; cdecl;
    function hashCode: Integer; cdecl;
    function invoke(receiver: JObject; args: TJavaObjectArray<JObject>): JObject; cdecl;
    function isAnnotationPresent(annotationType: JOwnLang_Class): Boolean; cdecl;
    function isBridge: Boolean; cdecl;
    function isSynthetic: Boolean; cdecl;
    function isVarArgs: Boolean; cdecl;
    function toGenericString: JString; cdecl;
    function toString: JString; cdecl;
  end;
  TJOwnMethod = class(TJavaGenericImport<JOwnMethodClass, JOwnMethod>) end;

  JOwnLang_ClassClass = interface(JObjectClass)
    ['{E1A7F20A-FD87-4D67-9469-7492FD97D55D}']
    {class} function forName(className: JString): JOwnLang_Class; cdecl; overload;
    {class} function forName(className: JString; shouldInitialize: Boolean; classLoader: JClassLoader): JOwnLang_Class; cdecl; overload;
  end;

  [JavaSignature('java/lang/Class')]
  JOwnLang_Class = interface(JObject)
    ['{B056EDE6-77D8-4CDD-9864-147C201FD87C}']
    function asSubclass(c: JOwnLang_Class): JOwnLang_Class; cdecl;
    function cast(obj: JObject): JObject; cdecl;
    function desiredAssertionStatus: Boolean; cdecl;
    function getAnnotation(annotationType: JOwnLang_Class): JAnnotation; cdecl;
    function getAnnotations: TJavaObjectArray<JAnnotation>; cdecl;
    function getCanonicalName: JString; cdecl;
    function getClassLoader: JClassLoader; cdecl;
    function getClasses: TJavaObjectArray<JOwnLang_Class>; cdecl;
    function getComponentType: JOwnLang_Class; cdecl;
    function getConstructors: TJavaObjectArray<JConstructor>; cdecl;
    function getDeclaredAnnotations: TJavaObjectArray<JAnnotation>; cdecl;
    function getDeclaredClasses: TJavaObjectArray<JOwnLang_Class>; cdecl;
    function getDeclaredConstructors: TJavaObjectArray<JConstructor>; cdecl;
    function getDeclaredField(name: JString): JField; cdecl;
    function getDeclaredFields: TJavaObjectArray<JField>; cdecl;
    function getDeclaredMethod(name: JString; parameterTypes: TJavaObjectArray<JOwnLang_Class>): JOwnMethod; cdecl;
    function getDeclaredMethods: TJavaObjectArray<JOwnMethod>; cdecl;
    function getDeclaringClass: JOwnLang_Class; cdecl;
    function getEnclosingClass: JOwnLang_Class; cdecl;
    function getEnclosingConstructor: JConstructor; cdecl;
    function getEnclosingMethod: JOwnMethod; cdecl;
    function getEnumConstants: TJavaObjectArray<JObject>; cdecl;
    function getField(name: JString): JField; cdecl;
    function getFields: TJavaObjectArray<JField>; cdecl;
    function getGenericInterfaces: TJavaObjectArray<Jreflect_Type>; cdecl;
    function getGenericSuperclass: Jreflect_Type; cdecl;
    function getInterfaces: TJavaObjectArray<JOwnLang_Class>; cdecl;
    function getMethods: TJavaObjectArray<JOwnMethod>; cdecl;
    function getModifiers: Integer; cdecl;
    function getName: JString; cdecl;
    function getPackage: JPackage; cdecl;
    //function getProtectionDomain: JProtectionDomain; cdecl;
    //function getResource(resourceName: JString): JURL; cdecl;
    function getResourceAsStream(resourceName: JString): JInputStream; cdecl;
    function getSigners: TJavaObjectArray<JObject>; cdecl;
    function getSimpleName: JString; cdecl;
    function getSuperclass: JOwnLang_Class; cdecl;
    function getTypeParameters: TJavaObjectArray<JTypeVariable>; cdecl;
    function isAnnotation: Boolean; cdecl;
    function isAnnotationPresent(annotationType: JOwnLang_Class): Boolean; cdecl;
    function isAnonymousClass: Boolean; cdecl;
    function isArray: Boolean; cdecl;
    function isAssignableFrom(c: JOwnLang_Class): Boolean; cdecl;
    function isEnum: Boolean; cdecl;
    function isInstance(object_: JObject): Boolean; cdecl;
    function isInterface: Boolean; cdecl;
    function isLocalClass: Boolean; cdecl;
    function isMemberClass: Boolean; cdecl;
    function isPrimitive: Boolean; cdecl;
    function isSynthetic: Boolean; cdecl;
    function newInstance: JObject; cdecl;
    function toString: JString; cdecl;
  end;
  TJOwnLang_Class = class(TJavaGenericImport<JOwnLang_ClassClass, JOwnLang_Class>) end;

implementation

end.

第三,您必须在使用权限

中启用 PROCESS_OUTGOING_CALL和READ_PHONE_STATE 项目

第四,您必须为Form1创建代码:

unit Unit1;

interface

uses
  System.SysUtils,
  System.Types,
  System.UITypes,
  System.Classes,
  System.Variants,

  FMX.Types,
  FMX.Controls,
  FMX.Forms,
  FMX.Graphics,
  FMX.Dialogs,
  FMX.Controls.Presentation,
  FMX.ScrollBox,
  FMX.Memo,

  CSBroadcastReceiver,
  Androidapi.JNI.JavaTypes.Own,

  Androidapi.Jni,
  AndroidApi.JNI.GraphicsContentViewText,
  Androidapi.Jni.JavaTypes,
  Androidapi.JNI.Os,
  Androidapi.JNIBridge,
  Androidapi.JNI.Telephony;

type
  TForm1 = class(TForm)
    Memo1: TMemo;
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private declarations }
    procedure CreateBroadcastReceiver;
    procedure BroadcastReceiverOnReceive(csContext: JContext; csIntent: JIntent);
    procedure CheckPhoneCallState(Context: JContext; Intent: JIntent);
    function KillCall(Context: JContext): Boolean;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  BroadcastReceiver: TCSBroadcastReceiver;

implementation

uses
  Androidapi.Jni.App, Androidapi.Helpers, Androidapi.Log;

{$R *.fmx}

procedure TForm1.CreateBroadcastReceiver;
begin
  if not Assigned(BroadcastReceiver) then
    begin
      BroadcastReceiver:= TCSBroadcastReceiver.Create(nil);
      BroadcastReceiver.OnReceive:= BroadcastReceiverOnReceive;
      BroadcastReceiver.RegisterReceive;
      BroadcastReceiver.Add('android.intent.action.PHONE_STATE');
      BroadcastReceiver.Add('android.intent.action.NEW_OUTGOING_CALL');
    end;
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  if Assigned(BroadcastReceiver) then
    BroadcastReceiver.Free;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  CreateBroadcastReceiver;
end;

procedure TForm1.BroadcastReceiverOnReceive(csContext: JContext; csIntent: JIntent);
begin
  CheckPhoneCallState(csContext, csIntent);
end;

procedure TForm1.CheckPhoneCallState(Context: JContext; Intent: JIntent);
var
  telephonyService: JObject;
  telephonyManager: JTelephonyManager;
  state: JString;
  incomingCallNumber: string;
  outgoingCallNumber: string;
  outputResult: string;
begin
  outputResult:= #13#10;

  telephonyService := TAndroidHelper.Context.getSystemService(TJContext.JavaClass.TELEPHONY_SERVICE);
  telephonyManager := TJTelephonyManager.Wrap((telephonyService as ILocalObject).GetObjectID);

  if JStringToString(Intent.getAction).Equals('android.intent.action.PHONE_STATE') then
    begin
      state:= Intent.getStringExtra(TJTelephonyManager.JavaClass.EXTRA_STATE);

      if state.equals(TJTelephonyManager.JavaClass.EXTRA_STATE_IDLE) then
        outputResult:= outputResult + 'Phone is IDLE ' + #13#10
      else if state.equals(TJTelephonyManager.JavaClass.EXTRA_STATE_RINGING) then
        begin
          incomingCallNumber:= JStringToString(Intent.getStringExtra(TJTelephonyManager.JavaClass.EXTRA_INCOMING_NUMBER));
          if incomingCallNumber.Equals('') then
            incomingCallNumber:= 'PRIVATE NUMBER';

          outputResult:= outputResult + 'Phone is RINGING' + #13#10;
          outputResult:= outputResult + 'Incoming call from ' + incomingCallNumber + #13#10;
          if incomingCallNumber = 'xyz' then
            if KillCall(Context) then
              outputResult:= outputResult + 'Call was terminated' + #13#10
            else
              outputResult:= outputResult + 'Call was not terminated' + #13#10;
        end
      else if state.equals(TJTelephonyManager.JavaClass.EXTRA_STATE_OFFHOOK) then
        outputResult:= outputResult + 'Phone is OFFHOOK' + #13#10;
    end
  else if JStringToString(Intent.getAction).Equals('android.intent.action.NEW_OUTGOING_CALL') then
    begin
      outgoingCallNumber:= JStringToString(Intent.getStringExtra(TJIntent.JavaClass.EXTRA_PHONE_NUMBER));
      outputResult:= outputResult + 'Outgoing call to ' + outgoingCallNumber + #13#10;
      if outgoingCallNumber = 'xyz' then
        begin
          BroadcastReceiver.SetResultData(nil);
          outputResult:= outputResult + 'Call is not allowed to ' + outgoingCallNumber + #13#10;
        end;
    end;

  Memo1.Lines.Append(outputResult);
end;

function TForm1.KillCall(Context: JContext): Boolean;
var
  telephonyService: JObject;
  classTelephony: JOwnLang_Class;
  methodGetITelephony: JOwnMethod;
  telephonyInterface: JObject;
  telephonyInterfaceClass: JOwnLang_Class;
  methodEndCall: JOwnMethod;
begin
  try
    telephonyService:= TAndroidHelper.Context.getSystemService(TJContext.JavaClass.TELEPHONY_SERVICE);
    classTelephony := TJOwnLang_Class.JavaClass.forName(telephonyService.getClass.getName);
    methodGetITelephony:= classTelephony.getDeclaredMethod(StringToJString('getITelephony'), nil);
    methodGetITelephony.setAccessible(True);
    telephonyInterface := methodGetITelephony.invoke(telephonyService, nil);
    telephonyInterfaceClass := TJOwnLang_Class.JavaClass.forName(telephonyInterface.getClass.getName);
    methodEndCall:= telephonyInterfaceClass.getDeclaredMethod(StringToJString('endCall'), nil);
    methodEndCall.invoke(telephonyInterface, nil);
    Result:= True;
  except
    on E: Exception do
    begin
      Result := False;
    end;
  end;
end;
end.

您可以下载完整的演示代码here