Integrando o SDK Na maior parte dos casos os fabricante disponibilizam um código de exemplo sobre como fazer a integração com seus dispositivos. Estes exemplos, assim como a biblioteca estarão desenvolvidos em Java. Como disse antes, saber pelo menos ler códigos fontes em Java será muito útil. A biblioteca que importamos expõem uma interface chamada JBluetoothService, ela quem faz toda a “mágica” para nós, dela iremos utilizar 4 funcionalidade em nosso projeto, que será: 1 - Listar os dispositivos pareados; 2 - Conexão com a Impressora; 3 - Status da Impressora; 4 - Envio de texto para impressão. Dessa forma começamos declarando uma variável Global Privada do tipo JBluetoothService, iremos chama-la de FService. // TForm1 = class(TForm) [...] private FService: JBluetoothService; A instância de nosso objeto principal se dará no clique do botão que listará os Devices pareados do mobile (btnDevices). JavaClass.Init() Quando trabalhamos com arquivos Bridge, onde nossas classe são de origem java, temos que esquecer um pouco a forma que estamos acostumado a instanciar nossos objetos. Ao invés de utilizarmos o Construtor padrão, o método Create, iremos utilizar o método JavaClass.Init(). A Instância da variavel FService, será feita através da classe TJBluetoothService, neste caso TJBluetoothService.JavaClass.init(...). // function init(P1: JContext; P2: JHandler): JBluetoothService; cdecl; O Método TJBluetoothService.JavaClass.Init exige dois parâmetros, “P1: JContext” e “P2: JHandler”. Grande parte dos dispositivos que são integrados no Android apenas exigem apenas que seja informado a Activity responsável pelo dispositivo. Este modelo possui uma implementação um pouco mais complexa, vejamos: No parâmetro "P1: JContext" iremos informar a Activity principal da nossa aplicação, para isso basta chamar o método MainActivity. No parâmetro "P2: JHandler" temos que informar um objeto do tipo Handle do Android. Esse Handle tem como objetivo manipular as mensagens/threads de um outro objeto especifico. Resumindo o Handle é o objeto que vai ficar "Escutando" as ações de um outro objeto, como se estivéssemos trabalhando com as mensagens do Windows para capturar um evento de um componente. Handle Android Para instanciarmos um Handle também chamaremos o método JavaClass.Init, e iremos precisar informar outros dois parâmetros, “looper: JLooper” e “callback: JHandler_Callback”. // function init(looper: JLooper; callback: JHandler_Callback): JHandler; O parametro Looper define quem será responsável por gerenciar a fila de execuções de mensagem, e o parâmetro Callback o responsável por capturar as mensagens executadas. Para parâmetro Looper vamos atribuir o Looper da aplicação mesmo, TJLooper.JavaClass.getMainLooper. Para o parametro Callback, que é o objeto que receberá as mensagens atribuídas ao Handle, não temos uma classe ou método que nos instancie ou retorne o objeto pronto, temos que criar no braço, o que não é nenhum bicho de 7 cabeça... Depois que você sabe o que está fazendo. JHandler_Callback Nossa classe de Callback ficará da seguinte forma: // THndCallback = class(TJavaLocal, JHandler_Callback) public constructor Create; function handleMessage(msg: JMessage): Boolean; cdecl; end; constructor THndCallback.Create; begin end; function THndCallback.handleMessage(msg: JMessage): Boolean; begin Result := True; end; Como nosso objetivo é fazer a implementação da impressora da forma mais simples possível não iremos implementar praticamente nada nesta classe. No método handleMessage implementaremos apenas o “result:= True”, para que não tenhamos nenhum Warning, mas seria aqui que você manipularia os eventos da impressora, como um possível OnConnect, por exemplo. este método lembra muito o método WndProc da VCL. Nesta classe também temos um detalhe muito importante, o Construtor, para criarmos uma classe de Callback o construtor da classe TJavaLocal não pode ser executado, então devemos declará-lo em nossa nova classe mesmo que vazio, o importante é que não devemos efetuar a chamada inherited no método. Agora temos todos os elementos necessário para iniciar nossa variável a instância do TJBluetoothService fica assim: // procedure TForm1.InstaciarServicoDeImpressao; var lHandler: JHandler; begin lHandler := TJHandler.JavaClass.init(TJLooper.JavaClass.getMainLooper, THndCallback.Create); FService := TJBluetoothService.JavaClass.init(MainActivity, lHandler); end; Com nossa variável FService instanciada, agora é efetuar a programação das funcionalidades propriamente ditas. Listando dispositivos pareados Agora vamos listar os dispositivos Bluetooth pareados. O Delphi já possui métodos que listam os dispositivos pareados, mas alguns fabricantes trabalham com classes de dispositivos própria, o que não é o caso do nosso fabricante, que retorna uma lista de objetos que implementam a inteface JBluetoothDevice, que é a interface base para Devices bluetooth no Delphi. Utilizaremos o método do fabricante e não o do Delphi, assim qualquer problema com a integração com a API da impressora será possível identificar logo na listagem dos dispositivos. O JBluetoothDevice possui o método getPairedDev, que retorna a lista dos dispositivos, dessa forma iremos declarar uma variável global para armazenar nossa lista. O tipo de retorno do método getPairedDev é um JSet; JSet No tipo JSet, a manipulação da lista é um pouco mais complexa que as que estamos acostumado a utilizar no Delphi. Para manipulá-lo utilizamos o método iterator do que por sua vez retorna um tipo JIterator, e é nele que percorremos os registros. Aí vai a receita de bolo: // var lIt: Jiterator; lDevicePareado: JBluetoothDevice; begin lIt:= FDeviceSet.iterator; while lIt.hasNext do begin lDevicePareado := TJBluetoothDevice.Wrap((lIt.next as ILocalObject).GetObjectID); Memo1.Lines.Add(jstringtostring(lDevicePareado.getName)+'='+ jstringtostring(lDevicePareado.getAddress)); if jstringtostring(lDevicePareado.getName) = 'HHW-UART-S10' then begin FDevice := lDevicePareado; Memo1.Lines.Add('Pareamento de impressora Localizado'); end; end; end; Cada registro no nosso iterator retorna um dispositivo pareado, que é atribuímos para a variável local lDevicePareado. Para visualizarmos os Devices pareados buscamos o Nome (lDevicePareado.getName) e o endereço de Mac (lDevicePareado.getAddress), e jogamos para o Memo na tela. Por fim identifico o nome do meu dispositivo referente a impressora e atribuo a uma variável global chamada FDevice. Estou identificando qual Device pareado é a impressora apenas efetuando um teste pelo nome do Pareamento, em sua aplicação você deve montar uma tela ou combo para que o usuário identifique qual dos itens da lista é a impressora e o selecione. Outro ponto importante que vale mencionar é que o método getPairedDev não vai localizar a sua impressora, ele apenas lista os dispositivos pareados, dessa forma o Android listará todos os dispositivos pareados, independente de estarem ligados ou não. Se listarmos os dispositivos mesmo com a impressora desligada ela poderá aparecer em nossa lista por já ter sido pareada anteriormente. Observe também que os retornos se dão em JString, ou seja Strings do Java, dessa forma devemos usar o método jstringtostring da unit Androidapi.Helpers para convertê-las em strings do Delphi. Conexão com a Impressora Tendo o Device mapeado vamos fazer a conexão, para isso basta chamar o método connect da variável FService passando como parâmetro o Device que queremos conectar. // FService.connect(FDevice); Ponto importante, as conexões com os dispositivos bluetooth demoram, em meus testes uma média de 2 segundo, o que representa quase um eternidade em termos de processamento, e o suficiente para você não poder efetuar um teste de impressão imediatamente após a conexão. Status da conexão Neste exemplo eu coloquei um botão para retornar o status da impressora, poderia testar através de um Timer ou mesmo na classe de Callback, mas por simplicidade coloquei em um botão. // function TForm1.ObterStatus: string; var lStateCod: integer; begin lStateCod := FService.getState; if lStateCod = TJBluetoothService.JavaClass.STATE_CONNECTED then Result := 'CONNECTED' else if lStateCod = TJBluetoothService.JavaClass.STATE_CONNECTING then Result := 'CONNECTING' else if lStateCod = TJBluetoothService.JavaClass.STATE_LISTEN then Result := 'LISTEN' else if lStateCod = TJBluetoothService.JavaClass.STATE_NONE then Result := 'NONE' else Result := 'Não identificado'; end; procedure TForm1.btnStatusClick(Sender: TObject); begin try Memo1.Lines.Add('Status: '+ ObterStatus); except on E:Exception do Memo1.Lines.Add(e.Message); end; end; Com este teste posso verificar a qualquer momento estado da impressora. Se ela estiver com o Status STATE_CONNECTED podemos enviar o comando de impressão. Impressão O Comando de Impressão por si só é extremamente simples: // procedure TForm1.Imprimir(pTexto: string); begin FService.sendMessage(StringToJString(pTexto),StringToJString('GBK')); end; procedure TForm1.btnImprimirClick(Sender: TObject); begin Imprimir(edit1.Text); end; Conclusão A integração de qualquer dispositivo bluetooth em aplicativos mobiles feitos em Delphi na maior parte das vezes pode ser feita de maneira muito simples e rápida, mas é preciso que o programador esteja familiarizado com a API do Android. Em nosso artigo mostramos que independente do SDK ser disponibilizado pelo fabricante apenas em Java, o Delphi possui estrutura para uma completa integração com de seus aplicativos mobiles e dispositivos bluetooth com um alto ganho de produtividade.