为什么 TTakePhotoFromCameraAction.OnDidFinishTaking 事件在 Android 10 上不起作用?

Why is the TTakePhotoFromCameraAction.OnDidFinishTaking event not working on Android 10?

使用 Delphi 10.4.2 FireMonkey,目标 Android 10 x64.

我正在尝试拍照并保存以供日后处理。

首先我尝试使用内置的TTakePhotoFromCameraAction,但是OnDidFinishTaking事件不起作用(相机屏幕可见,我可以拍照并按“确定” "), 我的程序出现了,但是我的 TakePhotoFinishTaking() 方法没有被调用。

procedure TMainForm.TakePhotoFinishTaking(Image: TBitmap);
begin
  DebugEnter(0, self, 'TakePhotoFinishTaking', [Image]);
  HidePopup;
  SetBilderActions(false);
  try
    PicGrid.RowCount := PicGrid.RowCount+1;
    InsertImageItem('', Image, PicGrid.RowCount-1);
  finally
    SetBilderActions(true);
  end;
  DebugLeave(0, self, 'TakePhotoFinishTaking');
end;

接下来我尝试直接使用IFMXCameraService

procedure TMainForm.btKameraClick(Sender: TObject);
var
  Service: IFMXCameraService;
  Params : TParamsPhotoQuery;
begin
  DebugEnter(0, self, 'btKameraClick', [Sender]);
  if PermissionsService.IsEveryPermissionGranted([cPermissionCamera, cPermissionReadExternalStorage, cPermissionWriteExternalStorage]) then begin
    if TPlatformServices.Current.SupportsPlatformService(IFMXCameraService, Service) then begin
      Params.RequiredResolution := TSize.Create(640, 640);
      Params.Editable           := false;
      Params.NeedSaveToAlbum    := true;
      Params.OnDidFinishTaking  := TakePhotoFinishTaking;
      Params.OnDidCancelTaking  := NIL;
      Service.TakePhoto(NIL, Params);
    end else begin
      FMX.DialogService.ASync.TDialogServiceAsync.MessageDialog('Kamerafunktion wird auf diesem Gerät nicht unterstützt!', TMsgDlgType.mtError, [TMsgDlgBtn.mbOK], TMsgDlgBtn.mbOK, 0)
    end;
  end else begin
    FMX.DialogService.ASync.TDialogServiceAsync.MessageDialog('Keine Kamera oder externer Speicher Rechte!', TMsgDlgType.mtError, [TMsgDlgBtn.mbOK], TMsgDlgBtn.mbOK, 0)
  end;
  DebugLeave(0, self, 'btKameraClick');
end;

相同的结果:我看到相机屏幕,拍照,按确定,我的程序返回,但没有 OnDidFinishTaking 事件。

然后我尝试拦截来自照片应用程序的消息:

FFinishTakingCamPhotoId   := TMessageManager.DefaultManager.SubscribeToMessage(TMessageDidFinishTakingImageFromCamera,  HandleMessage);
FFinishTakingLibPhotoId   := TMessageManager.DefaultManager.SubscribeToMessage(TMessageDidFinishTakingImageFromLibrary, HandleMessage);
FMessageReceivedImagePath := TMessageManager.DefaultManager.SubscribeToMessage(TMessageReceivedImagePath,               HandleMessage);

所有 subcribeToMessage() 调用都返回一个 ID,但从未为这些类型调用 HandleMessage()

此消息(例如)运行良好!

FOrientationChangedId     := TMessageManager.DefaultManager.SubscribeToMessage(TOrientationChangedMessage,              HandleMessage);
procedure TMainForm.HandleMessage(const Sender: TObject; const Msg: TMessage);
var
  ImgPath : String;
begin
  DebugEnter(0, self, 'HandleMessage', [Sender, Msg]);

  {$IFDEF Android}
  if Msg is TMessageResultNotification then begin
    DebugMsg(0, 'TMessageResultNotification');
    OnActivityResult(TMessageResultNotification(Msg).RequestCode, TMessageResultNotification(Msg).ResultCode, TMessageResultNotification(Msg).Value);
  end;
  if Msg is TMessageDidFinishTakingImageFromCamera then begin
    DebugMsg(0, 'TMessageDidFinishTakingImageFromCamera');
    TakePhotoFinishTaking(TMessageDidFinishTakingImageFromCamera(Msg).Value);
  end;
  if Msg is TMessageReceivedImagePath then begin
    DebugMsg(0, 'TMessageReceivedImagePath');
    ImgPath := TMessageReceivedImagePath(Msg).Value;
  end;
  if Msg is TMessageDidFinishTakingImageFromLibrary then begin
    DebugMsg(0, 'TMessageDidFinishTakingImageFromLibrary');
    TakePhotoFinishTaking(TMessageDidFinishTakingImageFromLibrary(Msg).Value);
  end;
  {$ENDIF}

我做错了什么?

您的应用未收到 TMessageDidFinishTakingImageFromCamera 消息的原因是,如果根据 [=] 中的代码将 OnDidFinishTaking 分配给 Params,则不会发送消息15=]:

procedure TImageManagerAndroid.DidReceiveBitmap(const Sender: TObject; const M: TMessage);
var
  ImagePath: string;
  Photo: TBitmap;
  RequestCode: Integer;
begin
  if M is TMessageReceivedImagePath then
  begin
    ImagePath := TMessageReceivedImagePath(M).Value;
    RequestCode := TMessageReceivedImagePath(M).RequestCode;
    Photo := TBitmap.CreateFromFile(ImagePath);
    try
      if Assigned(FParams.OnDidFinishTaking) then
        FParams.OnDidFinishTaking(Photo)
      else
      begin
        if RequestCode = TJFMXMediaLibrary.JavaClass.ACTION_TAKE_IMAGE_FROM_CAMERA then
          TMessageManager.DefaultManager.SendMessage(Self, TMessageDidFinishTakingImageFromCamera.Create(Photo));
        if RequestCode = TJFMXMediaLibrary.JavaClass.ACTION_TAKE_IMAGE_FROM_LIBRARY then
          TMessageManager.DefaultManager.SendMessage(Self, TMessageDidFinishTakingImageFromLibrary.Create(Photo));
      end;
    finally
      Photo.Free;
    end;
  end;
end;

下面的代码,本质上与你的代码相同,对我使用 Delphi 10.4.2 来说效果很好:

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.StdCtrls, FMX.Layouts, FMX.Objects;

type
  TForm1 = class(TForm)
    Layout1: TLayout;
    Button1: TButton;
    Image1: TImage;
    procedure Button1Click(Sender: TObject);
  private
    FCameraPermissions: TArray<string>;
    procedure CameraDidFinishTakingHandler(Image: TBitmap);
    procedure TakePhoto;
  public
    constructor Create(AOwner: TComponent); override;
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

uses
  System.Permissions,
  FMX.Platform.Android,
  FMX.MediaLibrary, FMX.Platform;

const
  cPermissionCamera = 'android.permission.CAMERA';
  cPermissionReadExternalStorage = 'android.permission.READ_EXTERNAL_STORAGE';
  cPermissionWriteExternalStorage = 'android.permission.WRITE_EXTERNAL_STORAGE';

constructor TForm1.Create(AOwner: TComponent);
begin
  inherited;
  FCameraPermissions := [cPermissionReadExternalStorage, cPermissionWriteExternalStorage, cPermissionCamera];
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  PermissionsService.RequestPermissions(FCameraPermissions,
    procedure(const APermissions: TArray<string>; const AGrantResults: TArray<TPermissionStatus>)
    begin
      if PermissionsService.IsEveryPermissionGranted(FCameraPermissions) then
        TakePhoto;
    end
  );
end;

procedure TForm1.TakePhoto;
var
  LCameraService: IFMXCameraService;
  LParams: TParamsPhotoQuery;
begin
  if TPlatformServices.Current.SupportsPlatformService(IFMXCameraService, LCameraService) then
  begin
    LParams.RequiredResolution := TSize.Create(640, 640);
    LParams.Editable := False;
    LParams.NeedSaveToAlbum := True;
    LParams.OnDidFinishTaking := CameraDidFinishTakingHandler;
    LParams.OnDidCancelTaking := nil;
    LCameraService.TakePhoto(nil, LParams);
  end;
end;

procedure TForm1.CameraDidFinishTakingHandler(Image: TBitmap);
begin
  Image1.Bitmap.Assign(Image);
end;

end.

好的 - 问题已解决。 这是一个旧的 Projekt,在 Androidmanifest-template.xml.

的应用程序节点中缺少选项 android:requestLegacyExternalStorage="true"

修复后一切正常,即使是带有 ObDidFinishTaking 事件的 TakePhotoFromCameraAction。

不幸的是,执行时没有错误或异常...

感谢@Dave 为我指明了正确的方向!