为什么 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 为我指明了正确的方向!
使用 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 为我指明了正确的方向!