I'm trying to create an Android app in Delphi 10.2 Tokyo that makes use of Camera2 API. I used Java2OP to create the interfaces that I need. Some of these interfaces are for callback/listeners. For example CameraDevice.StateCallback which Java2OP has translated to this:
JCameraDevice_StateCallbackClass = interface(JObjectClass)
['{3F5A7394-FD15-439C-9BFB-DF8D43F9F930}']
{class} function _GetERROR_CAMERA_DEVICE: Integer; cdecl;
{class} function _GetERROR_CAMERA_DISABLED: Integer; cdecl;
{class} function _GetERROR_CAMERA_IN_USE: Integer; cdecl;
{class} function _GetERROR_CAMERA_SERVICE: Integer; cdecl;
{class} function _GetERROR_MAX_CAMERAS_IN_USE: Integer; cdecl;
{class} function init: JCameraDevice_StateCallback; cdecl;
{class} property ERROR_CAMERA_DEVICE: Integer read _GetERROR_CAMERA_DEVICE;
{class} property ERROR_CAMERA_DISABLED: Integer read _GetERROR_CAMERA_DISABLED;
{class} property ERROR_CAMERA_IN_USE: Integer read _GetERROR_CAMERA_IN_USE;
{class} property ERROR_CAMERA_SERVICE: Integer read _GetERROR_CAMERA_SERVICE;
{class} property ERROR_MAX_CAMERAS_IN_USE: Integer read _GetERROR_MAX_CAMERAS_IN_USE;
end;
[JavaSignature('android/hardware/camera2/CameraDevice$StateCallback')]
JCameraDevice_StateCallback = interface(JObject)
['{3A3944F5-A71F-4CD6-98C6-04B8D65C3B52}']
procedure onClosed(camera: JCameraDevice); cdecl;//Deprecated
procedure onDisconnected(camera: JCameraDevice); cdecl;//Deprecated
procedure onError(camera: JCameraDevice; error: Integer); cdecl;//Deprecated
procedure onOpened(camera: JCameraDevice); cdecl;//Deprecated
end;
TJCameraDevice_StateCallback = class(TJavaGenericImport<JCameraDevice_StateCallbackClass, JCameraDevice_StateCallback>) end;
It is my understanding that I should combine TJavaLocal
with the interface that I want to use as callback/listener (in this case JCameraDevice_StateCallbackClass
). Here is what I have done:
unit CamDevStateCallback;
interface
uses
Androidapi.JNIBridge, android.hardware.camera2;
type
TCamera2Event = procedure(camera: JCameraDevice) of object;
TCamera2ErrorEvent = procedure(camera: JCameraDevice; error: Integer) of object;
TCamDevStateCallback = class(TJavaLocal, JCameraDevice_StateCallback)
protected
FOnClosed: TCamera2Event;
FOnDisconnected: TCamera2Event;
FOnError: TCamera2ErrorEvent;
FOnOpen: TCamera2Event;
public
procedure onClosed(camera: JCameraDevice); cdecl;
procedure onDisconnected(camera: JCameraDevice); cdecl;
procedure onError(camera: JCameraDevice; error: Integer); cdecl;
procedure onOpened(camera: JCameraDevice); cdecl;
class function CreateNew(aOnOpen, aOnClosed, aOnDisconnected: TCamera2Event; aOnError: TCamera2ErrorEvent): JCameraDevice_StateCallback;
property OnCameraClosed: TCamera2Event read FOnClosed write FOnClosed;
property OnCameraDisconnected: TCamera2Event read FOnDisconnected write FOnDisconnected;
property OnCameraError: TCamera2ErrorEvent read FOnError write FOnError;
property OnCameraOpen: TCamera2Event read FOnOpen write FOnOpen;
end;
implementation
{ TCamDevStateCallback }
class function TCamDevStateCallback.CreateNew(aOnOpen, aOnClosed, aOnDisconnected: TCamera2Event;
aOnError: TCamera2ErrorEvent): JCameraDevice_StateCallback;
var
tmpObj: TCamDevStateCallback;
begin
tmpObj := TCamDevStateCallback.Create;
tmpObj.OnCameraClosed := aOnClosed;
tmpObj.OnCameraDisconnected := aOnDisconnected;
tmpObj.OnCameraError := aOnError;
tmpObj.OnCameraOpen := aOnOpen;
Result := TJCameraDevice_StateCallback.Wrap((tmpObj as ILocalObject).GetObjectID);
end;
procedure TCamDevStateCallback.onClosed(camera: JCameraDevice);
begin
if Assigned(FOnClosed) then
FOnClosed(camera);
end;
procedure TCamDevStateCallback.onDisconnected(camera: JCameraDevice);
begin
if Assigned(FOnDisconnected) then
FOnDisconnected(camera);
end;
procedure TCamDevStateCallback.onError(camera: JCameraDevice; error: Integer);
begin
if Assigned(FOnError) then
FOnError(camera, error);
end;
procedure TCamDevStateCallback.onOpened(camera: JCameraDevice);
begin
if Assigned(FOnOpen) then
FOnOpen(camera);
end;
end.
This code does not compile because it complains the equals
, toString
differ from previous declaration and getClass
, notify
, notifyAll
and wait
are missing.
android.hardware.camera2.pas is created by Java2OP and contains the interface definition of JCameraDevice_StateCallback
and more.
The underlying problem here, as @DaveNottage has intimated, is that CameraDevice.StateCallback
is not a Java listener interface, but a Java callback class (albeit an abstract class that in some ways is therefore like an interface, but isn't an interface).
Because it is not a Java interface it is not a candidate for implementing with TJavaLocal
despite the fact that in Delphi it is represented by an interface (as all Java interfaces and classes are).
TJavaLocal
is for implementing Java interfaces, typically listener interfaces. It does not provide a means to inherit from a Java class (even an abstract Java class).
The clue is that the important interface created by Java2OP inherits from JObject
indicating that it is part of Java's Object
-based class hierarchy. A listener interface when imported inherits from IJavaInstance
.
Delphi doesn't support inheriting from Android Java classes (in contrast to its iOS support where Delphi does support inheriting from Objective-C classes, thanks to Objective-C being a native system unlike Android's managed Java VM system)