Jump to content
MikeMon

ZXing Delphi for 10.4

Recommended Posts

Hi

 

Does anyone know if ZXing Delphi is compatible with Delphi 10.4?

 

or is there a version for Delphi 10.4?

Share this post


Link to post

https://github.com/Spelt/ZXing.Delphi

It is compatible with 10.4 (although the readmeinforms support for delphi 10.3.3)

 

 I have to inform you that there are some leaks in the spelt code, nothing that you cannot solve.

  • Like 1

Share this post


Link to post

Just a information: The ZXing is a crossplatform solution, and is simple to implement, but is not the best choose. In Android I prefer the GoogleVision and in iOS I prefer use the apis of the AVFoundation, you will even avoid using TCamera, because it is very slow, very.

  • Like 2

Share this post


Link to post
9 hours ago, vfbb said:

Just a information: The ZXing is a crossplatform solution, and is simple to implement, but is not the best choose. In Android I prefer the GoogleVision and in iOS I prefer use the apis of the AVFoundation, you will even avoid using TCamera, because it is very slow, very.

Hi @vfbb

 

Thank you for your information. Will look into it.

Share this post


Link to post

Unfortunately, the ZXing is not working on Delphi 10.4. On iOS, the camera isn't working. On Android, the app is crashing with a floating point error.

Share this post


Link to post

Mike, if your notebook/desktop have a webcam test in windows. Your problem on mobile can be related to the permission to have access to the camera and not in relation to the ZXing code. Here I use it only in windows, and with Delphi 10.4 is working fine.

Share this post


Link to post
2 minutes ago, vfbb said:

Mike, if your notebook/desktop have a webcam test in windows. Your problem on mobile can be related to the permission to have access to the camera and not in relation to the ZXing code. Here I use it only in windows, and with Delphi 10.4 is working fine.

Hi. Thank you for your answer. The code working on Delphi 10.3.3 is crashing on Delphi 10.4. Btw, if the user hasn't given permission to access the camera yet, he/she will be asked to give one. That works quite fine on Delphi 10.3.3.

Share this post


Link to post
7 minutes ago, MikeMon said:

Hi. Thank you for your answer. The code working on Delphi 10.3.3 is crashing on Delphi 10.4. Btw, if the user hasn't given permission to access the camera yet, he/she will be asked to give one. That works quite fine on Delphi 10.3.3.

Ok, but the permission will be show just at first time. If your user negate the permission once, he will need to give the permission manually at the Settings of the iOS. In iOS go to the Settings > Your App > enable Camera, and try again

Share this post


Link to post
33 minutes ago, vfbb said:

Ok, but the permission will be show just at first time. If your user negate the permission once, he will need to give the permission manually at the Settings of the iOS. In iOS go to the Settings > Your App > enable Camera, and try again

It's not even asking the first time. Unfortunately.

Share this post


Link to post

Mike, but the ZXing don't ask the permission. You need to do this by your self. I don't know why it is working for you in 10.3, probably the TCamera did it internally.

I can't send you my entire code, but you will understand how it works by seeing part of the code:

  TipiOSUtils = class(TipFMXUtils, IipiOSUtils)
  private type
    { IipRequestCameraPermissionDelegate }

    IipRequestCameraPermissionDelegate = interface(IipInterface)
      procedure RequestAccessResult(AAuthorized: Boolean);
    end;

    { TipRequestCameraPermissionDelegate }

    TipRequestCameraPermissionDelegate = class(TipInterfacedObject, IipRequestCameraPermissionDelegate)
    private
      FStatusProc: TipPermissionStatusProc;
    public
      constructor Create(AStatusProc: TipPermissionStatusProc);
      procedure RequestAccessResult(AAuthorized: Boolean);
    end;
  {$IFDEF DELPHI_FIXES}
  private class var
    FCurrentAlert: UIAlertController;
    FRequestCameraPermissionDelegate: IipRequestCameraPermissionDelegate;
    class procedure AlertCancel(AAction: UIAlertAction);
    class procedure AlertSettings(AAction: UIAlertAction);
    class procedure RequestAccessResult(AAuthorized: Boolean);
  {$ENDIF}
  
// ....

{$IFDEF DELPHI_FIXES}
class procedure TipiOSUtils.AlertCancel(AAction: UIAlertAction);
var
  LRequestCameraPermissionDelegate: IipRequestCameraPermissionDelegate;
begin
  LRequestCameraPermissionDelegate := FRequestCameraPermissionDelegate;
  FRequestCameraPermissionDelegate := nil;
  if Assigned(LRequestCameraPermissionDelegate) then
    LRequestCameraPermissionDelegate.RequestAccessResult(False);
end;

class procedure TipiOSUtils.AlertSettings(AAction: UIAlertAction);
var
  LRequestCameraPermissionDelegate: IipRequestCameraPermissionDelegate;
begin
  GiOSUtils.OpenAppSettings;
  LRequestCameraPermissionDelegate := FRequestCameraPermissionDelegate;
  FRequestCameraPermissionDelegate := nil;
  if Assigned(LRequestCameraPermissionDelegate) then
    LRequestCameraPermissionDelegate.RequestAccessResult(False);
end;
{$ENDIF}

{$IFDEF DELPHI_FIXES}
class procedure TipiOSUtils.RequestAccessResult(AAuthorized: Boolean);
begin
  TipTask.Queue(
    procedure()
    var
      LRequestCameraPermissionDelegate: IipRequestCameraPermissionDelegate;
    begin
      LRequestCameraPermissionDelegate := FRequestCameraPermissionDelegate;
      FRequestCameraPermissionDelegate := nil;
      if Assigned(LRequestCameraPermissionDelegate) then
        LRequestCameraPermissionDelegate.RequestAccessResult(AAuthorized);
    end);
end;
{$ENDIF}

procedure TipiOSUtils.RequestCameraPermission(const ADescription: string;
  AStatusProc: TipPermissionStatusProc);
{$IFDEF DELPHI_FIXES}
var
  LCancelAction: UIAlertAction;
  LWindow: UIWindow;
{$ENDIF}
begin
  if not Assigned(AStatusProc) then
    Exit;
  {$IFDEF DELPHI_FIXES}
  case TAVCaptureDevice.OCClass.authorizationStatusForMediaType(AVMediaTypeVideo) of
    AVAuthorizationStatusNotDetermined:
      begin
        FRequestCameraPermissionDelegate := TipRequestCameraPermissionDelegate.Create(AStatusProc);
        TAVCaptureDevice.OCClass.requestAccessForMediaType(AVMediaTypeVideo, RequestAccessResult);
      end;
    AVAuthorizationStatusDenied:
      begin
        LWindow := TiOSHelper.SharedApplication.keyWindow;
        if (LWindow = nil) or (LWindow.rootViewController = nil) then
        begin
         AStatusProc(TPermissionStatus.Denied);
          Exit;
        end;
        FRequestCameraPermissionDelegate := TipRequestCameraPermissionDelegate.Create(AStatusProc);
        FCurrentAlert := TUIAlertController.Wrap(TUIAlertController.OCClass.alertControllerWithTitle(
          StrToNSStr(FMXLanguage.GetPermissionTitle),
          StrToNSStr(ADescription + #13#10 + FMXLanguage.GetiOSPermissionMessageText),
          UIAlertControllerStyleAlert
        ));
        FCurrentAlert.addAction(TUIAlertAction.Wrap(TUIAlertAction.OCClass.actionWithTitle(StrToNSStr(FMXLanguage.GetPermissionSettingsButtonText), UIAlertActionStyleDefault, AlertSettings)));
        LCancelAction := TUIAlertAction.Wrap(TUIAlertAction.OCClass.actionWithTitle(StrToNSStr(FMXLanguage.GetPermissionCancelButtonText), UIAlertActionStyleCancel, AlertCancel));
        FCurrentAlert.addAction(LCancelAction);
        FCurrentAlert.setPreferredAction(LCancelAction);
        LWindow.rootViewController.presentViewController(FCurrentAlert, True, nil);
      end;
    AVAuthorizationStatusAuthorized: AStatusProc(TPermissionStatus.Granted);
    AVAuthorizationStatusRestricted: AStatusProc(TPermissionStatus.PermanentlyDenied);
  else
    AStatusProc(TPermissionStatus.Denied);
  end;
  {$ELSE}
  AStatusProc(TPermissionStatus.Denied);
  {$ENDIF}
end;

 

Share this post


Link to post

What I doing? If the status is not determined (first time) I request the access (you can request just if the status is not determined). If is denied I simple show a message informing to go to the Settings and enable the permission manually (all aps do this, example: Telegram). 

Share this post


Link to post
9 hours ago, vfbb said:

What I doing? If the status is not determined (first time) I request the access (you can request just if the status is not determined). If is denied I simple show a message informing to go to the Settings and enable the permission manually (all aps do this, example: Telegram). 

 

Hi. I'm using the code provided in the uMain.pas unit of the advancedTestApp project. This is some of the code for the camera permission:

// Optional rationale display routine to display permission requirement rationale to the user
procedure TFormMain.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
  TDialogService.Showmessage('The app needs to access the camera in order to work',
    procedure(const AResult: TModalResult)
    begin
      APostRationaleProc
    end)
end;



procedure TFormMain.AccessCameraPermissionRequestResult(Sender: TObject; const APermissions: TArray<string>; const AGrantResults: TArray<TPermissionStatus>);
begin
  // 1 permission involved: CAMERA
  if (Length(AGrantResults) = 1) and (AGrantResults[0] = TPermissionStatus.Granted) then
  { Fill the resolutions. }
  begin
    CreateCamera();
  end
  else
    Showmessage('Cannot access the camera because the required permission has not been granted')
end;



procedure TFormMain.FormActivate(Sender: TObject);
begin
{$IFDEF ANDROID}
  FPermissionCamera := JStringToString(TJManifest_permission.JavaClass.CAMERA);
{$ENDIF}
  PermissionsService.RequestPermissions([FPermissionCamera], AccessCameraPermissionRequestResult, DisplayRationale);
end;



procedure TFormMain.StartCapture;
begin

  FBuffer.Clear(TAlphaColors.White);
  FActive := True;
  LabelFPS.Text := 'Starting capture...';
  PermissionsService.RequestPermissions([FPermissionCamera], ActivateCameraPermissionRequestResult, DisplayRationale);

  StartStopWatch();
  lblScanning.Text := 'Scanning on';
  FaLblScanning.Enabled := True;
end;



procedure TFormMain.ActivateCameraPermissionRequestResult(Sender: TObject; const APermissions: TArray<string>; const AGrantResults: TArray<TPermissionStatus>);
begin
  // 1 permission involved: CAMERA
  if (Length(AGrantResults) = 1) and (AGrantResults[0] = TPermissionStatus.Granted) then
  begin
    { Turn on the Camera }
    CreateCamera();
    FCamera.Active := True;
  end
  else
    Showmessage('Cannot start the camera because the required permission has not been granted')
end;



procedure TFormMain.CreateCamera();
begin
  if (FCamera <> nil) then
    exit;

  FCamera := TCameraComponent.Create(self);
  FCamera.Quality := TVideoCaptureQuality.MediumQuality;
  FCamera.FocusMode := TFocusMode.AutoFocus;

  FCamera.OnSampleBufferReady := CameraComponent1SampleBufferReady;
end;

 

Share this post


Link to post

Mike, the "PermissionsService .RequestPermissions" works only in Android, in others platforms it have not been implemented, will simple skip.

 

The code I gave you will work perfectly, but I think you are having trouble adapting it. So I simplified things for you:

{$IFDEF iOS}
type
  TipPermissionStatusProc = reference to procedure(AStatus: TPermissionStatus);

  { TipiOSUtils }

  TipiOSUtils = class
  private type
    { IipRequestCameraPermissionDelegate }

    IipRequestCameraPermissionDelegate = interface(IipInterface)
      procedure RequestAccessResult(AAuthorized: Boolean);
    end;

    { TipRequestCameraPermissionDelegate }

    TipRequestCameraPermissionDelegate = class(TipInterfacedObject, IipRequestCameraPermissionDelegate)
    private
      FStatusProc: TipPermissionStatusProc;
    public
      constructor Create(AStatusProc: TipPermissionStatusProc);
      procedure RequestAccessResult(AAuthorized: Boolean);
    end;
  end;

uses
  System.Classes,
  FMX.Dialogs,
  iOSapi.AVFoundation;

{ TipiOSUtils.TipRequestCameraPermissionDelegate }

constructor TipiOSUtils.TipRequestCameraPermissionDelegate.Create(
  AStatusProc: TipPermissionStatusProc);
begin
  inherited Create;
  FStatusProc := AStatusProc;
end;

procedure TipiOSUtils.TipRequestCameraPermissionDelegate.requestAccessResult(
  AAuthorized: Boolean);
begin
  if AAuthorized then
    FStatusProc(TPermissionStatus.Granted)
  else
    FStatusProc(TPermissionStatus.Denied);
end;

{ TipiOSUtils }

class procedure TipiOSUtils.RequestAccessResult(AAuthorized: Boolean);
begin
  TThread.Queue(nil,
    procedure()
    var
      LRequestCameraPermissionDelegate: IipRequestCameraPermissionDelegate;
    begin
      LRequestCameraPermissionDelegate := FRequestCameraPermissionDelegate;
      FRequestCameraPermissionDelegate := nil;
      if Assigned(LRequestCameraPermissionDelegate) then
        LRequestCameraPermissionDelegate.RequestAccessResult(AAuthorized);
    end);
end;

procedure TipiOSUtils.RequestCameraPermission(AStatusProc: TipPermissionStatusProc);
begin
  case TAVCaptureDevice.OCClass.authorizationStatusForMediaType(AVMediaTypeVideo) of
    AVAuthorizationStatusNotDetermined:
      begin
        FRequestCameraPermissionDelegate := TipRequestCameraPermissionDelegate.Create(AStatusProc);
        TAVCaptureDevice.OCClass.requestAccessForMediaType(AVMediaTypeVideo, RequestAccessResult);
      end;
    AVAuthorizationStatusDenied: AStatusProc(TPermissionStatus.Denied);
    AVAuthorizationStatusAuthorized: AStatusProc(TPermissionStatus.Granted);
    AVAuthorizationStatusRestricted: AStatusProc(TPermissionStatus.PermanentlyDenied);
  else
    AStatusProc(TPermissionStatus.Denied);
  end;
end;
{$ENDIF}

And to use, replace your TFormMain.FormActivate by this:

{$IFDEF iOS}
var 
  LiOSUtils: TipiOSUtils.Create;
{$ENDIF}

procedure TFormMain.FormActivate(Sender: TObject);
begin
{$IFDEF ANDROID}
  FPermissionCamera := JStringToString(TJManifest_permission.JavaClass.CAMERA);
  PermissionsService.RequestPermissions([FPermissionCamera], AccessCameraPermissionRequestResult, DisplayRationale);
{$ELSEIF defined(iOS)}
  LiOSUtils: TipiOSUtils.Create;
  LiOSUtils.RequestCameraPermission(
    procedure(AStatus: TPermissionStatus)
    begin
      if AStatus = TPermissionStatus.Granted then
        CreateCamera()
      else
        Showmessage('You need to enable the Camera permission in system Settings. Please go to the Settings > MyApp > enable Camera.');
    end);
{$ENDIF}
end;

Note: To test the permission you need to unistall your app before compile.

Edited by vfbb
Fixed the showmessage

Share this post


Link to post
1 hour ago, vfbb said:

Mike, the "PermissionsService .RequestPermissions" works only in Android, in others platforms it have not been implemented, will simple skip.

 

The code I gave you will work perfectly, but I think you are having trouble adapting it. So I simplified things for you:

And to use, replace your TFormMain.FormActivate by this:

Note: To test the permission you need to unistall your app before compile.

 

1.) Thank you for your valuable help. I will check the iOS code above. Just FYI, the Android version is not working, either. When running the advancedTestApp, though it IS asking for permission and I'm choosing yes, the minute I'm using the "Scan off/on" button, the app is freezing.

 

2.) In addition to the above, I'm getting the following error on some Android devices and using Delphi 10.3.3, Assertion failure (FastUtils.pas, line 172), which is 

Assert((ASrcHeight and 3) = 0);

Any ideas?

Share this post


Link to post

@MikeMon Today I don't have an android device to check it, but first I would check 2 things in andoird:

1) the Porject Options > Uses Permission > Camera = true.

2) Test if the "TFormMain.AccessCameraPermissionRequestResult" is executing in the main thread with LIsMainThread := TThread.Current.ThreadID = MainThreadID;

 

About Delphi 10.3.3 I do not use anymore and the code of iOS that I posted will work only in 10.4 because the iOSapi.AVFoundation.pas of the 10.3.3 not have the apis of media permission.

Share this post


Link to post
24 minutes ago, vfbb said:

@MikeMon Today I don't have an android device to check it, but first I would check 2 things in andoird:

1) the Porject Options > Uses Permission > Camera = true.

2) Test if the "TFormMain.AccessCameraPermissionRequestResult" is executing in the main thread with LIsMainThread := TThread.Current.ThreadID = MainThreadID;

 

About Delphi 10.3.3 I do not use anymore and the code of iOS that I posted will work only in 10.4 because the iOSapi.AVFoundation.pas of the 10.3.3 not have the apis of media permission.

Camera is true and the AccessCameraPermissionRequestResult IS in the main thread.

 

The error on 10.3.3 is on Android ONLY.

Share this post


Link to post

After some testing, I found out the following:

 

The advancedTestApp is crashing on Android and not compiling on iOS using Delphi 10.4. The reason is that the FMX.Media.Android.pas and FMX.Media.AVFoundation.pas (and maybe the FastUtils.pas) provided by ZXing Delphi are not compatible with Delphi 10.4. AdvancedTestApp works just fine with the original files coming with Delphi 10.4. But unfortunately, by using the originals, the app is VERY slow both on iOS and Android.

 

Any third party camera controls recommended for Firemonkey (Android and iOS) except winsoft?

Edited by MikeMon

Share this post


Link to post
1 hour ago, MikeMon said:

FMX.Media.Android.pas and FMX.Media.AVFoundation.pas (and maybe the FastUtils.pas) provided by ZXing Delphi 

They should not be providing entire source files. If they are, they're very likely violating copyright.

1 hour ago, MikeMon said:

Any third party camera controls recommended for Firemonkey (Android and iOS) except winsoft?

I'm working on one, which is a total revamp of this project: https://github.com/DelphiWorlds/Camera. If you're interested, please join my Slack team, here: https://slack.delphiworlds.com, and go to the #kastri channel.

Share this post


Link to post

@MikeMon I forgot that in Delphi Rio there was a bug in the android permissions and I noticed that it is still in Delphi Sydney. You have to fix the FMX.Platform.Android.pas

 

procedure TFMXNativeActivityListener.onRequestPermissionsResult(ARequestCode: Integer;
  APermissions: TJavaObjectArray<JString>; AGrantResults: TJavaArray<Integer>);
// -----------------------------------------------------------------------------
// [iPub - github.com/viniciusfbb] - 28/05/2020 - Delphi 10.4
// The delphi messages system isn't thread-safe and this request result can be
// called in background threads
{$IFDEF DELPHI_FIXES} // -------------------------------------------------------
begin
  TThread.Queue(nil,
  procedure()
  var
    MsgData: TPermissionsRequestResultData;
    Msg: TPermissionsRequestResultMessage;
  begin
    MsgData.RequestCode := ARequestCode;
    MsgData.Permissions := APermissions;
    MsgData.GrantResults := AGrantResults;
    Msg := TPermissionsRequestResultMessage.Create(MsgData);
    TMessageManager.DefaultManager.SendMessage(nil, Msg);
  end);
{$ELSE} // ---------------------------------------------------------------------
var
  MsgData: TPermissionsRequestResultData;
  Msg: TPermissionsRequestResultMessage;
begin
  MsgData.RequestCode := ARequestCode;
  MsgData.Permissions := APermissions;
  MsgData.GrantResults := AGrantResults;
  Msg := TPermissionsRequestResultMessage.Create(MsgData);
  TMessageManager.DefaultManager.SendMessage(nil, Msg);
{$ENDIF} // --------------------------------------------------------------------
end;

 

Edited by vfbb
  • Like 1

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

×