Formatando campos numéricos no FireMonkey

Para deixar mais completa a informação passada no vídeo acima, vamos implementar uma rotina de formação onde poderemos variar a máscara, aumentando as possibilidades de formatação.

Notem que a rotina mostrada no vídeo serve para a formatação de um campo de valor, enquanto apresentada nesse artigo vai servir para campos tipo telefone, CPF, CNPJ, etc.

Vamos incluir a function abaixo, ela fará a formatação conforme a máscara que for informada:

function TTestEdit.ApplyMask(aMask, aValue: String): String;
Var
   M, V  : Integer;
   Texto : String;
begin
Result := '';
Texto  := '';
aMask  := aMask.ToUpper;
for V := 0 to aValue.Length-1 do
   if aValue.Chars[V] In ['0'..'9'] Then
      Texto := Texto + aValue.Chars[V];
M := 0;
V := 0;
while (V < Texto.Length) And (M < aMask.Length) do
   Begin
   While aMask.Chars[M] <> '#' Do
      Begin
      Result := Result + aMask.Chars[M];
      Inc(M);
      End;
   Result := Result + Texto.Chars[V];
   Inc(M);
   Inc(V);
   End;
end;

para formatar um número de telefone, substituímos o evento OnTyping pelo a seguir:

procedure TTestEdit.Edit1Typing(Sender: TObject);
begin
TThread.Queue(Nil,
   Procedure
   begin
   Edit1.Text := ApplyMask('(##) #.####-####', Edit1.Text);
   Edit1.CaretPosition := Edit1.Text.Length;
   End);
end;

Um formulário com 4 TEdits, cada um chamando seu evento OnTyping aplicando as máscaras ‘(##) #.####-####’, ‘###.###.###-##’, “###.###.###/####-##’ e ‘#-(#)-(#)-#’ se comportará como no vídeo abaixo:

Em um próximo artigo vamos pegar nosso aprendizado de hoje e criar o componente TMaskEdit, colocando-o na paleta de componentes do Delphi.

Até a próxima!

Chamando a calculadora do Android de dentro de sua aplicação.

Neste post vamos ver como chamar não só a calculadora, mas qualquer outra aplicação, basta ter no nome do package.

Como diz meu amigo LandersonGomes, vamos usar o “coração do Android”. Os intents.

O Delphi tem a interface completa para o trabalho, basta adicionar no Uses de sua unit:


  {$IFDEF ANDROID}
  Androidapi.Jni,
  Androidapi.JNI.GraphicsContentViewText,
  Androidapi.JNI.provider,
  Androidapi.JNI.JavaTypes,
  Androidapi.JNI.Net,
  Androidapi.JNI.App,
  AndroidAPI.jNI.OS,
  Androidapi.JNIBridge,
  FMX.Helpers.Android,
  Androidapi.Helpers,
  FMX.Platform.Android,
  {$ENDIF ANDROID}

Agora, colocando em prática:

Procedure TForm1.CallAndroidCalculator;
Var
   Intent : JIntent;
   Pack   : JComponentName;
begin
Pack   := TJComponentName.JavaClass.init(StringToJString('com.android.calculator2'), StringToJString('com.android.calculator2.Calculator'));
Intent := TJIntent.JavaClass.init(TJIntent.JavaClass.ACTION_MAIN);
Intent.addCategory(TJIntent.JavaClass.CATEGORY_LAUNCHER);
Intent.setComponent(Pack);
TAndroidHelper.Activity.StartActivity(Intent);
End;

Simples não é? Em Java seria assim:

Intent intent = new Intent();
intent.setAction(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.setComponent(new ComponentName("com.android.calculator2",
"com.android.calculator2.Calculator"));
startActivity(intent);

Nesse link você encontra a lista completa de intents para os aplicativos do Google.

Uma fonte de informação muito valiosa é o tutorial escrito pelo Landerson Gomes sobre o uso dos intents com o Delphi.

https://www.landersongomes.com.br/embarcadero/delphi/intents-com-delphi-xe5-comunicando-apps-atraves-do-android?fbclid=IwAR13nkmOQayJaA81p1edBgCwgO02vrQnLhMePhbYg3pj9w5_AjXzfEHmES4

Espero que tenha sido útil. Até a próxima.

O tal do FileProvider do Android.

Hoje me deparei com a parada total dos compartilhamentos de fotos e documentos do meu app, tudo me retornava o erro “android.os.fileuriexposedexception”. Tudo funcionava normalmente até a instalação do update 1 do Delphi Rio.

Pesquisando o erro no grande oráculo, descobri um tal de FileProvider, solução da Google para evitar possíveis erros ao compartilhar arquivos de um aplicativo para outro. Caso o aplicativo que receba os arquivos não possua a permissão de “read external storage”, poderá gerar um erro se o arquivo compartilhado estiver em um armazenamento externo.

Abaixo, um passo a passo da solução que encontrei.

Primeiro passo:

Alterar o template do manifesto do seu app, adicione essas linhas no manifesto logo apos a tag Application.

   <provider
       android:name="android.support.v4.content.FileProvider"
       android:authorities="com.xxx.yyy.fileprovider"
       android:exported="false"
       android:grantUriPermissions="true">
       <meta-data 
           android:name="android.support.FILE_PROVIDER_PATHS"
           android:resource="@xml/provider_paths"/>
       </provider >

Não faça como meu colega de turma do 2º ano do técnico, que ao copiar minha prova de COBOL, copiou junto meu nome em “Author”. Substituam o com.xxx.yyy pelo package do seu app.

Crie um xml com o seguinte conteúdo:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
   <external-path name="external_files" path="."/>
</paths>

Salve-o com o nome de provider_paths.xml. Vá até Projects -> Deployment e inclua esse xml para ser salvo em res\xml\

Segundo passo:

Baixe a biblioteca KastriFree. https://github.com/DelphiWorlds/KastriFree

Use essas duas units:

DW.Android.Helpers.pas
DW.Androidapi.JNI.FileProvider.pas

Coloque-as na uses da sua unit, use-as para definir as Uris.

procedure TDocumentos.OpenFile(FileName, FileType: String);
Var
   Intent : JIntent;
   Uri    : Jnet_Uri;
begin
Uri    := TAndroidHelperEx.UriFromFileName(FileName);
Intent := TJIntent.JavaClass.init(TJIntent.JavaClass.ACTION_VIEW);
Intent.setFlags(TJIntent.JavaClass.FLAG_GRANT_READ_URI_PERMISSION);
intent.setDataAndType(URI, StringToJString(FileType));
SharedActivity.startActivity(intent);
End;

O que mudou foi “Uri := TAndroidHelperEx.UriFromFileName(FileName);” e a inclusão do flag “Intent.setFlags(TJIntent.JavaClass.FLAG_GRANT_READ_URI_PERMISSION);”

Nesse exemplo usei a rotina para visualizar imagens, pdfs, etc.. (ACTION_VIEW), é a mesma coisa para compartilhamentos (ACTION_SEND/ ACTION_SEND_MULTIPLE).

No meu caso eu sempre copiei a foto, documento ou zip para a pasta definida em System.IOUtils.TPath.GetTempPath e a compartilhei de lá. Continuei usando dessa mesma forma, deixo para você testar se o compartilhamento funciona de todas as pastas.

Contornando o problema do Push na API 26

Muitos tem sofrido com a mensagem “O aplicativo parou.” ao enviar um push para um aparelho Android 8 ou superior ustilizando a API 26, quando nosso app não está sendo executado no aparelho.

Isso é um problema do Delphi que espero ser corrigido no Update 1 da versão 10.3. Mas enquanto essa correção não sai, podemos usar um artifício do Firebase Messaging.

A solução? Algo bem simples, mas que pode gerar um código adicional em nosso app. Apenas defina a prioridade da mensagem como alta.

Abaixo um exemplo de envio onde o erro acontece:

Após fazer a alteração mostrada na imagem abaixo, o erro parar de ocorrer.

Se seu uso para o Push é apenas mostrar a notificação, problema resolvido. Mas no meu caso como podem ver, envio algumas chaves no Json que preciso que meu app as leia. Mas como ler o Json, se após clickar na notificação e abrir o app, os eventos onReceiveNotification e
onReceiveLocalNotification não são chamados?

Nesse caso vamos ter que capturar o intent que chamou nosso app e extrair de lá a informação que precisamos. O código abaixo resolve esse problema, basta usa-lo para pegar o intent no evento onCreate do form principal de sua app.

Function GetJsonNotification retorna o Json contido no intent que chamou o app
(no caso de uma notificação GCM)

Outra forma de resolver o problema é escrevendo e/ou reescrevendo partes do código Java do FireMonkey, mas para cada versão do Delphi o trabalho terá que ser refeito, a forma descrita nesse post, funciona em qualquer versão alterando apenas o código do seu app.