Um leitor de código de barras portátil com FMX e App Tethering

Screen Shot 2017-04-08 at 19.40.29

A inspiração para este exemplo veio da discussão com um cliente sobre opções para leitura de código de barras em aplicações mobile. Não somente utilizando a câmera do próprio celular, mas também possíveis integrações com leitores externos, suporte a RFID, etc.

Neste artigo vou abordar a captura e decodificação do código de barras pela câmera do celular, para Android e iOS. Nos próximos vamos falar da integração de leitores externos via Bluetooth, inclusive para leitores de RFID.

Uma vez capturado o código de barras, o segundo passo é transferir o mesmo para uma aplicação “host” que estará em execução no desktop, podendo utilizá-lo para pagamento de uma conta no seu internet banking, uma aplicação de entrada/saída de materiais, etc.

Barcode em Android e iOS

Muito já se publicou sobre isso, e existem diversas boas soluções e exemplos a um “Google” de distância, para Delphi e também C++ Builder.

Para este exemplo vou utilizar algumas abordagens de leitura que me pareceram sólidas, mas unificando os mesmos afim de simplificar sua utilização em um projeto multiplataforma.

Para cumprir esta tarefa, existem duas abordagens possíveis: bibliotecas externas dinâmicas e/ou estáticas, ou código nativo compilado diretamente em sua aplicação.

ZXing.Delphi

Iniciando pelo código nativo, o ZXing (zebra crossing) é provavelmente o projeto multiplataforma de código aberto mais ativo. A boa noticia é que, entre tantas outras linguagens, ele possui um “port” para Delphi, aqui: https://github.com/Spelt/ZXing.Delphi.

Sua integração ao FireMonkey é bastante simples, seja para iOS ou Android. O código utilizado em nosso app está baseado em um exemplo disponível no próprio repositório do ZXing.Delphi (folder aTestApp) mas basicamente estamos falando de algo assim:

FScanManager := TScanManager.Create(TBarcodeFormat.CODE_128, nil);
FReadResult := FScanManager.Scan(scanBitmap);

Fora isso, é adicionar todos os paths da biblioteca no Search Path do seu projeto e compilar. Para facilitar, aqui estão todos os paths a serem incluídos (obviamente você deverá ajustar de acordo com a localização em seu computador):

C:\COMPs\ZXing;C:\COMPs\ZXing\Filtering;C:\COMPs\ZXing\Common;C:\COMPs\ZXing\Common\Detector;C:\COMPs\ZXing\Common\ReedSolomon;C:\COMPs\ZXing\1D Barcodes;C:\COMPs\ZXing\2D Barcodes;C:\COMPs\ZXing\2D Barcodes\Decoder;C:\COMPs\ZXing\2D Barcodes\Detector;C:\COMPs\ZXing\2D Barcodes\Encoder

ZXing Android App

Uma outra alternativa, neste caso específico para Android, e também baseada no projeto ZXing, é uma app disponível no Google Play, a qual pode ser invocada de sua app Delphi via “Intent”. Sua integração requer a execução de um “Intent”, e o retorno é capturado via o retorno do “activity”. Para a criação desta classe minha referência foram os posts do MVP Brian Long, o qual escreve muito (e bem!) sobre integração com o ecossistema do Android. A classe resultante você encontra no exemplo que estou compartilhando logo abaixo.

O próximo passo é encontrar funcionalidade similar, com uma biblioteca externa para iOS, criando assim uma classe “similar” a utilizada pelo versão Android.

ZBar barcode reader para iOS

Esta biblioteca, também de código aberto, também está disponível para várias plataformas, entre elas iOS. Para integrar um módulo Xcode em uma aplicação Delphi faz-se necessário a criação de um “wrapper” deste assembly. Você encontra alguma explicação sobre isso aqui, mas não trata-se de um processo óbvio.

Para esta classe, tomei como exemplo a implementação feita pela TMS Software, um componente também de código aberto, o qual permite que sua app iOS faça a leitura de um barcode com apenas duas linhas de código. A partir da classe contida neste componente, apliquei algumas pequenas modificações, inclusive de nomenclatura, para criar uma classe similar a versão Android.

Importante observar que, para compilar esta classe, você precisa do módulo “libzbar.a” disponível juntamente com seu projeto, já que trata-se de um link estático. Você encontrará a versão 32 e 64 bits da biblioteca no repositório do ZBar, e também no download que estou disponibilizando.

A classe resultante também está disponível no exemplo abaixo, e a integração dela em seu app fica também extremamente simples:

  fFMXBarcode := TFMXBarcode.Create(Application);
  fFMXBarcode.OnGetResult := OnFMXBarcodeResult;
  ...
  fFMXBarcode.Show(False)

Este evento acima será o responsável por devolver o código de barras resultante, em ambas as implementações, iOS e Android.

App Tethering

A partir do momento em que temos o barcode capturado e disponível, não importando qual das metodologias acima você tenha escolhido, código nativo ou biblioteca externa, nosso próximo objetivo é enviá-lo para uma aplicação host, onde poderá ser utilizado para qualquer fim, como por exemplo, capturar o código de barras de uma conta a pagar no celular e “colar” o mesmo no browser do seu desktop, em um internet banking por exemplo.

Para tal, utilizaremos a tecnologia do App Tethering, disponível para Delphi e C++ Builder. Este framework permite o “pareamento” de aplicações em qualquer plataforma, via Wifi ou Bluetooth, e o envio de informações entre elas. Se você ainda não teve contato com o mesmo, no Delphi Academy temos um episódio completo sobre ele disponível.

Em nossa aplicação mobile, adicionamos um “TTetheringManager” e um “TTetheringAppProfile” e configuramos o protocolo para Network. Para iniciar o “pareamento”, basta uma linha de código:

  TetheringManager1.AutoConnect

E para enviar o código de barras capturado, temos o seguinte:

  TetheringAppProfile1.SendString(TetheringManager1.RemoteProfiles.First,
    'Barcode', edtResult.Text);

Criamos então uma app “host”, também em FireMonkey, a qual irá receber o código enviado, e copiá-lo para a memória de seu desktop, ficando o mesmo disponível para ser “colado” em qualquer lugar em que o necessite.

Nesta app também devemos adicionar um “TTetheringManager” e um “TTetheringAppProfile”. No evento “ResourceReceived” temos:

procedure TMainForm.TetheringAppProfile1ResourceReceived(const Sender: TObject;
  const AResource: TRemoteResource);
begin
  if AResource.Hint = 'Barcode' then
  begin
    edtResult.Text := AResource.Value.AsString;
    SetClipboard(edtResult.Text);
  end;
end;

E finalmente, este é o método que coloca a informação no clipboard de seu desktop, seja ele Windows ou macOS:

procedure TMainForm.SetClipboard(s: string);
var
  Svc: IFMXClipboardService;
begin
  if TPlatformServices.Current.SupportsPlatformService(IFMXClipboardService, Svc) then
    Svc.SetClipboard(s);
end;

Conclusão

Este exemplo mostra como o Delphi e o  C++ Builder são plataformas poderosas e completas. Com o mínimo de código você tem uma app rodando em Android e iOS, capturando códigos de barra, e enviando-o para uma aplicação “host” que poderá estar em Windows ou macOS. Tudo com uma linguagem, um código fonte, é para poucos!

Para encerrar, aqui você pode encontrar o código fonte da aplicação para estudos e, possivelmente, implementação de algo similar em sua própria solução:

Embarcadero Code Central:

https://cc.embarcadero.com/item/30760

Repositório GitHub:

https://github.com/flrizzato/Barcode

Código de Barras no Delphi para iOS!

Olá pessoal!

Recebemos muitas solicitações de clientes pedindo por um exemplo de captura de código de barras a partir de uma aplicação iOS criada com Delphi XE4. Neste artigo vou mostrar como ler e decodificar código de barras através de uma aplicação Delphi para iOS, executando em iPhone, iPad ou iPod!

A dinâmica do processo consiste em capturar uma imagem utilizando a câmera do dispositivo, e decodificar o código de barras contido nesta imagem utilizando uma biblioteca especializada.

Bibliotecas de Leitura de Código de Barras para iOS

Existe um sem número de bibliotecas para decodificação de código de barras com suporte a diversas plataformas, pagas e não pagas, com mais ou menos features, basta um search no Google para receber uma lista considerável a ser explorada. Neste caso estamos interessados em bibliotecas específicas para iOS, e geralmente elas são criadas em (e para uso com) Objective-C.

Assim sendo, qualquer que seja sua escolha, será necessário transcrever o cabeçalho da biblioteca para Pascal, utilizando um dos diversos métodos disponíveis. Nestes endereços você pode aprender mais sobre este processo:

http://alturl.com/v2wtx

http://alturl.com/y87xb

Para esta implementação estou fazendo uso da ZBar (http://zbar.sourceforge.net/). Trata-se de uma biblioteca open, bastante competente, e que possui seu header traduzido para Pascal por um desenvolvedor coreano chamado Simon Choi, e publicado em seu blog neste link: http://blog.naver.com/simonsayz/120175561755. *** Todos os créditos aqui para o Simon pelo excelente trabalho! ***

Implementando a Leitura do Código de Barras

Uma vez de posse da biblioteca (neste caso representada por um único assembly “libzbar.a”), e também com o código que expõe a interface da biblioteca ZBar em mãos, o que temos que fazer é implementar uma classe  Delphi que faz uso destes métodos, traduzindo-os em uma interface amigável, a qual será utilizada pela nossa aplicação iOS. Veja abaixo como ficou a declaração da classe:

  TZBarCode = class(TObject)
  private
    ZBarView: ZBarReaderView;
    ZBarEvent: TZBarReaderViewDelegate;
    FActive: Boolean;
    function GetActive: Boolean;
    procedure SetActive(value: Boolean);
    function GetOnBarCode: TOnBarCode;
    procedure SetOnBarCode(value: TOnBarCode);
  protected
    destructor Destroy; override;
  public
    constructor Create; virtual;
    procedure Free;
    procedure SetFrame(View: UIView; Frame: CGRect);
    property Active: Boolean Read GetActive Write SetActive;
    property OnBarCode: TOnBarCode Read GetOnBarCode Write SetOnBarCode;
  end;

Construindo a Interface da Aplicação

Do ponto de vista de interface visual temos o seguinte:

– Um TEdit (edtResult), o qual receberá o resultado da leitura do código de barras;

– Um TMemo (memImage), utilizado somente para definir a área onde a imagem do processo de captura será exibida (você poderia definir as coordenadas manualmente, mas o uso de um componente alinhado aos demais controles torna a interface mais dinâmica);

– Um TListBox (lstHistory), utilizado para armazenar o resultado das últimas leituras;

– Um TSwitch (swtONOFF), responsável por ativar o processo de captura;

– Um TButton (btnCopy), demonstrando como copiar o resultado para a memória;

– Um TButton (btnClear), responsável por eliminar os resultados das capturas efetuadas;

O resultado esperado você pode conferir nesta imagem:

Codificando o Formulário da App

Inicialmente, no escopo private da classe principal do form, declare uma variável que será utilizada para criar a instância da classe de captura, e um método que será atribuído ao evento de captura, quando ela ocorrer:

  private
    { Private declarations }
    ZBarCode: TZBarCode;
    procedure OnFindBarCode(Sender: TObject; BarCode: String);
  public

No método OnFindBarCode, basta atribuir o resultado que será recebido através do parâmetro BarCode ao nosso edtResult, além de armazená-lo no histórico:

procedure TMainForm.OnFindBarCode(Sender: TObject; BarCode: String);
begin
  edtResult.Text := BarCode;
  lstHistory.Items.Add(FormatDateTime('dd/mm/yyyy hh:nn:ss', Now) + ' - ' + BarCode);
end;

No evento OnSwitch do nosso TSwitch, vamos instanciar a classe, atribuir o método ao evento correspondente, definir a área de plotagem da imagem e ativar a captura:

procedure TMainForm.swtONOFFSwitch(Sender: TObject);
begin
  if not Assigned(ZBarCode) then
  begin
    ZBarCode := TZBarCode.Create;
    ZBarCode.OnBarCode := OnFindBarCode;
    ZBarCode.setFrame(WindowHandleToPlatform(Self.Handle).View,
      CGRectMake(memImage.Position.X, memImage.Position.Y, memImage.Width,
      memImage.Height));
  end;
  ZBarCode.Active := swtONOFF.IsChecked;
end;

E para completar, aqui está o código a ser adicionado nos dois botões restantes:

procedure TMainForm.butClearClick(Sender: TObject);
begin
  edtResult.Text := '';
  lstHistory.Items.Clear;
end;
procedure TMainForm.btnCopyClick(Sender: TObject);
begin
  edtResult.SelectAll;
  edtResult.CopyToClipboard;
end;

Um detalhe importante: devido a natureza da biblioteca utilizada (trata-se de um assembly para iOS/ARM) esta aplicação não funcionará no simulador, apenas em um dispositivo físico.

Aqui você tem uma imagem da aplicação sendo executada em um iPad:

Neste link você pode fazer o download do exemplo que está sendo executado acima: http://cc.embarcadero.com/item/29485

Espero que gostem e que seja útil em seus projetos, até a próxima!