B4J: Creating your own components in ABMaterial


Although ABMaterial has already a lot of components build-in, sometimes you just need a specific one.  I had a question on the B4J forum from CGP who needed a Gauge component.

So let’s build one! CGP had found a very nice library JustGage that would allow him to get the Gauge he wanted. Its value had to be updateable from within B4J, and this was a bit tricky. It looked like we have to be a bit creative because this javascript library did not connect its parent tag with the created JustGage.

We will have to keep track of the JustGage variable ourselves by adding a script tag in the build:

Sub ABMComp_Build(internalID As String) As String
  Return $"<div id="${internalID}"></div><script>var _${internalID};</script>"$
End Sub

The rest of the code is straight forward and we can just follow the javascript instructions and add them in our relative events.

An ABMaterial custom component has 4 important events:

ABMComp_Build(): This is where we have to add the html tag (anchor) for the new component.  In this particular case we also have to add a <script> tag that will hold our justGage variable.  We can then later use this variable to update the Gauge chart.

ABMComp_FirstRun(): Here we can add a script that will run, well, at first run. In most cases, this is where we add the libraries javascript initialization code. The B4J $””$ Smartstrings are in methods like this particulary handy. Use them!

ABMComp_Refresh(): The place where we can adjust properties of the javascript component.  Like for this component, we’ll adjust the value of the gauge.

ABMComp_CleanUp(): An event that is raised when the component is removed.  Could be useful to do some cleanup in the html of set javascript variables to null.

Here is the complete B4J code for our customGauge component:

Sub Class_Globals
   Public ABMComp As ABMCustomComponent
  Public myValue As Int
  Public myLabel As String
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize(InternalPage As ABMPage, ID As String, value As Int, name As String)
  ABMComp.Initialize("ABMComp", Me, InternalPage, ID)
  myValue = value
  myLabel = name
End Sub

Sub ABMComp_Build(internalID As String) As String
  Return $"<div id="${internalID}"></div><script>var _${internalID};</script>"$
End Sub

' Is useful to run some initalisation script.
Sub ABMComp_FirstRun(InternalPage As ABMPage, internalID As String)
  Dim script As String = $"_${internalID} = new JustGage({
  id: "${internalID}",
  value: ${myValue},
  Min: 0,
  Max: 100,
  relativeGaugeSize: true,
  title: "${myLabel}"

  InternalPage.ws.Eval(script, Array As Object(ABMComp.ID))
  ' flush not needed, it's done in the refresh method in the lib
End Sub

' runs when a refresh is called
Sub ABMComp_Refresh(InternalPage As ABMPage, internalID As String)
  Dim script As String = $"_${internalID}.refresh(${myValue});"$
  InternalPage.ws.Eval(script, Null)
End Sub

' do the stuff needed when the object is removed
Sub ABMComp_CleanUp(InternalPage As ABMPage, internalID As String)

End Sub

Now, how do we use our new component in our B4J ABMaterial WebApp?

First, we’ll need to make our custGauge a global variable on the page:

Sub Class_Globals
  Dim custGauge As CustomGauge

Next, we add the justGauge libraries in the BuildPage().  We’ve placed them in \www\js\custom folder so we can add:

Sub BuildPage()

In the ConnectPage, we’ll create the actual Gauge and we add a button so we can change its value:

Sub ConnectPage()
  custGauge.Initialize(page, "custGauge", 25, "Test")

  Dim custGaugebtn1 As ABMButton
  custGaugebtn1.InitializeRaised(page, "custGaugebtn1", "", "", "BUTTON", "")

And finally we add out button code to adjust the value of the Gauge:

Sub custGaugebtn1_Clicked(Target As String)
   custGauge.myValue = Rnd(10,100)
End Sub

And just like that, it looks like a fun new component we’ve got in B4J ABMaterial!

Until next time,


Click here to Donation and support ABMaterial


B4J: ABMXPlay first online demo


This is the first public demo of the new ABMaterial 2.0 component ABMXPlay.  It contains several basic demos of what this new component can do. Use the ‘hamburger’ menu on the top left to see another demo.

Although the current version of the demo still uses pure B4J server code (e.g. when you click, it does a round-trip to the server), I’m quite happy with the responiveness.  But as I mentioned in a previous post, even this round-trip will be eliminated when it starts using the Basic to JavaScript transpiler B4JS.

I’m making good progress on B4JS.  At the time of this writing, I’m able to transpile a lot of the B4J core functions. See here for an overview.

An example of some B4J core function tests and their translations to Javascript.

B4J code:

Sub B4JSCoreFunctions
   Log("------- Start B4JSCoreFunctions")
   Log("(True And False): false <=> " & (True And False)) 
   Log("(True Or False): true <=> " & (True Or False)) 
   Log("Rnd(0,10): Number between 0 and 10 <=> " & Rnd(0,10))
   Log("Abs(-2): 2 <=> " & Abs(-2))
   Log("Not(False): true <=> " & Not(False))
   Log("CosD(45):  0.70710678... <=> " & CosD(45))
   Log("Cos(0.5):  0.87758256... <=> " & Cos(0.5))
   Log("SinD(45):  0.70710678... <=> " & SinD(45))
   Log("Sin(0.5):  0.47942553... <=> " & Sin(0.5))
   Log("ACosD(-1):  180... <=> " & ACosD(-1))
   Log("ACos(0.5):  1.04719755... <=> " & ACos(0.5))
   Log("ASinD(-1):  -90... <=> " & ASinD(-1))
   Log("ASin(0.5):  0.52359877... <=> " & ASin(0.5))
   Log("TanD(45):  0.99999999... <=> " & TanD(45))
   Log("Tan(0.5):  0.54630248... <=> " & Tan(0.5))
   Log("ATanD(45):  88.7269699... <=> " & ATanD(45))
   Log("ATan(0.5):  0.46364760... <=> " & ATan(0.5))
   Log("ATan2D(90,45): 3.4349488... <=> " & ATan2D(90,45))
   Log("ATan2(8,4):  1.10714871... <=> " & ATan2(8,4))

   Log("try/Catch example, only visible in B4JS.")
     Dim i As Int = 1/0 'ignore
   End Try

   Log("Chr(65): A <=> " & Chr(65))
   Log("Asc(""A""): 65 <=> " & Asc("A"))
   Log("cE: 2.71828182... <=> " & cE)
   Log("Ceil(1.65): 2 <=> " & Ceil(1.65))
   Log("cPI: 3.14159265... <=> " & cPI)
   Log("Floor(1.65): 1 <=> " & Floor(1.65))
   Log("IsNumber(""alain""): false <=> " & IsNumber("alain"))
   Log("IsNumber(""15.2""): true <=> " & IsNumber("15.2"))
   Log("Logarithm(10, 1000): 0.33333333.... <=> " & Logarithm(10, 1000))
   Log("Max(10,5): 10 <=> " & Max(10,5))
   Log("Min(10,5): 5 <=> " & Min(10,5))
   Log("Power(2,3): 8 <=> " & Power(2,3))
   Log("Round(1.12345): 1 <=> " & Round(1.12345))
   Log("Round2(1.12345, 2): 1.12 <=> " & Round2(1.12345, 2))
   Log("Sqrt(9): 3 <=> " & Sqrt(9))

   Dim str1 As String = "Test €100 is £84.73"
   Dim theBytes() As Byte
   theBytes = str1.GetBytes("UTF8")
   Log("BytesToString(theBytes,0,theBytes.Length, ""UTF-8""): Test €100 is £84.73 <=> " & BytesToString(theBytes,0,theBytes.Length, "UTF-8"))
End Sub

The JavaScript transpiled code:

     try {
       console.log("------- Start B4JSCoreFunctions");
       console.log("(True And False): false <=> "+(true && false));
       console.log("(True Or False): true <=> "+(true || false));
       console.log("Rnd(0,10): Number between 0 and 10 <=> "+(Math.floor(Math.random() * (10 - 0) + 0)));
       console.log("Abs(-2): 2 <=> "+(Math.abs(-2)));
       console.log("Not(False): true <=> "+!(false));
       console.log("CosD(45):  0.70710678... <=> "+(Math.cos((45)/(180/Math.PI))));
       console.log("Cos(0.5):  0.87758256... <=> "+(Math.cos(0.5)));
       console.log("SinD(45):  0.70710678... <=> "+(Math.sin((45)/(180/Math.PI))));
       console.log("Sin(0.5):  0.47942553... <=> "+(Math.sin(0.5)));
       console.log("ACosD(-1):  180... <=> "+(Math.acos(-1)*(180/Math.PI)));
       console.log("ACos(0.5):  1.04719755... <=> "+(Math.acos(0.5)));
       console.log("ASinD(-1):  -90... <=> "+(Math.asin(-1)*(180/Math.PI)));
       console.log("ASin(0.5):  0.52359877... <=> "+(Math.asin(0.5)));
       console.log("TanD(45):  0.99999999... <=> "+(Math.tan((45)/(180/Math.PI))));
       console.log("Tan(0.5):  0.54630248... <=> "+(Math.tan(0.5)));
       console.log("ATanD(45):  88.7269699... <=> "+(Math.atan(45)*(180/Math.PI)));
       console.log("ATan(0.5):  0.46364760... <=> "+(Math.atan(0.5)));
       console.log("ATan2D(90,45): 3.4349488... <=> "+(Math.atan2(90,45)*(180/Math.PI)));
       console.log("ATan2(8,4):  1.10714871... <=> "+(Math.atan2(8,4)));
       console.log("try/Catch example, only visible in B4JS.");
       try {
         var _i=1/0;
       } catch(err) {
       console.log("Chr(65): A <=> "+String.fromCharCode(65));
       console.log("Asc(\"A\"): 65 <=> "+"A".charCodeAt(0));
       console.log("cE: 2.71828182... <=> "+2.7182818284590452353602874713527);
       console.log("Ceil(1.65): 2 <=> "+(Math.ceil(1.65)));
       console.log("cPI: 3.14159265... <=> "+Math.PI);
       console.log("Floor(1.65): 1 <=> "+(Math.floor(1.65)));
       console.log("IsNumber(\"alain\"): false <=> "+(!isNaN(parseFloat("alain")) && isFinite("alain")));
       console.log("IsNumber(\"15.2\"): true <=> "+(!isNaN(parseFloat("15.2")) && isFinite("15.2")));
       console.log("Logarithm(10, 1000): 0.33333333.... <=> "+(Math.log(10)/Math.log(1000)));
       console.log("Max(10,5): 10 <=> "+Math.max(10,5));
       console.log("Min(10,5): 5 <=> "+Math.min(10,5));
       console.log("Power(2,3): 8 <=> "+Math.pow(2,3));
       console.log("Round(1.12345): 1 <=> "+Math.round(1.12345));
       console.log("Round2(1.12345, 2): 1.12 <=> "+(+(Math.round(1.12345+"e+"+2)+"e-"+2)));
       console.log("Sqrt(9): 3 <=> "+(Math.sqrt(9)));
       var _str1="Test €100 is £84.73";
       var _thebytes=[];
       _thebytes = _str1.getBytes();
       console.log("BytesToString(theBytes,0,theBytes.Length, \"UTF-8\"): Test €100 is £84.73 <=> "+bytesToString(_thebytes.slice(0,_thebytes.length)));
     catch(err) {
       console.log(err.message + ' ' + err.stack);

Or a more challenging transpiling of the B4J ‘String.GetBytes()’ command:

String.prototype.getBytes = function() {
     var utf8 = [];
     var str = this;
     for (var i=0; i < str.length; i++) {
       var charcode = str.charCodeAt(i);
       if (charcode < 0x80) utf8.push(charcode);
       else if (charcode < 0x800) {
         utf8.push(0xffffffc0 | (charcode >> 6), 0xffffff80 | (charcode & 0x3f));
       } else if (charcode < 0xd800 || charcode >= 0xe000) {
         utf8.push(0xffffffe0 | (charcode >> 12), 0xffffff80 | ((charcode>>6) & 0x3f), 0xffffff80 | (charcode & 0x3f));
       } else {
         utf8.push(0xef, 0xbf, 0xbd);
     return utf8;

Now that I can write real B4J code and translate it to JavaScript, I can start introducing it into ABMaterial. Once I’ve done this for the ABMXplay component, I’ll put up a new demo (using B4JS) and then we can compare the results. My first tests look very promising!

WARNING: the demo contains audio fragments and the South Park characters can be rather offensive!

It runs on my personal server. Please be gentle: ABMXPlay demo

Happy programming!


ABXPlay for Xojo is dead, long live ABMXPlay for B4J!


As many will have noticed in the last year,  there are fewer articles about Xojo on Alwaysbusy’s Corner.  I do like to write graphics projects/frameworks and Xojo is more a language for business apps. Over the years, I’ve written several ‘Proof of Concept’ articles on graphics programming with RB/Xojo.  And although the algorithms could be written, I always quickly hit a wall when it came to speed. I sometimes get requests to update the existing articles, but IMHO, Xojo is becoming the new VB6.  Just like at the end of the popular VB6, using declares in Xojo are in many cases the only solution to make your program work in a modern way.

But it has been fun and I’m sure Xojo can be used to write several other type of projects. I have no regrets having learned RB/Xojo in the past.

But in this view, I have discontinued the ABXPlay for Xojo project and decided to start all over again with ABMXPlay for B4J as a ABMaterial 2.0 component. B4J is excellent when it comes to pure speed in WebApps and that is exactly what is needed for a game framework.

The complete rewrite of ABMXPlay for B4J is better than the Xojo version.  It is integrated in ABMaterial so it can make use of this engine too.  As the B4J syntax is a lot easier than the one in Xojo to interpret (it is very similar to what RB used to be) and the used file format is plain text, I’m working on a B4JS transpiler that will translate B4J code to Javascript.  This changes everything massively!  While still being able to write simple Basic syntax, you are actually writing powerful and fast client side Javascript.

ABMXPlay is going to be the first ABMaterial component with this functionality. In the following months, I’ll gradually will introduce this to other ABMaterial components as they can equally benefit from B4JS. Imagine having a form, being able to check the input fields, making some price calculations etc without having to talk to the server.  Only when the user hits the ‘buy’ button, an actual request is made to the server.  And for you as a programmer, all you have to do is continue programming in B4J as you always did!

So, still a lot of work to do. Coffee and Red bull will be my best friends in the upcoming nights. But it is going to be worth it!

Note: ABXVision for Xojo is also DISCONTINUED: Interested parties in purchasing the source code for the Xojo versions can contact me via email.

Until the next update,


Click here to Donation and support ABMaterial

B4J: ABMaterial officially named…

Just a little announcement: ABMaterial for B4J finally got its own domain abmaterial.com! My brainchild is moving out…


Click here to Donation and support ABMaterial


B4J: ABMaterial Public 1.12/Donators 1.20 now released

So, what happened to 1.09, 1.10 and 1.11?
Version 1.09 does not exist. I’ve changed it to 1.10 so I can use the major number as the main release update and the minor number for maintenance releases. e.g. maintenance releases for 1.10 are 1.11,1.12, etc… The next big release is 1.20.

Highlights of the releases:

All components are fully dynamic

In a ‘static’ page, all the components are written in the .html file. In a ‘dynamic’ page they are not. Only when you connect as a user, the components are inserted in their browser.

This seems trivial, but this actually means we can ‘write’ components (html, css and javascript) at run-time, depending on e.g. the user that logged in. Some user is allowed to see one chart, another user another chart. Or one may be able to delete a record from your database, another is not. Or showing a certain modal sheet depending on the user without having to add all possibilities in the HTML. Or show the page in different languages depending on who logged in!

New component ABMTimeLine

ABMTimeline is a component to present a time line of events. Using the ABMTimeLineElement you can create events, with some assets like images.


New component ABMFlexWall

ABMFlexWall is a simple galarie component for images.  Together with the IsMaterialBoxed=true setting, it creates an easy to use image wall.


Speed, the need for speed…

Following all the guidelines of Google, ABMaterial is one of the fastest framesworks around.  Not only in time of development, but this is certainly the case for the user experience. And all done in the background for you!


Firebase Auth and Storage support (1.20)

ABMaterial is always on top of new technologies.  Latest in line is support of Googles Firebase API. You can use Firebase Auth to login to your ABMaterial (1.20) WebApp and with Storage, you can upload/download files with ease. Together with B4X Firebase support, you can build the most powerful apps ever.

New components ABMSVGSurface, ABMFileInput, ABMTableMutable and ABMPatternLock (1.20)

Always on the move, new components are introduced with every release.  Donators can find more info on these new components in their mail.  More on this blog when 1.20 is released to the public.

So check out the online demo abmaterial.com or download your free copy from the B4J website

Until next time!


Click here to Donation if you like my work


B4J: ABTelegram bots library

Bots are the next hot thing in 2016! So in my spare-spare time I’ve been working on a wrapper for the Telegram API v2.0. It has a rather unconventional API but I’m reworking it to make it very easy to write bots in B4J.

Bots could be very useful as yet another way to ‘talk’ with IoT devices. You can control LEDs, temperatures, ask for stats etc right from within the Telegram Chat App (which runs on about every kind of platform, being the desktop, a browser, iOS, Android, etc…). Or you can, like I did to test the wrapper, write a little game:


Note: this library is in very early stages so the wrapper is not available for download yet. Full source code of the game will also be available when released.

Some sample code to show you how easy it is to write a bot in B4J (this is not the game code, just some tests):

Code to initialize the library:

Sub Process_Globals
   Dim ABT As ABTelegram
End Sub

Sub AppStart (Args() As String)
   Dim b As MyBot

End Sub

Code for the Bot:

'Class module
Sub Class_Globals
Dim ABT As ABTelegram
Public Bot As ABTLongPollingBot
Private botToken As String = &quot;207189xxxx:yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy&quot;
Private botUserName As String = &quot;zzzzzzzzzBot&quot;
'Private callbackCounter As Int
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize
Bot.Initialize(&quot;Bot&quot;, Me, botToken, botUserName)
Dim upds As List = Bot.botgetUpdates
For i = 0 To upds.Size - 1
Dim upd As ABTUpdate = upds.Get(i)
Log(&quot;Go update: &quot; &amp; upd.UpdateId)
End Sub

Sub Bot_UpdateReceived(update As ABTUpdate)
Log(&quot;Got an update&quot;)
If update.hasMessage Then
Dim msg As ABTMessage = update.GetMessage
Log(&quot;Message: &quot; &amp; msg.Text)

'        Dim userPhotos As ABTUserProfilePhotos = Bot.BotGetUserProfilePhotos(msg.Chat.id)
'        Log(&quot;photos: &quot; &amp; userPhotos.TotalCount)
'        Dim Photos As List = userPhotos.Photos
'        For i = 0 To Photos.Size - 1
'            Dim photoSizes As ABTPhotoSizes = Photos.Get(i)
'            For j = 0 To photoSizes.Sizes.Size - 1
'                Dim photoSize As ABTPhotoSize = photoSizes.Sizes.Get(j)
'                Dim photoFile As ABTFile = Bot.BotGetFile(photoSize.FileId)
'                Log(photoFile.FilePath)
'                Dim job1 As HttpJob
'                job1.Initialize(&quot;Job&quot; &amp; j, Me)
'                job1.Tag = photoFile.FilePath.Replace(&quot;photo/&quot;, &quot;&quot;)
'                Dim url As String = &quot;https://api.telegram.org/file/bot&quot; &amp; botToken &amp; &quot;/&quot; &amp; photoFile.FilePath
'                   job1.Download(url)
'               Next
'        Next

'        callbackCounter = callbackCounter + 1
'        Bot.BotSendMessageAsync(callbackCounter, msg.ChatId, &quot;Alweer hallo &quot; &amp; msg.Chat.FirstName)

Bot.BotSendMessage(msg.ChatId, &quot;Using an Emoji &quot; &amp; &quot;:grinning: in the message!&quot;) ' see    https://github.com/vdurmont/emoji-java for a list of emojis

'        Bot.BotSendPhoto(True, msg.ChatId, File.DirApp &amp; &quot;/&quot; &amp; &quot;telegram.png&quot;)

'        callbackCounter = callbackCounter + 1
'        Bot.BotSendDocumentAsync(callbackCounter, True, msg.ChatId, File.DirApp &amp; &quot;/&quot; &amp; &quot;mydocument.pdf&quot;, &quot;mydocument.pdf&quot;)

'        Bot.BotSendDocument(True, msg.ChatId, File.DirApp &amp; &quot;/&quot; &amp; &quot;mydocument.pdf&quot;, &quot;mydocument.pdf&quot;)

Bot.BotSendAudio(True, msg.ChatId, File.DirApp &amp; &quot;/&quot; &amp; &quot;myaudio.mp3&quot;)

'        Bot.BotSendVideo(True, msg.ChatId, File.DirApp &amp; &quot;/&quot; &amp; &quot;myvideo.mp4&quot;)

'        Bot.BotSendSticker(True, msg.ChatId, File.DirApp &amp; &quot;/&quot; &amp; &quot;mysticker.webp&quot;)

'        Bot.BotSendContact(msg.ChatId, &quot;+32496000000&quot;, &quot;Alain&quot;)

'        Bot.BotSendChatAction(msg.ChatId, ABT.CHATACTIONTYPE_TYPING)

'        Bot.BotSendLocation(msg.ChatId, 50.8492, 2.8779)

'        Bot.BotSendVenue(msg.ChatId, 50.8492, 2.8779, &quot;my venue&quot;, &quot;Grote markt 1&quot;)

Bot.BotSendVoice(True, msg.ChatId, File.DirApp &amp; &quot;/&quot; &amp; &quot;telegram.ogg&quot;)

'        Dim keyb As ABTReplyKeyboard
'        Dim rows As List
'        rows.Initialize
'        Dim tmpRow As ABTInlineKeyboardRow
'        tmpRow.Initialize
'        tmpRow.AddButton(&quot;test button&quot;, &quot;http://one-two.com&quot;, &quot;&quot;, &quot;&quot;)
'        rows.Add(tmpRow)
'        keyb.InitializeAsInlineKeyboardMarkup(rows)
'        Bot.BotEditMessageTextEx(msg.ChatId, msg.MessageId, 0, &quot;Extended test&quot;, ABT.PARSEMODE_HTML, False, keyb)

Dim rkeyb As ABTReplyKeyboard
Bot.BotSendMessageEx(msg.ChatId, &quot;some test: reply please&quot;, ABT.PARSEMODE_HTML, False, False, 0, rkeyb)

'        Dim rkeyb2 As ABTReplyKeyboard
'        Dim rows2 As List
'        rows2.Initialize
'        For i = 1 To 100
'            Dim tmpRow2 As ABTKeyboardRow
'            tmpRow2.Initialize
'            tmpRow2.AddButton(&quot;Press me&quot;)
'            rows2.Add(tmpRow2)
'        Next
'        rkeyb2.InitializeAsReplyKeyboardMarkup(rows2,False, False, False)
'        Bot.BotSendMessageEx(msg.ChatId, &quot;some test: press the button&quot;, ABT.PARSEMODE_HTML, False, False, 0, rkeyb2)

'        Dim rkeyb3 As ABTReplyKeyboard
'        Dim rows3 As List
'        rows3.Initialize
'        Dim tmpRow3 As ABTInlineKeyboardRow
'        tmpRow3.Initialize
'        tmpRow3.AddButton(&quot;Show image&quot;, &quot;&quot;, &quot;showimage&quot;, &quot;&quot;)
'        rows3.Add(tmpRow3)
'        rkeyb3.InitializeAsInlineKeyboardMarkup(rows3)
'        Bot.BotSendMessageEx(msg.ChatId, &quot;some test: press the button&quot;, ABT.PARSEMODE_HTML, False, False, 0, rkeyb3)
End If
If update.hasCallbackQuery Then
Dim cquery As ABTCallbackQuery = update.GetCallbackQuery
Log(&quot;CallbackQuery: &quot; &amp; cquery.data)
Select Case cquery.Data
Case &quot;showimage&quot;
Dim msg As ABTMessage = cquery.Message

Bot.BotSendPhotoEx(True, msg.ChatId , File.DirApp &amp; &quot;/&quot; &amp; &quot;myphoto.jpg&quot;, &quot;A new photo of darth vader!&quot;,False, 0, Null )
Bot.BotSendLocation(msg.ChatId, 50.8492, 2.8779)
Bot.BotEditMessageText(msg.ChatId, msg.MessageId, &quot;&quot;, &quot;Shown&quot;)
End Select
End If
If update.hasInlineQuery Then
Dim ciquery As ABTInlineQuery = update.GetInlineQuery
Log(&quot;InlineQuery: &quot; &amp; ciquery.Query)
If ciquery.Query = &quot;loc&quot; Then
Dim lstResults As List
Dim myanswer As ABTInlineQueryResult
myanswer.InitializeAsLocation(&quot;1&quot;, 50.8492, 2.8779, &quot;Your location&quot;)
Bot.BotAnswerInlineQuery(ciquery.Id, lstResults)
End If
End If
If update.hasChosenInlineResult Then
Dim ccresult As ABTChosenInlineResult = update.GetChosenInlineResult
Log(&quot;ChosenInlineResult: &quot; &amp; ccresult.Query)
End If
End Sub

Sub Bot_AsyncSendReceived(methodType As String, callbackId As String, success As Boolean, asyncObject As ABTObject)
Log(&quot;Async method: &quot; &amp; callbackId &amp; &quot; &quot; &amp; methodType &amp; &quot; &quot; &amp; success)
If success And asyncObject &amp;lt;&amp;gt; Null Then
If asyncObject.objectType=&quot;ABTMessage&quot; Then
Dim message As ABTMessage = asyncObject
Log(&quot;Async callback: &quot; &amp; message.MessageId)
End If
End If
End Sub

Sub JobDone (Job As HttpJob)
Log(&quot;JobName = &quot; &amp; Job.JobName &amp; &quot;, Success = &quot; &amp; Job.Success)
If Job.Success = True Then
Dim fSave As OutputStream = File.OpenOutput(File.DirApp, Job.tag, False)
File.Copy2(Job.GetInputStream, fSave)
Log(&quot;Error: &quot; &amp; Job.ErrorMessage)
End If
End Sub


Click here to Donation if you like my work


B4J: ABMaterial Public 1.07/Donators 1.08 now released

ABMaterial public version 1.07 is now available from the B4J website!

What’s new:

ABMGenerator object: allows generating CRUD and messagebox modal sheets fast

Given a set of parameters in a couple of lines of code, ABMGenerator can generate several hundreds of lines of B4J code that only need to be tuned by the programmer to its specific wishes.

Refer to this post for more info.

Infinite Scrolling pages (e.g. like Twitter or Facebook)

With just a couple of lines code, you can create Infinite Scrolling Pages with ABMaterial.

See this post for more info and a demo.

Support for Google Analytics

more info and a tutorial, check out this previous article.

New component ABMSocialShare



New component ABMEditor


Read the README1.07.TXT for the full release notes.

Download ABMaterial Public version 1.07

Version 1.08 is going to be all about speed! But I’ll post a seperate article on my experiences here later on this. A couple of new components off course and some new functionalities in the ABMNavigationBar. Donators should have received this version by now. (mail me if you didn’t).


Click here to Donation if you like my work


%d bloggers like this: