Acessando classes Java e Objective-C com Delphi XE5

Olá pessoal,

Hoje vamos falar um pouco sobre acesso a classes nativas Java e Objective-C em Delphi XE5 para aplicações mobile.

Introdução

A ampla maioria das funcionalidades (visuais e não visuais) necessárias a uma aplicação mobile em Delphi ou C++ Builder estão representadas através de componentes e classes do FireMonkey e da RTL.

Porém, existem SDKs e bibliotecas específicas as quais não possuem sua representação no framework. Na maioria dos casos são na verdade classes e métodos que executam uma função específica daquele sistema operacional (iOS ou Android), ou ainda acessar um pacote de terceiros, como leitores de códigos de barra, apenas para citar um exemplo.

A boa notícia é que o Firemonkey é totalmente aberto para integrações, então vamos mostrar alguns exemplos do que pode ser feito.

Criando “Wrappers”

Os wrappers são a maneira de incorporar uma classe Java ou Objective-C em um projeto Delphi ou C++ Builder. Como referência, considere os links abaixo, um sobre Java e outro sobre iOS:

http://www.pclviewer.com/android/androidJNI.html

http://blogs.embarcadero.com/fernandorizzato/index.php/2013/06/26/codigo-de-barras-no-delphi-para-ios/

Alguns exemplos

Através de exemplos postados em blogs e portais, e também criando alguns outros, centralizamos em uma única app alguns wrappers que me chamaram a atenção, organizando seu código de maneira a tornar o entendimento o mais simples possível. O exemplo disponibilizado contempla:

– Abertura de URLs e execução de outras ações pré-definidas

– Status da rede, internet, 3G, etc.

– Envio de e-mail

– Leitura de código de barras (iOS e Android)

– Toast Message (exclusivo do Android)

Código Fonte

Vou postar abaixo, a título de ilustração, a unit responsável por obter o status de rede, porém você pode baixar o projeto completo neste link: http://cc.embarcadero.com/item/29811

unit NetworkState;
interface
uses
{$IFDEF IOS}
  Macapi.ObjectiveC, Macapi.CoreFoundation, Macapi.Dispatch,
  iOSApi.CocoaTypes, iOSApi.Foundation, Posix.SysSocket;
{$ENDIF}
{$IFDEF ANDROID}
System.SysUtils, FMX.Helpers.Android, Androidapi.JNIBridge,
  Androidapi.JNI.GraphicsContentViewText, Androidapi.JNI.JavaTypes;
{$ENDIF}
{$IFDEF IOS}
const
  libSystemConfiguration =
    '/System/Library/Frameworks/SystemConfiguration.framework/SystemConfiguration';
  kSCNetworkFlagsConnectionAutomatic = 8;
  kSCNetworkFlagsConnectionRequired = 4;
  kSCNetworkFlagsInterventionRequired = 16;
  kSCNetworkFlagsIsDirect = 131072;
  kSCNetworkFlagsIsLocalAddress = 65536;
  kSCNetworkFlagsReachable = 2;
  kSCNetworkFlagsTransientConnection = 1;
  kSCNetworkReachabilityFlagsConnectionAutomatic = 8;
  kSCNetworkReachabilityFlagsConnectionOnDemand = 32;
  kSCNetworkReachabilityFlagsConnectionOnTraffic = 8;
  kSCNetworkReachabilityFlagsConnectionRequired = 4;
  kSCNetworkReachabilityFlagsInterventionRequired = 16;
  kSCNetworkReachabilityFlagsIsDirect = 131072;
  kSCNetworkReachabilityFlagsIsLocalAddress = 65536;
  kSCNetworkReachabilityFlagsReachable = 2;
  kSCNetworkReachabilityFlagsTransientConnection = 1;
  kSCNetworkReachabilityFlagsIsWWAN = $40000;
{$ENDIF}
type
{$IFDEF IOS}
  SCNetworkReachabilityFlags = UInt32;
  SCNetworkReachabilityRef = ^__SCNetworkReachability;
  __SCNetworkReachability = record
  end;
  SCNetworkReachabilityContext = record
    version: CFIndex;
    info: Pointer;
    retain: function(info: Pointer): Pointer;
    release: procedure(info: Pointer);
    copyDescription: function(info: Pointer): CFStringRef;
  end;
  SCNetworkReachabilityContextPtr = ^SCNetworkReachabilityContext;
  SCNetworkReachabilityCallback = procedure(target: SCNetworkReachabilityRef;
    flags: SCNetworkReachabilityFlags; info: Pointer);
  TReachability = class;
  Reachability = interface(NSObject)
    ['{B405394F-57B1-4FF1-83D9-8FBFA38FFD7B}']
    function startNotifier: LongBool; cdecl;
    procedure stopNotifier; cdecl;
    function isReachable: LongBool; cdecl;
    function isReachableViaWWAN: LongBool; cdecl;
    function isReachableViaWiFi: LongBool; cdecl;
    function isConnectionRequired: LongBool; cdecl;
    function connectionRequired: LongBool; cdecl;
    function isConnectionOnDemand: LongBool; cdecl;
    function isInterventionRequired: LongBool; cdecl;
    function currentReachabilityStatus: NSInteger; cdecl;
    function reachabilityFlags: SCNetworkReachabilityFlags; cdecl;
    function currentReachabilityString: NSString; cdecl;
    function currentReachabilityFlags: NSString; cdecl;
  end;
  ReachabilityClass = interface(NSObjectClass)
    ['{39EC0490-2787-4BB9-95EA-77BB885BFD01}']
    function reachabilityWithHostname(hostname: NSString): Pointer; cdecl;
    function reachabilityForInternetConnection: Pointer; cdecl;
    function reachabilityWithAddress: Pointer; cdecl;
    function reachabilityForLocalWiFi: Pointer; cdecl;
  end;
  TReachability = class(TOCGenericImport<ReachabilityClass, Reachability>)
  end;
function SCNetworkReachabilityCreateWithAddress(allocator: CFAllocatorRef;
  address: psockaddr): SCNetworkReachabilityRef; cdecl;
  external libSystemConfiguration name _PU +
  'SCNetworkReachabilityCreateWithAddress';
function SCNetworkReachabilityCreateWithAddressPair(allocator: CFAllocatorRef;
  localAddress: psockaddr; remoteAddress: psockaddr): SCNetworkReachabilityRef;
  cdecl; external libSystemConfiguration name _PU +
  'SCNetworkReachabilityCreateWithAddressPair';
function SCNetworkReachabilityCreateWithName(allocator: CFAllocatorRef;
  nodename: PChar): SCNetworkReachabilityRef; cdecl;
  external libSystemConfiguration name _PU +
  'SCNetworkReachabilityCreateWithName';
function SCNetworkReachabilityGetTypeID: CFTypeID; cdecl;
  external libSystemConfiguration name _PU + 'SCNetworkReachabilityGetTypeID';
function SCNetworkReachabilityGetFlags(target: SCNetworkReachabilityRef;
  var flags: SCNetworkReachabilityFlags): Boolean; cdecl;
  external libSystemConfiguration name _PU + 'SCNetworkReachabilityGetFlags';
function SCNetworkReachabilitySetCallback(target: SCNetworkReachabilityRef;
  callout: SCNetworkReachabilityCallback;
  var context: SCNetworkReachabilityContext): Boolean; cdecl;
  external libSystemConfiguration name _PU + 'SCNetworkReachabilitySetCallback';
function SCNetworkReachabilityScheduleWithRunLoop
  (target: SCNetworkReachabilityRef; runLoop: CFRunLoopRef;
  runLoopMode: CFStringRef): Boolean; cdecl;
  external libSystemConfiguration name _PU +
  'SCNetworkReachabilityScheduleWithRunLoop';
function SCNetworkReachabilityUnscheduleFromRunLoop
  (target: SCNetworkReachabilityRef; runLoop: CFRunLoopRef;
  runLoopMode: CFStringRef): Boolean; cdecl;
  external libSystemConfiguration name _PU +
  'SCNetworkReachabilityUnscheduleFromRunLoop';
function SCNetworkReachabilitySetDispatchQueue(target: SCNetworkReachabilityRef;
  queue: dispatch_queue_t): Boolean; cdecl;
  external libSystemConfiguration name _PU +
  'SCNetworkReachabilitySetDispatchQueue';
{$IFDEF CPUARM}
function FakeLoader: Reachability; cdecl;
  external 'libReachability.a' name 'OBJC_CLASS_$_Reachability';
{$ENDIF}
{$ENDIF}
{$IFDEF ANDROID}
JConnectivityManager = interface;
JNetworkInfo = interface;
JNetworkInfoClass = interface(JObjectClass)
  ['{E92E86E8-0BDE-4D5F-B44E-3148BD63A14C}']
end;
[JavaSignature('android/net/NetworkInfo')]
JNetworkInfo = interface(JObject)['{6DF61A40-8D17-4E51-8EF2-32CDC81AC372}']
{ Methods }
function isAvailable: Boolean;
cdecl;
function isConnected: Boolean; cdecl;
  function isConnectedOrConnecting: Boolean; cdecl;
  end;
TJNetworkInfo = class(TJavaGenericImport<JNetworkInfoClass, JNetworkInfo>)
end;
JConnectivityManagerClass = interface(JObjectClass)
  ['{E03A261F-59A4-4236-8CDF-0068FC6C5FA1}']
{ Property methods }
function _GetTYPE_WIFI: Integer;
cdecl;
function _GetTYPE_WIMAX: Integer; cdecl;
  function _GetTYPE_MOBILE: Integer; cdecl;
  { Properties }
  property TYPE_WIFI: Integer read _GetTYPE_WIFI;
  property TYPE_WIMAX: Integer read _GetTYPE_WIMAX;
  property TYPE_MOBILE: Integer read _GetTYPE_MOBILE;
  end;
[JavaSignature('android/net/ConnectivityManager')]
JConnectivityManager = interface(JObject)
  ['{1C4C1873-65AE-4722-8EEF-36BBF423C9C5}']
{ Methods }
function getActiveNetworkInfo: JNetworkInfo;
cdecl;
function getNetworkInfo(networkType: Integer): JNetworkInfo; cdecl;
end;
TJConnectivityManager = class(TJavaGenericImport<JConnectivityManagerClass,
  JConnectivityManager>)
end;
{$ENDIF}
type
  TMobileNetworkStatus = class(TObject)
  public
    constructor Create;
    destructor Destroy; override;
    function isConnected: Boolean;
    function IsWiFiConnected: Boolean;
    function IsMobileConnected: Boolean;
  end;
implementation
{$IFDEF ANDROID}
function GetConnectivityManager: JConnectivityManager;
var
  ConnectivityServiceNative: JObject;
begin
  ConnectivityServiceNative := SharedActivityContext.getSystemService
    (TJContext.JavaClass.CONNECTIVITY_SERVICE);
  if not Assigned(ConnectivityServiceNative) then
    raise Exception.Create('Could not locate Connectivity Service');
  Result := TJConnectivityManager.Wrap
    ((ConnectivityServiceNative as ILocalObject).GetObjectID);
  if not Assigned(Result) then
    raise Exception.Create('Could not access Connectivity Manager');
end;
{$ENDIF}
{$IFDEF IOS}
function GetInternetReachability: Reachability;
begin
  Result := TReachability.Wrap
    (TReachability.OCClass.reachabilityForInternetConnection);
end;
{$ENDIF}
constructor TMobileNetworkStatus.Create;
begin
end;
destructor TMobileNetworkStatus.Destroy;
begin
  inherited;
end;
function TMobileNetworkStatus.isConnected: Boolean;
{$IFDEF ANDROID}
var
  ConnectivityManager: JConnectivityManager;
  ActiveNetwork: JNetworkInfo;
{$ENDIF}
begin
{$IFDEF IOS}
  Result := GetInternetReachability.isReachable;
{$ENDIF}
{$IFDEF ANDROID}
  ConnectivityManager := GetConnectivityManager;
  ActiveNetwork := ConnectivityManager.getActiveNetworkInfo;
  Result := Assigned(ActiveNetwork) and ActiveNetwork.isConnected;
{$ENDIF}
end;
function TMobileNetworkStatus.IsMobileConnected: Boolean;
{$IFDEF ANDROID}
var
  ConnectivityManager: JConnectivityManager;
  MobileNetwork: JNetworkInfo;
{$ENDIF}
begin
{$IFDEF IOS}
  Result := GetInternetReachability.isReachableViaWWAN;
{$ENDIF}
{$IFDEF ANDROID}
  ConnectivityManager := GetConnectivityManager;
  MobileNetwork := ConnectivityManager.getNetworkInfo
    (TJConnectivityManager.JavaClass.TYPE_MOBILE);
  Result := MobileNetwork.isConnected;
{$ENDIF}
end;
function TMobileNetworkStatus.IsWiFiConnected: Boolean;
{$IFDEF ANDROID}
var
  ConnectivityManager: JConnectivityManager;
  WiFiNetwork: JNetworkInfo;
{$ENDIF}
begin
{$IFDEF IOS}
  Result := GetInternetReachability.isReachableViaWiFi;
{$ENDIF}
{$IFDEF ANDROID}
  ConnectivityManager := GetConnectivityManager;
  WiFiNetwork := ConnectivityManager.getNetworkInfo
    (TJConnectivityManager.JavaClass.TYPE_WIFI);
  Result := WiFiNetwork.isConnected;
{$ENDIF}
end;
initialization
{$IFDEF IOS}
{$IFDEF CPUARM}
if False then
  FakeLoader;
{$ENDIF}
{$ENDIF}
end.

Notas Importantes

1- No exemplo que faz acesso ao status da rede/internet/3G, quando utilizado para iOS, faz-se necessário importar o framework “SystemConfiguration” no SDK Manager do Delphi ou C++ Builder. Detalhes de como fazê-lo estão neste post: http://delphi.radsoft.com.au/2013/10/adding-other-ios-frameworks-to-the-sdk-manager/

2- A leitura de código de barras para iOS faz uso da biblioteca ZBar, já inclusa neste projeto. Maiores detalhes e novas versões, aqui: http://zbar.sourceforge.net/

3- A leitura de código de barras para Android faz uso do aplicativo Zebra Crossing: https://play.google.com/store/apps/details?id=com.google.zxing.client.android

Outras fontes de pesquisa

Provavelmente você deve estar se perguntando o que mais é possível fazer, então vou listar abaixo algumas URLs interessantes que servirão como ponto de partida:

https://www.embarcadero.com/rad-in-action/programming-devices-and-gadgets

http://blogs.embarcadero.com/davidi/2014/01/09/43215

http://www.fmxexpress.com/tag/sdk/

É isso aí… abraços e até o próximo!

Advertisements

2 thoughts on “Acessando classes Java e Objective-C com Delphi XE5

  1. Pingback: Atualizador Automático vs Google Play TDevRocks

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s