I covered this previously in a few webinars and presentations, but never published the source code for WebBroker on Android. To be honest I hadn’t tested it with C++Builder before, but I completely expected it to work, and it did. I also updated the tests on Emteria.OS (FKA RTAndroid) and it also works there.
The process of porting a Delphi or C++Builder WebBroker project to Android is pretty straight forward, but I’m publishing the code anyway. You create a Windows FMX WebBroker project, then copy all the code into a regular FireMonkey project. You will need to copy a few files from the RTL folder locally so you can reference them since they aren’t included in the Android package.
Web.WebReq.pas
Web.WebBroker.pas
Web.WebConst.pas
IdHTTPWebBrokerBridge.pas
IdCompilerDefines.inc
For C++Builder you also need
Web.WebReq.hpp
Web.WebBroker.hpp
Web.WebConst.hpp
IdHTTPWebBrokerBridge.hpp
Here are the links for the Delphi and C++Builder projects. They were built and tested in with 10.3.1 Rio. I also compiled some updated details on how to build the project and how to install and test on Emteria.OS.
I mention this in the slide deck, but officially WebBroker isn’t supported on Android. I tested it, and it seems to work, but if you run into an instance where it doesn’t work as expected, then you are on your own. Please don’t contact support and tell them I said it should work. Thanks!
I wanted to finish this side project during Women’s History Month to honor the Amazing Grace Hopper and her contributions to the field of Computer Science. I found this interesting SVG of Grace Hopper that wanted to figure out how to render in Delphi using the FireMonkey TPath, but it also looked like it should be animated somehow . . . .
I’ve rendered some simple SVG graphics with the TPath component before, but this one was more complicated. It has multiple colors and variable opacity. This requires multiple TPath instances to handle each variation. It was a simple matter of loading in the SVG file using an IXMLDocument, then parsing the elements, and creating a TPath for each one. For fun I included a variable sleep between each draw. Also, to make sure all the paths have the same relative size I added a couple MoveTo calls to define the client area.
var
XmlSvg: IXMLDocument;
val: String;
vals: TArray<String>;
node: IXMLNode;
path: TPath;
begin
tabControl.ActiveTab := TabItem2;
// This removes the encoded carriage returns
XmlSvg := LoadXMLData(StringReplace(memo1.Text, '
', '', [rfReplaceAll]));
if XmlSvg.DocumentElement.HasAttribute('viewBox') then
begin
val := XmlSvg.DocumentElement.Attributes['viewBox'];
vals := val.Split([' ']);
SVGLayout.Width := vals[2].ToInteger - vals[0].ToInteger;
SVGLayout.Height := vals[3].ToInteger - vals[1].ToInteger;
end;
for var idx := 0 to XmlSvg.DocumentElement.ChildNodes.Count - 1 do
begin
node := XmlSvg.DocumentElement.ChildNodes[idx];
if (node.NodeName = 'path') and (node.HasAttribute('d')) then
begin
path := TPath.Create(svgLayout);
path.Parent := svgLayout;
path.WrapMode := TPathWrapMode.Stretch;
path.Align := TAlignLayout.Contents;
path.Data.Data := node.Attributes['d'];
path.Data.MoveTo(TPointF.Zero);
path.Data.MoveTo(TPointF.Create(SVGLayout.Width, SVGLayout.Height));
if node.HasAttribute('opacity') then
path.Opacity := StrToFloat(node.Attributes['opacity']);
if node.HasAttribute('fill') and (node.Attributes['fill'] <> 'none') and (node.Attributes['fill'] <> '') then
path.Fill.Color := TAlphaColorRec.Alpha or StringToAlphaColor(node.Attributes['fill']);
end;
Sleep(Trunc(TrackBar1.Value));
svgLayout.Repaint;
Application.ProcessMessages;
end;
This is by no means a complete implementation of the SVG standard, but it is getting closer! Close enough for some simple SVG images though, and possibly a useful basis for more complicated ones.
The animations was just a matter of assigning a TFloatAnimation to each TPath that adds some random movement. I included both slight scales and rotations. I could have done both on each, but was afraid that might be too much movement.
var
dance: TFloatAnimation;
path: TPath;
I: Integer;
begin
for I := 0 to pred(SVGLayout.ChildrenCount) do
begin
if SVGLayout.Children[I] is TPath then
begin
path := SVGLayout.Children[I] as TPath;
dance := TFloatAnimation.Create(nil);
dancers.Add(dance);
dance.Parent := Path;
dance.AutoReverse := True;
dance.Loop := True;
dance.StartFromCurrent := True;
case random(4) of
0: begin
case random(2) of
0: dance.PropertyName := 'Scale.X';
1: dance.PropertyName := 'Scale.Y';
end;
case random(2) of
0: dance.StopValue := 1.01;
1: dance.StopValue := 0.99;
end;
end;
1: begin
case random(2) of
0: begin
dance.PropertyName := 'Position.X';
case random(2) of
0: dance.StopValue := Path.Position.X - random - 0.01;
1: dance.StopValue := Path.Position.X + random + 0.01;
end;
end;
1: begin
dance.PropertyName := 'Position.Y';
case random(2) of
0: dance.StopValue := Path.Position.Y - random - 0.01;
1: dance.StopValue := Path.Position.Y + random + 0.01;
end;
end;
end;
end;
2..3: begin
dance.PropertyName := 'RotationAngle';
path.RotationCenter.X := random;
path.RotationCenter.Y := random;
case random(2) of
0: dance.StopValue := random + 0.01;
1: dance.StopValue := -1 * random - 0.01;
end;
end;
end;
dance.Enabled := True;
end;
end;
And we end up with something like this . . .. (Down scaled and lower FPS)
I’m posting my code if you want to play with it some more. The source SVG is embedded in a memo instead of reading it from a file. It was written with Delphi 10.3.1 Rio.