Delphi (and C++Builder) XE5 ‘mobile add-on pack’ currently being discounted

I’ve just noticed the ‘mobile add-on pack’ for Delphi (and C++Builder) XE5 Professional is currently selling for half price on Embarcadero’s online store (link). If you’re tempted, then it’s probably prudent to buy support and maintanance at the same time, which does admittedly bump the price back up… though still not to the non-discounted price of the ‘add-on pack’ itself. Also, keep in mind the ‘mobile add-on pack’ is something specific to Delphi Professional and C++Builder Professional – if you have RAD Studio Professional or higher, or Delphi or C++Builder Enterprise or higher, then mobile support (iOS and Android for Delphi, just iOS – currently – for C++Builder) is built-in.

Advertisements

CCR Exif v1.5.3 + Android app based on it in Google Play

Recently I have committed some updates to CCR Exif (my open source image metadata parsing library) that get it working with Delphi XE5, and in particular, Delphi for Android. In the main, that wasn’t hard to do – in one place I’d (mis)used the Objects property of a TStrings instance to hold casted enum values, which is a no-no under ARC, but otherwise things (with the odd [Weak] added) were fine. Well, fine with one major caveat: the code needs Andreas Hausladen’s patch to restore AnsiChar, PAnsiChar and UTF8String. Bluntly, if support for those types really does go away in XE6, then I’ll just have to say the code will support Delphi for mobile in XE5 and XE5 only, because I’m not going to crappify it.

The whole issue is a shame, because the general language and RTL compatibility between platforms (Windows/OS X/iOS/Android) and frameworks (FMX/VCL) is excellent. For example, for reading XMP metadata, my Exif code uses the IDOMxxx interfaces, the lower-level counterpart to the rather more heavyweight TXMLDocument. Both are available to use whatever the platform. Another example: at various points the FMX platform code for Android needs to wait on another thread (the Java thread) finishing a method call. What does it use for the task? Why, TEvent, using exactly the same method calls you might have made in a VCL project ten years ago.

Anyhow, proof of the pudding, I’ve written up an Exif editing app and put it on Google Play – it’s called Exif Inspector and is available here:

https://play.google.com/store/apps/details?id=com.contiguity.ExifInspector&hl=en

Brickbats welcomed, partly because you’ll have to pay a small amount to make them 😉

Loading the app’s icon into a TImage on Windows

Last time I showed how to load the icon of an FMX for Android app into a TImage. In practice, a lot of the debugging of a Delphi mobile app is likely to be with a Win32 ‘mobile preview’ build though… in which case it would be useful to load the icon when running on Windows as well.

To do that, you need to first add a *.ico version to the project. This is done via Project Options: select the Application item on the left, then All configurations – 32 bit Windows platform from the combo box at the top. Probably stating the obvious, but the icon loaded here should be a .ico version of the .png files set up for Android.

Once done, the most direct equivalent of the Android code I presented previously would be to use the LoadIcon Windows API to load the icon, then GetIconInfo and so forth to get the bitmap bits to copy to a FMX bitmap. Easier is to use the VCL however – since Vcl.Graphics.pas doesn’t bring in anything else of the VCL, there’s little point in deliberately avoiding it. As such, here’s an expanded version of the code I presented last time with a VCL-based Windows implementation added:

unit AppIconLoader;

interface

uses
  System.SysUtils, System.Classes, FMX.Graphics;

function GetAppIcon(Dest: TBitmap): Boolean;

implementation

uses
  {$IF DEFINED(ANDROID)}
  AndroidApi.JniBridge, AndroidApi.Jni.App, AndroidApi.Jni.GraphicsContentViewText, FMX.Helpers.Android,
  {$ELSEIF DEFINED(MSWINDOWS)}
  Vcl.Graphics,
  {$ENDIF}
  FMX.Surfaces;

function GetAppIcon(Dest: FMX.Graphics.TBitmap): Boolean;
{$IF DEFINED(ANDROID)}
var
  Activity: JActivity;
  Drawable: JDrawable;
  Bitmap: JBitmap;
  Surface: TBitmapSurface;
begin
  Result := False;
  Activity := SharedActivity;
  Drawable := Activity.getPackageManager.getApplicationIcon(Activity.getApplicationInfo);
  Bitmap := TJBitmapDrawable.Wrap((Drawable as ILocalObject).GetObjectID).getBitmap;
  Surface := TBitmapSurface.Create;
  try
    if not JBitmapToSurface(Bitmap, Surface) then
      Exit;
    Dest.Assign(Surface);
  finally
    Surface.Free;
  end;
  Result := True;
end;
{$ELSEIF DEFINED(MSWINDOWS)}
var
  Icon: TIcon;
  Stream: TMemoryStream;
begin
  Result := False;
  Stream := nil;
  Icon := TIcon.Create;
  try
    Icon.LoadFromResourceName(HInstance, 'MAINICON');
    if Icon.Handle = 0 then Exit;
    Stream := TMemoryStream.Create;
    Icon.SaveToStream(Stream);
    Stream.Position := 0;
    Dest.LoadFromStream(Stream);
  finally
    Icon.Free;
    Stream.Free;
  end;
  Result := True;
end;
{$ELSE}
begin
  Result := False;
end;
{$ENDIF}

end.

Loading the app’s icon into a TImage on Android

Working with Delphi for Android, I wanted to load my app’s icon into a TImage. Here’s the code I came up with:

uses
  AndroidApi.JniBridge, AndroidApi.Jni.App, AndroidApi.Jni.GraphicsContentViewText,
  FMX.Helpers.Android, FMX.Surfaces;

function GetAppIcon(Dest: TBitmap): Boolean;
var
  Activity: JActivity;
  Drawable: JDrawable;
  Bitmap: JBitmap;
  Surface: TBitmapSurface;
begin
  Result := False;
  Activity := SharedActivity;
  Drawable := Activity.getPackageManager.getApplicationIcon(Activity.getApplicationInfo);
  Bitmap := TJBitmapDrawable.Wrap((Drawable as ILocalObject).GetObjectID).getBitmap;
  Surface := TBitmapSurface.Create;
  try
    if not JBitmapToSurface(Bitmap, Surface) then
      Exit;
    Dest.Assign(Surface);
  finally
    Surface.Free;
  end;
  Result := True;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  GetAppIcon(Image1.MultiResBitmap[0].Bitmap);
end;

The slightly awkward manner of casting from a JDrawable to a JBitmapDrawable in the middle is because Java type casts are not supported directly by the Delphi to Java bridge. As such, we have to get a handle to the raw Java object and re-wrap it to the desired derived class… but that’s too much of a faff all told, and the syntax will be familiar with anyone who has used the Delphi to Objective-C bridge before.

Improved XE5 mobile previews part 2

Last time around I blogged about how to extract the default XE5 mobile styles into *.style files – in a nutshell, you need to create an app that extracts the desired style resource to a file, then open the file in the stylebook editor, delete the ‘Description’ resource, and resave. In that post I went on to suggest using the extracted files in TStyleBook components. There is however a better way – provide a custom FMX.MobilePreview unit instead (NB: for all about the standard mobile previews – introduced in XE5 update 2 – check out either Marco Cantù’s blog post or the documentation).

To do this, create a new unit and, modeling on the standard FMX.MobilePreview.pas, add the following code:

unit FMX.MobilePreview;
{
  Use actual Android style
}
interface

implementation

{$IFDEF MSWINDOWS}

{$IF CompilerVersion <> 26}
  {$MESSAGE Error 'Custom FMX.MobilePreview.pas expects XE5!'}
{$IFEND}

uses
  System.Types, System.SysUtils, FMX.Styles, FMX.Graphics, FMX.Platform;

var
  OldInitProc: TProcedure;

procedure InitializeStyle;
begin
  if Assigned(OldInitProc) then
    OldInitProc();
  //!!!change path to whereever you have put the resaved *.style file
  TStyleManager.SetStyle(TStyleManager.LoadFromFile('C:\Delphi\LibXE5\Android.style'));
end;

initialization
  OldInitProc := InitProc;
  InitProc := @InitializeStyle;

finalization
  TStyleManager.SetStyle(nil);
  TCanvasManager.UnInitialize;
{$ENDIF}
end.

If you save the unit as FMX.MobilePreview.pas, compile and ensure the DCUs are in the global search path, you can have the IDE pick it up rather than the standard FMX.MobilePreview.pas. Alternatively, save it under a different name and just amend your project files to use it rather than FMX.MobilePreview.

That said, if you do this, something still won’t quite be right – namely, the font:

Font not right

This is because in the current FireMonkey code, the default font is a property of the platform rather than the style. As such, the font used here is a regular Windows one rather than Roboto, the default font on Android. Fixing this discrepancy is easy however – back in the custom unit, first add the following class immediately after the uses clause:

type
  TFMXSystemFontService = class(TInterfacedObject, IFMXSystemFontService)
  public
    function GetDefaultFontFamilyName: string;
  end;

function TFMXSystemFontService.GetDefaultFontFamilyName: string;
begin
  Result := 'Roboto';
end;

Secondly, unregister the standard IFMXSystemFontService implementation before registering our own by adding the following lines at the top of the unit’s initialization block:

  TPlatformServices.Current.RemovePlatformService(IFMXSystemFontService);
  TPlatformServices.Current.AddPlatformService(IFMXSystemFontService, TFMXSystemFontService.Create);

In all, the code for unit should now look like this:

unit FMX.MobilePreview;
{
  Use actual Android style
}
interface

implementation

{$IFDEF MSWINDOWS}
{$IF CompilerVersion <> 26}
  {$MESSAGE Error 'Custom FMX.MobilePreview.pas expects XE5!'}
{$IFEND}
uses
  System.Types, System.SysUtils, FMX.Styles, FMX.Graphics, FMX.Platform;

type
  TFMXSystemFontService = class(TInterfacedObject, IFMXSystemFontService)
  public
    function GetDefaultFontFamilyName: string;
  end;

function TFMXSystemFontService.GetDefaultFontFamilyName: string;
begin
  Result := 'Roboto';
end;

var
  OldInitProc: TProcedure;

procedure InitializeStyle;
begin
  if Assigned(OldInitProc) then
    OldInitProc();
  TStyleManager.SetStyle(TStyleManager.LoadFromFile('C:\Users\CCR\Downloads\Android FMX (resaved, and minus description).style'));
end;

initialization
  TPlatformServices.Current.RemovePlatformService(IFMXSystemFontService);
  TPlatformServices.Current.AddPlatformService(IFMXSystemFontService, TFMXSystemFontService.Create);
  OldInitProc := InitProc;
  InitProc := @InitializeStyle;

finalization
  TStyleManager.SetStyle(nil);
  TCanvasManager.UnInitialize;
{$ENDIF}
end.

Run the mobile preview again, and the font is now how it should be:

Font fixed