B4J: New in ABMaterial 3.50: Drag and Drop about anything


It may have been a bit silent the last few weeks around ABMaterial, but behind the scenes I have been working nights and weekends on maybe the biggest new feature since Chipmunk was introduced: Drag & Drop!

In 3.50, it will be possible to drag ABMContainers (and hence about anything in it) from one cell to another.

Let’s watch a video demonstration first:

Pretty cool hè!

How it works in B4J:
I explain the basics here, the DragDrop demo project included in the ABMaterial 3.50 download will show all tricks shown in the demo video.

1. Page_ParseEvent() gets a little bit of extra code to sync the moves made in the browser with our server

If SubExists(Me, eventName) Then
     If eventName = "page_dropped" Then
     End If
     Select Case Params.Size

2. Next we define a ‘DRAG’ group:

' name, minimum height of a cell
page.DragDropCreateGroup("DRAG", 150)

3. We add cells to this ‘DRAG’ group we want to drag & drop between. The last ‘null’ parameter can be any ABMComponent you want to use a title of the cell (e.g. the COLUMN X ABMLabel in the demo).

page.DragDropAddZone("DRAG", "DRAGCELL1", page.Cell(3,1),null)
page.DragDropAddZone("DRAG", "DRAGCELL2", page.Cell(3,2),null)
page.DragDropAddZone("DRAG", "DRAGCELL3", page.Cell(3,3),null)

4. We add our draggable ABMContainers to the cells. Note: they MUST be added with AddArrayComponent!

Here we also enable them to be draggable with cont.EnableDragDropZone().  There is also a shortcut method to enable/disable all ‘Zones/Cells’ in a group with cont.EnableDragDropAllZonesFromGroup() in one go.

page.Cell(3,1).AddArrayComponent(CreateCard("1", True, True, True, "I may be dragged everywhere", "../images/list1.jpg"), "Cards")

Sub CreateCard(ID As String, AllowInCell1 As Boolean, AllowInCell2 As Boolean, AllowInCell3 As Boolean, Title As String, image As String) As ABMContainer
  Dim cont As ABMContainer
  cont.Initialize(page, ID, "")
  cont.AddRows(1,True,"").AddCells12(1, "")

  cont.IsTextSelectable = False

  If AllowInCell1 Then
    ' can be dragged to zone 1 of 'DRAG'
  End If
  If AllowInCell2 Then
    ' can be dragged to zone 2 of 'DRAG'
  End If
  If AllowInCell3 Then
    ' can be dragged to zone 3 of 'DRAG'
  End If
  cont.EnableDragDropZone("DRAG","DRAGCELL4",True) ' for the sidebar

  Dim card As ABMCard
  card.InitializeAsCard(page, "card", "MY CARD", Title, ABM.CARD_SMALL, "")
  card.Image = image
  card.AddAction("Press me")

  ' add the card to the page
  Return cont
End Sub

5. There are three events you will receive (all on Page level):

Sub Page_DragStart(component As String, source As String)
  Log("Drag start: " & component)
End Sub

Sub Page_DragCancelled(component As String, source As String)
  Log("Drag cancelled: " & component)
End Sub

' there are four parameters: component, source , target, before
Sub Page_Dropped(Params As Map)
  Log("Dropped: " & Params.Get("component"))
End Sub

And this is basically it! Very simple without having to write any HTML, CSS, PHP or Javascript. All done in pure B4J!

I’m finishing up ABMaterial 3.50, ABMonitor (an upcoming article on this for B4J and B4A soon on the blog) and ABTreeTableView now and early next week I think I may be able to release it to the donators.



Click here to Donation and support ABMaterial

B4X: ABMaterial 3.03 Public/3.20 Donators released

A lot of internal changes were made in the ABMaterial 3.20 ‘Chipmunk’ release.  For example, ABMContainer has been rewritten from the ground-up, but with minimal impact to your existing projects.  It allows adding them as array components, and has much shorter ID tags in the generated HTML.

Mindful and Stanmiller had another go on the cache issues.  They came up with a very nice solution (I had tears in my eyes) and both have done extensive testing on this subject. A big thanks again!  ABM 3.20 is backwards compatible with the previous 3.0.3 Cache System, but I would advice you to make the changes whenever you have the chance.

A nice new feature for mobile devices is the New Debug feature using a modified version of Conzole!

New Debug at work:

Using this new feature is very simple in B4J:

First, you have to enable the console in BuildPage():

' params are: open on load, width
page.DebugConsoleEnable(True, 300)

Two methods can be used to show or hide the console programmaticaly. Can be handy as a buid-in tool in your apps where the user enters a code on his device and you can then check the console.


Now you can start doing some cool stuff 🙂 Some examples:

Show some text, or a server value (note: strings must be between single quotes!)

page.DebugConsoleLog("'Server click at: " & DateTime.Time(DateTime.Now) & "'") ' shows the text

Now I generated an error on purpose, so the demo can show it catches the error:

page.DebugConsoleLog("'value: ' + $('#pi002-r2c1').html();") ' generates an error, the ; at the end is wrong

Same code, but with the correct javascript syntax, showing the html() value of the tag:

page.DebugConsoleLog("'value: ' + $('#pi002-r2c1').html()") ' generates the html value of the pi002-r2c1 tag

It works also on the desktop, although you probably will use the Chrome console as you can do more.

But there is more. You can also redirect all browser console message directly into the B4J log window!  You can use the following commands anywhere in your own code to start/stop receiving the browser console messages.


This is a very powerful feature if you develop Webapps for mobile devices.  It can be done otherwise, but it is a pain in the a$$ to set it up. Now, you can easily debug it on your iPhone, iPad, Android or any other device.

ABMaterial 3.20 has over 20 new features and bug fixes.  Following the same philosophy as B4X, the quick release cycle has proven to work very well for ABMaterial and its many users.  Donators will receive their download mail shortly.  The public version 3.03 can be downloaded from this blog too.

I’m also glad that with these releases, now everyone can work with ‘Chipmunk‘, and this does also mean the development cycle for ‘Butterfly‘  has ended.

I would like to take this opportunity to thank everyone for the support, hourless testing and inventive ideas you came up with in the last few years. You’ve made ABMaterial one hell of a WebApp Development Tool!


But we are not at the end! Far from it as I already have big ideas for the next releases.

Until then, happy programming!


Click here to Donation and support ABMaterial

B4X: An experiment for practical use of IoT on the shop floor


At OneTwo, we are always searching for ways to make things easier on the workfloor.  For nearly a decade, we introduced barcode scanners everywhere! From architects and accountants, over carpenters and contractors, to farmers and gardeners are now using our small barcode scanners to record times, jobs and used materials.

The last couple of weeks, I’ve been investigating how we could tackle another common issue, but now on the factory shop floor.  Working on a specific step in the development of a product demands the constant focus of the operator so using a barcode scanner to get the instructions is, well, just not practical if you have your hands full.

Time to get to the lab! Provided with heaps of coffee, some unhealthy snacks and an open mind we started putting some ideas together on the whiteboard.  We wanted to give as much feedback to the operator as possible using monitors,  beamer projections at the workstation and personal instructions on phones and tablets.

The key to deliver hyper-contextual content to the users is knowing the location of every chess piece in the game: the workstation, the machine that is going to be build and the operator(s).  The underlying communication technology was going to be Bluetooth Low Energy (BLE). The Raspberry Pi has it and so does almost any phone or tablet. By attaching a cheap BLE Beacon to the machine we could understand the location of all the players.

Using B4X for this project was a no-brainer, as Erels toolbox is just made to build stuff like this! One important link in the chain was missing:  the BLE reader in B4J for the Raspberry Pi.  However, writing some small scripts and calling it using jShell and a couple of timers did the job just fine.

Scripts code:

Two scripts to discover BLE devices around the Raspberry Pi.  Each one runs in its own jShell in B4J. First one scans for devices, second one reads whatever they broadcast.

# Beacon Scan by Alain Bailleul 2017
sudo timeout -s SIGINT 5s hcitool lescan

# Beacon Scan by Alain Bailleul 2017
sudo timeout -s SIGINT 5s hcidump -X > scan.txt

Also, as the Raspberry Pi has to be a BLE beacon itself (so the Android Native app can discover it), I had to write another little script:

# Beacon Scan by Alain Bailleul 2017

sudo hciconfig hci0 leadv 3
sudo hcitool -i hci0 cmd 0x08 0x0008 19 02 01 06 03 03 aa fe 11 16 aa fe 10 00 02 6f 6e 65 2d 74 77 6f 07 $1 $2 $3 00 00 00 00 00 00
sudo hciconfig

IPN=$(ip addr show eth0 | awk ‘/inet / {print $2}’ | cut -d/ -f 1)
IPW=$(ip addr show wlan0 | awk ‘/inet / {print $2}’ | cut -d/ -f 1)
MACN=$(ip link show eth0 | awk ‘/ether/ {print $2}’)
MACW=$(ip link show wlan0 | awk ‘/ether/ {print $2}’)

echo “$MACW;$IPW;$MACN;$IPN;END” > macip.txt

For anything related to presenting the instructions, we could use ABMaterial.

Alright! We’ve got everything we needed to set up a test scenario for a workstation so time to bring out the cool slide:

How it works

Although there are a lot of physical components and different OS’s in play, B4X has all the tools to make them seamlessly talk to each other using one language.  For the native Android part, I wrote my own BLE discovery library based on the altbeacons library. (Note, there is a library available in B4A to discover BLE devices).

For the video demonstration, things you have to keep in mind:


  1. OneTwo box + BLE beacon: represents a machine arriving on the workstation
  2. Raspberry Pi (under the table): represents the workstation
  3. Android Phone: represents an operator/visitor at the workstation


  1. On the monitor, for each ‘machine’ some info is presented (e.g. a serial number, to what country it has to be shipped, etc)
  2. On the phone: specific instructions for the operator(s) for this ‘machine’, or a greeting to a visitor.
  3. On the workstation, projected global instructions for the operator(s) for this ‘machine’ with a beamer.

Let’s have a look how all of this plays out:

This turned out to work all very well for a first trial! For real world usage, the project will need some more work (like improving the algorithms to discover the BLE devices, setting thresholds or calculating them without calibration, etc…).  But we are very excited with the possibilities this low-cost solution can already show in such a short time.

Next step will be setting this up on a real workstation on the shop floor in a real factory later this month.

Until next time,


Click here to Donation and get the latest ABMaterial first!



B4J: ABMaterial Facebook-like timeline in less than 200 lines!


I recently had a question if it was possible to create a Facebook-like WebApp with ABMaterial for B4J. I had some time to spare tonight (yeah right):rolleyes: and decided to give it a go…

All I needed were a couple of ABMContainers and the new CloseContent/OpenContent feature introduced in 3.02. The existing NextContent system would provide the infinite page.

It also uses a ABMCustomComponent for the ‘Like’ feature (I based it on this project), and you can find the source code of the class under the video:

About an hour, and 200 lines of B4J code later, I had a working timeline!  A real project would of course need some way to add an article, save them in the database, user management, etc but this is out of scope of this article.

Note: This example will be included in the upcoming 3.02 Maintenance Update, but I would like to share the source source code for the ‘Like’ functionality beforehand.

Source code for the ‘Like’ custom component.
You’ll need this zip file for the images and js/css files. Put the images and css in the /www/css/custom folder, the js file in the /www/js/custom/ folder.

The class LikeComponent:

'Class module
Sub Class_Globals
   Public ABMComp As ABMCustomComponent
   Public Counter As Int
End Sub

'Initializes the object. Countries and data are Json Strings
Public Sub Initialize(InternalPage As ABMPage, ID As String, AlreadyLiked As Int)
   ABMComp.Initialize("ABMComp", Me, InternalPage, ID)
   Counter = AlreadyLiked
End Sub

Sub ABMComp_Build(internalID As String) As String
   Return $"
<div id="${internalID} class="facebook-reaction"><!-- container div for reaction system -->
        <span class="like-btn"> <!-- Default like button -->
          <span class="like-btn-emo like-btn-default"></span> <!-- Default like button emotion-->
           <span class="like-btn-text">Like</span> <!-- Default like button text,(Like, wow, sad..) default:Like  -->
<ul class="reactions-box"> <!-- Reaction buttons container-->
	<li class="reaction reaction-like" data-reaction="Like"></li>
	<li class="reaction reaction-love" data-reaction="Love"></li>
	<li class="reaction reaction-haha" data-reaction="HaHa"></li>
	<li class="reaction reaction-wow" data-reaction="Wow"></li>
	<li class="reaction reaction-sad" data-reaction="Sad"></li>
	<li class="reaction reaction-angry" data-reaction="Angry"></li>
<div class="like-stat"> <!-- Like statistic container-->
           <span class="like-emo"> <!-- like emotions container -->
             <span class="like-btn-like"></span> <!-- given emotions like, wow, sad (default:Like) -->
           <span class="like-details">${Counter} others</span></div>
End Sub

Sub ABMComp_FirstRun(InternalPage As ABMPage, internalID As String)
   Dim script As String = $"$("#${internalID} .reaction").on("click",function(){  // like click
     var data_reaction = $(this).attr("data-reaction");
     $("#${internalID} .like-details").html("You and ${Counter} others");
     $("#${internalID} .like-btn-emo").removeClass().addClass('like-btn-emo').addClass('like-btn-'+data_reaction.toLowerCase());
     $("#${internalID} .like-btn-text").text(data_reaction).removeClass().addClass('like-btn-text').addClass('like-btn-text-'+data_reaction.toLowerCase()).addClass("active");;

     if(data_reaction == "Like") {
         $("#${internalID} .like-emo").html('<span class="like-btn-like"></span>');
     } else {
         $("#${internalID} .like-emo").html('<span class="like-btn-like"></span><span class="like-btn-'+data_reaction.toLowerCase()+'"></span>');
     var json = {'target': '${internalID}', 'like': data_reaction};
     b4j_raiseEvent('likecomponent_liked', json);

     $("#${internalID} .like-btn-text").on("click",function(){ // undo like click
       $("#${internalID} .like-btn-text").text("Like").removeClass().addClass('like-btn-text');
       $("#${internalID} .like-btn-emo").removeClass().addClass('like-btn-emo').addClass("like-btn-default");
       $("#${internalID} .like-emo").html('<span class="like-btn-like"></span>');
       $("#${internalID} .like-details").html("${Counter} others");
       var json = {'target':'${internalID}'};
         b4j_raiseEvent('likecomponent_unliked', json);

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

Sub ABMComp_Refresh(InternalPage As ABMPage, internalID As String)

End Sub

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

End Sub


Sub Class_Globals
   Dim myLike As LikeComponent
End Sub

' adding the component
myLike.initialize(page, ID & "Like", Rnd(20, 500))

Events (the name LikeComponent is set in the FirstRun() method of the class:

public Sub LikeComponent_Liked(value As Map)
   Log(value.Get("like") & " ----> " & value.Get("target"))
End Sub

public Sub LikeComponent_UnLiked(value As Map)
   Log("Unliked ----> " & value.Get("target"))
End Sub

Et voila,  a nice little demonstration of what you can make using B4J and ABMaterial.

I’ll end with a little word of advice:

If you only plan to learn one programming language this year, B4X is definitely the one you should pick!  With ABMaterial, I give you the power to write responsive, professional WebApps, and with the other B4X tools you write native Android, iOS, Desktop (Mac, Windows, Linux), Raspberry Pi and Arduino apps.  All with the same language!  You’ll wonder why you ever bothered looking at all those other pricy programming tools out there…

Happy programming!


Click here to Donation and get the latest ABMaterial first!

B4J: ABMaterial 2.51 Public/3.00 ‘Chipmunk’ for Donators Released!


I’m very happy to release ABMaterial 3.00 “Chipmunk” for B4J with over 35 new features and bugfixes!  The most easy-to-use framework for developing professional responsive WebApps for all platforms is now at its third major release.

‘Chipmunk?’, I hear you think… Yes, from now on, every major version of ABMaterial will have a codename. Those names were already used internally and are animal names, matching the versions position in the alphabet. (FYI, version 1.x was “Albatros“, 2.x was “Butterfly“). For version 4.0, I’ll let you decide what it is called… :)

For the occasion, I also gave the demo app a new Theme. Make sure to check it out.

Please express your thanks to Mindful, who has done a remarkable effort to join me for a 3 day program-jam session to solve the reconnecting issues. The solution looks very clean and is easy to understand. A big thanks Mihai!

Chipmunk now also has the possibility to generate template projects for both B4J and B4A using a WebView wich makes it possible to use the Native functionalities (like accessing a camera, contacts, folders and much more) on your Android and Desktop/Raspberry Pi devices. An extra ABMController library makes it easy to communicate bidirectional between the WebView and the B4X code. The ABMController library for B4i is in the making.

The public version 2.51 with the Google Analytics bugfix is available to download here.

Donators will receive their download link for Chipmunk shortly.


Click here to Donation and get the latest ABMaterial first!


B4A: ABFlicB4A library for Flic buttons


You probably have seen these nifty little IoT Flic buttons around. They are a fun and relative cheap BLE button that allows you to do something when the button is clicked, doubleclicked or hold.

I decided to write a wrapper for the Android SDK to use with B4A.

How to use:
1. Install the Flic app from the Google Play.
2. On their developer page, create a new app: you get a key and a secret.
3. Copy the ABFlicB4A library jar and xml to your library folder and select it in B4A

Here is a small video demonstrating the library.  You can the use the full power of B4A to do about, well, everything…

Example usage code:

Sub Process_Globals

End Sub

Sub Globals
   Private flic As ABFlic
   Private Button2 As Button
   Private Button3 As Button
   Private Button4 As Button
   Private Label1 As Label
   Private Button1 As Button

   Private MyFlicID As String

   Private FlicResults As List
End Sub

Sub Activity_Create(FirstTime As Boolean)
End Sub

Sub Activity_Resume
   ' your key and secret
End Sub

Sub Button1_Click
End Sub

Sub Button2_Click
End Sub

Sub Button3_Click
End Sub

Sub Button4_Click
End Sub

Sub flic_Added(buttonID As String, Name As String)
   MyFlicID = buttonID
   Log("Added: " & buttonID)
   Label1.Text = "Added: " & buttonID & CRLF & Label1.Text
End Sub

Sub flic_Clicked(buttonID As String, wasQueued As Boolean, timeDiff As Int)
   MyFlicID = buttonID
   Log("Clicked: " & buttonID)
   Label1.Text = "Clicked: " & buttonID & CRLF & Label1.Text
End Sub

Sub flic_DoubleClicked(buttonID As String, wasQueued As Boolean, timeDiff As Int)
   MyFlicID = buttonID
   Log("DoubleClicked: " & buttonID)
   Label1.Text = "DoubleClicked: " & buttonID & CRLF & Label1.Text
End Sub

Sub flic_Holded(buttonID As String, wasQueued As Boolean, timeDiff As Int)
   MyFlicID = buttonID
   Log("Holded: " & buttonID)
   Label1.Text = "Holded: " & buttonID & CRLF & Label1.Text
End Sub

Sub flic_Removed(buttonID As String)
   MyFlicID = ""
   Log("Removed: " & buttonID)
   Label1.Text = "Removed: " & buttonID & CRLF & Label1.Text
End Sub

Sub flic_Error(err As Int)
   Log("Error: " & err)
   Label1.Text = "Error: " & err & CRLF & Label1.Text
End Sub

I’m currently working on a Desktop/Raspberry Pi version of this library, which I will share in the B4J forum later.

The library can be found at the B4A forum.

Happy programming!


B4J:Programming LEGO Mindstorms EV3


I was wondering if it would be possible to use B4J to program the Lego Mindstorms EV3 Brick for some time. So, some weeks ago, I finally spend a couple of evenings looking into it. And yes, B4J goes Robotics! It uses a modified LeJOS Firmware.

I upgraded LeJOS to support Java 1.8, which is probably the most technical part of the project.  I cannot provide it here, as it depends on your Java version but if you follow the next steps, you can build your own.  This is done on a Windows PC:

Upgrading the LeJOS Firmware to use Java 1.8

The following procedure will install the java 8 environment compatible with leJOS 9.0 from windows 7. The only extra tool needed is 7ZIP (a free tool) which can generate tar and gz format.  In the following I use a temporary directoyr as F:\temp. Replace it with your own temp directory wherever it appears.

  1. Go to java site http://www.oracle.com/technetwork/java/ … 82511.html
  2. Create an account if you dont have one then Accept Licensee agreement and download ejdk-8-xxxxxxx-linux-arm-sflt-xxxxxxxx.tar.gz or the latest java8 jre for EV3.
  3. Unzip this file in your F:\temp directory. For simpler process you may rename the extracted directory  as ejdk8. Run 7zip as administrator!
  4. Go to directory ejdk8\ejdk1.8.0\bin. Save the file jrecreate.bat. Now right click on jrecreate.bat and select “modify”.  Add one first line to the bat file as SET JAVA_HOME=C:\Progra~2\Java\jdk1.8.xxxx.  this is where java8 is installed on my system. Program~2 stand for Program Files (x86) but is easier to type.
  5. Start a command window: Start->All prog->Accessory->commands.
  6. In this window type in the following commands
    cd temp
    cd ejdk8\ejdk1.8.0\bin
    jrecreate.bat –dest newjre –profile compact2 –vm clientIt should start creating the new jre files in the directory “newjre” under ejdk8\ejdk1.8.0\bin
    Wait for the final message.
    Close the command window.
  7. With file explorer go to F:\temp\ejdk8\ejdk1.8.0\bin. Copy “newjre” directory back to F:\temp
  8. Rename the “newjre” directory as something expected by LeJOS such as
  9. Right click on it. Open with 7ZIP and add to archive. Select tar format. It will create ejre-8-b132-linux-arm-sflt.tar file.
  10. Right click on tar copy then add archive with gzip format. It will create ejre-8-b132-linux-arm-sflt.tar.gz. You have created the JRE file for java8 expected by EV3.
  11. Insert your SD card and format it.
  12. Copy the ejre-8-b132-linux-arm-sflt.tar.gz under the sd card top directory.
  13. Go to C:\Program Files\leJOS EV3
  14. Now remove the SD from PV and insert the card into the EV3 brick then power it. Wait for the end of the install (about 10mn).

If anything goes wrong, format again the SD, empty the F:\temp directory and restart at step 3.  if it persist try another SD card.

Your brick is ready!

Next I wrote a B4J wrapper for the basics like motors, sensors etc. This project is just a hobby and may grow in the future. But I just wanted to share this first video (badly directed so no Oscars here, unless…)

Obviously, I wanted to test out my code, but I had to build a bot first.  Well, I was in for a surprise! It took me over 3 hours to build EV3D4 and I can tell you, I’m not a very patient person so I may have taken some shortcuts in the construction.

What happens in the video:
1. I uploaded the B4J created .jar file to the brick via ssh and I’m ready to start it.
2. After some time, it runs (on the brick shows a disclamer and waits for a key).
3. I disconnect the brick, put it on the ground and press a button on the brick.
4. I programmed the IR sensor so if it sees a wall, it turns away, else it keeps going.

Fun fact is you can use B4J-Bridge.jar on the brick!

This is handy as debugging a Brick is a b*tch. You must have a lot of patience and prepared to restart your brick A LOT. This is not a B4J limitation, just to say how unstable programming a Brick is in general.

Coding is straightforward in B4J (this is all the code I wrote, including turning if it is about to hit a wall):

Sub Process_Globals
   Private StartTimer As Timer
   Public EV3 As ABLegoEV3
   Public LeftMotor As ABLLargeRegulatedMotor
   Public RightMotor As ABLLargeRegulatedMotor
   Public Sensor As ABLIRSensor
   Public sp As ABLSensorMode

   Public control As Int = 0
   Public distance As Int = 255
End Sub

Sub AppStart (Args() As String)

   LeftMotor.Speed = 400
   RightMotor.Speed = 400
   LeftMotor.Acceleration = 800
   RightMotor.Acceleration = 800

   Log("starting sensor")
   sp = Sensor.DistanceMode 


   StartTimer.Initialize("StartTimer", 100)
   StartTimer.Enabled = True
End Sub

Sub StartTimer_Tick
   ' read the sensor
   Dim sample(sp.sampleSize) As Float
   control = Sensor.getRemoteCommand(0)
   sp.fetchSample(sample, 0)
   distance = sample(0)
   Log("Control: " & control & " Distance: " & distance) 

   If distance < 70 Then
     Log("A wall!")

     LeftMotor.rotate2(-180, True) ' start Motor.B rotating backward
     RightMotor.rotate(-180)  ' rotate C farther To make the turn
     If Bit.And(DateTime.Now, 1) <> 0 Then
         LeftMotor.rotate2(-180, True) ' start Motor.B rotating backward
         RightMotor.rotate(180)  ' rotate C farther To make the turn
         RightMotor.rotate2(-180, True) ' start Motor.B rotating backward
         LeftMotor.rotate(180) ' rotate C farther To make the turn
     End If
     Log("Let's walk")

     LeftMotor.Speed = 400
     RightMotor.Speed = 400
     LeftMotor.Backward ' my motors are installed inverted on this robot
  End If
End Sub

Sub IntroMessage()
   Dim g As ABLGraphicsLCD = EV3.GraphicsLCD
   g.drawString("Bumper Car Demo", 5, 0, 0)
   g.Font = g.Font.SmallFont
   g.drawString("Demonstration of the Behavior", 2, 20, 0)
   g.drawString("subsumption classes. Requires", 2, 30, 0)
   g.drawString("a wheeled vehicle with two", 2, 40, 0)
   g.drawString("independently controlled", 2, 50, 0)
   g.drawString("motors connected to motor", 2, 60, 0)
   g.drawString("ports B and C, and an", 2, 70, 0)
   g.drawString("infrared sensor connected", 2, 80, 0)
   g.drawString("to port 4.", 2, 90, 0)

   ' Quit GUI button:
   g.Font = g.Font.SmallFont
   Dim y_quit As Int = 100
   Dim width_quit As Int = 45
   Dim height_quit As Int = width_quit/2
   Dim arc_diam As Int = 6
   g.drawString("QUIT", 9, y_quit+7, 0)
   g.drawLine(0, y_quit,  45, y_quit) ' top line
   g.drawLine(0, y_quit,  0, y_quit+height_quit-arc_diam/2) ' left line
   g.drawLine(width_quit, y_quit,  width_quit, y_quit+height_quit/2) ' right line
   g.drawLine(0+arc_diam/2, y_quit+height_quit,  width_quit-10, y_quit+height_quit) ' bottom line
   g.drawLine(width_quit-10, y_quit+height_quit, width_quit, y_quit+height_quit/2) ' diagonal
   g.drawArc(0, y_quit+height_quit-arc_diam, arc_diam, arc_diam, 180, 90)

   ' Enter GUI button:
   g.fillRect(width_quit+10, y_quit, height_quit, height_quit)
   g.drawString2("GO", width_quit+15, y_quit+7, 0,True)

   If (EV3.Button.ESCAPE.isDown()) Then
   End If
End Sub

As I said, this is just for fun. No idea how far this will go.  You can download the source code for the B4J library from my Github.



B4J: Msgbox and Inputbox in ABMaterial 2.51

upload_2017-2-12_12-0-6Sometimes you just need to show a quick Msgbox or a single-field Inputbox to the user. In ABMaterial for B4J you have probably, just like me, already made a couple of modal sheets to do this.

But in 2.51 I’ve integrated the possibility for such cases just to do it in one line of code (like a normal msgbox/inputbox).

It replaces the default looking ‘alert’ box from JavaScript to a more modern looking box.  The SweetAlert2 library, which is build on the orginal SweetAlert one, has been extended for ABMaterial so you can ‘Theme’ about anything you want.

For example, to show the msgbox you see on top of this article, this is the code you need to write:

page.Msgbox("MSGBOX1", "This is a very simple msgbox", "Msgbox()", "OK", "")

Some more examples:

theme.MsgBox("image").ConfirmButtonColor = ABM.COLOR_GREEN
theme.MsgBox("image").CancelButtonColor = ABM.COLOR_RED
theme.MsgBox("image").SetImage("https://unsplash.it/400/200", 400, 200)
theme.MsgBox("image").BackColor = ABM.COLOR_BLUEGREY
theme.MsgBox("image").BackColorIntensity = ABM.INTENSITY_LIGHTEN5

page.Msgbox2("MSGBOX3", "This is an msgbox with an image using a {C:#00AA00}theme{/C}!", "Msgbox2() 2", "OK","CANCEL",True, ABM.MSGBOX_TYPE_WARNING, "image")


page.InputBox("INPUTBOX1", "This is an inputbox for text.","OK", "CANCEL", True, ABM.INPUTBOX_TYPE_QUESTION, ABM.INPUTBOX_QUESTIONTYPE_TEXT,"","Type some text...", "", "Please enter something!", "")


page.InputBox("INPUTBOX3", "This is an inputbox for a password.","OK", "CANCEL", True, ABM.INPUTBOX_TYPE_QUESTION, ABM.INPUTBOX_QUESTIONTYPE_PASSWORD,"","Type your password...", "", "Please enter a password", "")


page.InputBox("INPUTBOX6", "This is an inputbox with a radio selection.","OK", "CANCEL", True, ABM.INPUTBOX_TYPE_QUESTION, ABM.INPUTBOX_QUESTIONTYPE_RADIO,"#00ff00", "","{'#ff0000': 'Red','#00ff00': 'Green','#0000ff': 'Blue'}", "You must select a color", "")


page.InputBox("INPUTBOX7", "This is an inputbox with a checkbox.","OK", "CANCEL", True, ABM.INPUTBOX_TYPE_QUESTION, ABM.INPUTBOX_QUESTIONTYPE_CHECKBOX,"", "I agree with the terms and conditions","", "Please agree with the conditions", "")



Sub page_MsgboxResult(returnName As String, result As String)
   myToastId = myToastId + 1
   page.ShowToast("toast" & myToastId, "toastgreen", returnName & " " & result, 5000)
End Sub

Sub page_InputboxResult(returnName As String, result As String)
   myToastId = myToastId + 1
   page.ShowToast("toast" & myToastId, "toastred", returnName & " " & result, 5000)
End Sub

ABMaterial Mainenance Release 2.51 will be available for the donators in the following days.


Click here to Donation and get the latest ABMaterial first!

B4J: Custom component ABMGridTable (Team Project)

One of the most asked for components in ABMaterial is an editable Grid.  We already have ABMTable and ABMTableMutable, but they have their limitations when it comes to being editable. So, as this kind of object is huge, I suggest we make it a Team Effort to build one.

I did have a look around and found a nice one jsGrid which is feature rich, easy to use and fits rather well with the ABMaterial look and feel.

I already did the first part: modified the default CSS so it works with ABMaterial (checkboxes and combos did not work), created a basic class template and a sample on how to use it in a webapp.

Copy the CSS and JS files from the zip the css/custom/ and /js/custom/ folders of the ABMaterial www folder.
Start with localhost:51042/GridDemo

My current result looks like this (allows adding, deleting, changing, sorting and filtering):


Creating even such a complex component is very easy in B4J with ABMaterial.  Lets dive into some code:

The ABMTableGrid class

'Class module
Sub Class_Globals
	Public ABMComp As ABMCustomComponent
	Private cmbCountries As String
	Private DBdata As String
End Sub

'Initializes the object. Countries and data are Json Strings
Public Sub Initialize(InternalPage As ABMPage, ID As String, Countries As String, data As String)
	ABMComp.Initialize("ABMComp", Me, InternalPage, ID)
	cmbCountries = Countries
	DBdata = data
End Sub

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

Sub ABMComp_FirstRun(InternalPage As ABMPage, internalID As String)
	Dim script As String = $"_${internalID} = $("#${internalID}").jsGrid({
		height: "100%",
		width: "100%",
		filtering: true,
		inserting: true,
		editing: true,
		sorting: true,
		paging: true,
		autoload: true,
		pageSize: 10,
		pageButtonCount: 5,
		deleteConfirm: "Do you really want to delete the client?",
		controller: {
			loadData: function(filter) {
				return $.grep(${DBdata}, function(client) {
					var fName = filter.Name.toUpperCase();
					var fAddress = filter.Address.toUpperCase();
					return (!filter.Name || client.Name.toUpperCase().indexOf(fName) > -1)
						&& (!filter.Age || client.Age === filter.Age)
						&& (!filter.Address || client.Address.toUpperCase().indexOf(fAddress) > -1)
						&& (!filter.Country || client.Country === filter.Country)
						&& (filter.Married === undefined || client.Married === filter.Married);
			insertItem: function(insertingClient) {
				var json = JSON.stringify(insertingClient);
				b4j_raiseEvent('${ABMComp.ID}_inserted', {'value':json});
			updateItem: function(updatingClient) {
				var json = JSON.stringify(updatingClient);
				b4j_raiseEvent('${ABMComp.ID}_updated', {'value':json});
			deleteItem: function(deletingClient) {
				var json = JSON.stringify(deletingClient);
				b4j_raiseEvent('${ABMComp.ID}_deleted', {'value':json});
		fields: [
			{ name: "Name", type: "text", width: 150 },
			{ name: "Age", type: "number", width: 50 },
			{ name: "Address", type: "text", width: 200 },
			{ name: "Country", type: "select", items: ${cmbCountries}, valueField: "Id", textField: "Name" },
			{ name: "Married", type: "checkbox", title: "Is Married", sorting: false},
			{ type: "control" }

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

public Sub SetDBData(data As String)
	DBdata = data
End Sub

Sub ABMComp_Refresh(InternalPage As ABMPage, internalID As String)
	Dim script As String = $"$("#${internalID}").jsGrid("refresh");"$
	InternalPage.ws.Eval(script, Null)
End Sub

Usage in a WebApp:

Sub Class_Globals
	Dim myTableGrid As ABMTableGrid
	' fake database for demo purposes
	Dim database As Map
End Sub

' make sure you copied the files from the .zip in the right folders
public Sub BuildPage()
End Sub

public Sub ConnectPage()
	' as for the demo, we fake a real database here


	' for our combo, we add a Json string counting the countries
	myTableGrid.Initialize(page, "myTableGrid", $"[
		{ Name: "", Id: 0 },
		{ Name: "United States", Id: 1 },
		{ Name: "Canada", Id: 2 },
		{ Name: "United Kingdom", Id: 3 },
		{ Name: "France", Id: 4 },
		{ Name: "Brazil", Id: 5 },
		{ Name: "China", Id: 6 },
		{ Name: "Russia", Id: 7 }
	]"$, LoadData)
End Sub

Sub LoadData() As String
	' we have to return a Json string
	Dim data As StringBuilder
	For i = 0 To database.size - 1
		If i > 0 Then
		End If
	Return data.ToString
End Sub

Sub myTableGrid_Inserted(value As Map)
End Sub

Sub myTableGrid_Updated(value As Map)
End Sub

Sub myTableGrid_Deleted(value As Map)
End Sub

Sub LoadFakeData()
	database.Put(0,$"{"ID": 0,"Name": "Otto Clay","Age": 61,"Country": 6,"Address": "Ap #897-1459 Quam Avenue","Married": false}"$)
	database.Put(1,$"{"ID": 1,"Name": "Connor Johnston","Age": 73,"Country": 7,"Address": "Ap #370-4647 Dis Av.","Married": false}"$)
End Sub

As you can see, adding this rather complex component took me about 10 minutes to make, literally the time to put on a pot of Java. (pun intended!)

So this is were you guys can come in!

Some suggestions:
1. Adapt my class so the fields are generic (maybe by passing a Map with field definitions in initialize and then ‘build’ the correct JavaScript string in FirstRun?).
2. I only have set some basic parameters (like sorting, filtering etc), but this component has a lot more to offer. They can become properties of the class. See the jsGrid link at the top of this article.

Download the full source code and share your changes in this B4J Forum topic, so everyone can benefit from your creation.


Click here to Donation if you like my work

B4J: ABMaterial 2.20 Public/2.50 Donators released


This is just an announcement for the new version(s) of ABMaterial for B4J! It has been a while since I’ve been able to release a new free (public) version, but it is worth the wait!

As the donators already know, 2.20 was a major release, as it tackled a whole new reconnecting system.  If you’re using the free version (pre 2.00), make sure you read the included documents to see how you need to upgrade to the new version!

It was also the first version that introduced the Grid Builder.  With this tool, it is now very easy to create your responsive framework.

2.50 containes a lot of new stuff, as you’ve read in the posts on this blog: ABMChat, ABMPlanner and ABMPercentSlider are only a few of the new things.  Worth mentioning are the major changes in the ABMPivot component, adding new features like exporting to Excel, collapsable rows and columns, new presentations and so on.

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

Donators will receive a download link for 2.50 shortly.

Happy Programming!


Click here to Donation if you like my work

B4J: New ABMPlanner and ABMPercentSlider (ABMaterial 2.50)

abmplannerA friend of mine, who is a doctor, wanted me to write a scheduling webapp. He had some perticular requests:

1. You should be able to schedule per 5 minutes
2. But still need to be able to see the whole week
3. Wanted only to see the 5 working days (no weekends)
4. Should see from 06:00 to 21:00
5. As his assistant has to ‘fill’ the schedule, he also wanted somehow to show her when he would not be available.

I told him this quickly added up to showing about 840 possible appointments I had to show him on his desktop, tablet and iPhone. “Yes”, was his dry answer…

I didn’t know any such object, so I’ve put on my thinking hat and came up with ABMPlanner for B4J!

Although it has been written especially for his needs, I’m sure it can come in handy for other kind of projects too. For example you can schedule per 5, 10, 15, 20 or 30 minutes. You can set the start/end time of a day. And it can show a full week (including the weekend). You can give each task different colors etc…

It can also show the week as a ‘HeatMap’, so he can see very fast where he can put someone in-between other patients.

The second component ABMPercentSlider (in the video when the modalsheet is opened) is an alternative radio group component. It ranges the possibilities in a ‘percentage’ range, with 5% intervals.

But have a look at the video (app is work in progress and sorry it is in dutch). It can be intimidating at first glance, but once you ‘get’ it, it’s actually a breeze to schedule stuff.

Some relevant code:


Dim plan As ABMPlanner
plan.Initialize(page, "plan", False, 6,20, 5,False,"black")

Adding a task (loading a random generated week):

Sub LoadRandomWeek(StartDay As Long,doRefresh As Boolean)
   Dim plan As ABMPlanner = page.Component("plan")

   Dim Days(7) As String
   Dim Day As Int = DateTime.GetDayOfMonth(StartDay)
   Dim nextDay As Long = StartDay
   Dim perNext As Period
   perNext.Days = 1
   For i = 0 To 6
     If ActiveDay = -1 Then
       If DateTime.Date(nextDay) = DateTime.Date(DateTime.Now) Then
         ActiveDay = i
       End If
     End If
     Days(i)   = Day
     If Days(i).Length = 1 Then
       Days(i) = "0" &amp; Days(i)
     End If
     nextDay = DateUtils.AddPeriod(nextDay, perNext)
     Day = DateTime.GetDayOfMonth(nextDay)
   Dim mins As List
   plan.SetDayLabels("MA " &amp; Days(0), "DI " &amp; Days(1), "WO " &amp; Days(2), "DO " &amp; Days(3), "VR " &amp; Days(4),  "ZA " &amp; Days(5),  "ZO " &amp; Days(6))
   Dim PerTotal As Int = 12

   For i = 0 To 4
     Dim u As Int = 6
     Dim m As Int = 0
     Dim l As Int = 0
     Dim lIndex As Int
     Do While u &lt; 21
       Do While m &lt; PerTotal    
         m = m + Rnd(1,3)          
         lIndex = Rnd(0,3)
         l = mins.Get(lIndex)
         If (u*PerTotal + m + l) &lt; (21*PerTotal) And m &lt; PerTotal And (i &lt;&gt; 2 Or u &lt;=11 ) Then
           Dim patient As String = FirstNames.get(Rnd(0,19)) &amp; " " &amp; LastNames.get(Rnd(0,19))
           counter = counter + 1
           plan.AddTask(NewTask(i, "T" &amp; counter,u,m,l,patient,lIndex)) ' taskid must be a string!                
         End If
         m = m + l
       m = m - PerTotal
       u = u + 1

   For i = 12 To 20
   If doRefresh Then
   End If
End Sub


Sub plan_ActiveDayChanged(day As Int)
   Log("New day: " &amp; day)
   ActiveDay = day
   ' important to sync the server with the browser!
   Dim plan As ABMPlanner = page.Component("plan")
End Sub

Sub plan_MinutesClicked(Value As String)
   Dim spl() As String = Regex.Split(";", Value)
   ' spl(0) is here always 0, as this would contain the menu item clicked.  This is to be uniform with the MenuClicked Value param
   Log("Day: " &amp; spl(1))
   Log("Hour: " &amp; spl(2))
   Log("MinPer5: " &amp; spl(3))
   ' use the ABM.PLANNER_STATUS_ constants to check the status
   Log("Status: " &amp; spl(4))
   Log("Task ID: " &amp; spl(5))

End Sub

Sub plan_MenuClicked(MenuType As String, Value As String)
   Dim spl() As String = Regex.Split(";", Value)
   Dim plan As ABMPlanner = page.Component("plan")
   Log("MenuType: " &amp; MenuType)
   Log("Value: " &amp; Value)
   Select Case MenuType
       ' update the database

       Log("Day: " &amp; spl(1))

       Select Case spl(0)
           plan.SetDayStatus(spl(1), True)
           plan.SetDayStatus(spl(1), False)
       End Select

       ' IMPORTANT to perform the action in the browser
       ' update the database

       Log("Day: " &amp; spl(1))
       Log("Hour: " &amp; spl(2))

       Select Case spl(0)
           plan.SetHourStatus(spl(1), spl(2), True)
           plan.SetHourStatus(spl(1), spl(2), False)
       End Select

       ' IMPORTANT to perform the action in the browser
       ' update the database

       Log("Day: " &amp; spl(1))
       Log("Hour: " &amp; spl(2))
       Log("MinPer5: " &amp; spl(3))
       ' use the ABM.PLANNER_STATUS_ constants to check the status
       Log("Status: " &amp; spl(4))
       Log("Task ID: " &amp; spl(5))

       Select Case spl(0)
           cutTaskID = spl(5)
           copyTask = plan.GetTask(spl(5))
           IsCut = True
           myToastID = myToastID + 1
           page.ShowToast("toast" &amp; myToastID, "toastred", "Verplaats " &amp; copyTask.Text &amp; " naar...", 5000)
           copyTask = plan.GetTask(spl(5))
           IsCut = False
           myToastID = myToastID + 1
           page.ShowToast("toast" &amp; myToastID, "toastred", "Kopieer " &amp; copyTask.Text &amp; " naar...", 5000)
           If IsCut Then
             counter = counter + 1
             copyTask = copyTask.Clone("T" &amp; counter)
           End If
           copyTask.Day = spl(1)
           copyTask.StartHour = spl(2)      
           copyTask.StartMinututesPer = spl(3)
           If IsCut Then
             myToastID = myToastID + 1
             page.ShowToast("toast" &amp; myToastID, "toastred", copyTask.Text &amp; " verplaatst!", 5000)
             myToastID = myToastID + 1
             page.ShowToast("toast" &amp; myToastID, "toastred", copyTask.Text &amp; " gekopieerd!", 5000)
           End If
       End Select

   End Select
End Sub

These are the final components for ABMaterial 2.50. I’m now going to prepare everything for the next release. (updating the demos etc), so donators will receive these new toys shortly…


Click here to Donation and support ABMaterial



B4J: New ABMChat component for ABMaterial 2.50


ABMChat is a new cool looking ABMaterial component to create a Web based chat program. The inner workings are very much like Erels ChatRoom, but with a slick interface. This can be a useful control if for example you want to give chat-like support within you own B4J webapp.

Some features:
– Balloon/Bubble-like chat
– Support for some ASCII emoji
– IsTyping ballon

Coding this with B4J and ABMaterial is very easy:

Code ChatShared module:

Sub Process_Globals
   Public AvoidDuplicates As Map
   Private connections As Map
   Public LastMessages As List
   Private IsTypingMap As Map
End Sub

public Sub Init
   AvoidDuplicates = Main.srvr.CreateThreadSafeMap
End Sub

public Sub NewConnection(page As ChatPage, pageID As String)
   DateTime.TimeFormat = "HH:mm"

   Dim IdentifyBubble As ABMChatBubble
   IdentifyBubble.Initialize("Server", page.ChatName & " has joined the chat...", "Server " & DateTime.Time(DateTime.Now), "server", "server")
   JoinLeftMessage(pageID, IdentifyBubble)
   connections.Put(pageID, page)
   CallSubDelayed2(page, "CurrentConversation", LastMessages)
End Sub

public Sub NewMessage(pageID As String, Message As ABMChatBubble)
   For Each page As ChatPage In connections.Values
     CallSubDelayed2(page, "NewMessage", Message)
End Sub

public Sub JoinLeftMessage(pageID As String, Message As ABMChatBubble)
   For Each page As ChatPage In connections.Values
     CallSubDelayed2(page, "NewMessage", Message)
End Sub

public Sub IsTyping(pageID As String, Value As String)
   Dim typeBool As Boolean
   If Value = "" Then
     IsTypingMap.put(pageID.ToLowerCase, "true")
   End If
   If IsTypingMap.Size > 0 Then
     typeBool = True
   End If
   For Each page As ChatPage In connections.Values
     CallSubDelayed3(page, "IsTyping", pageID, typeBool)
End Sub

public Sub Disconnected(page As ChatPage, pageID As String)
   If connections.ContainsKey(pageID) = False Or connections.Get(pageID) <> page Then Return
   Dim LeaveBubble As ABMChatBubble
   LeaveBubble.Initialize("Server", page.ChatName & " has left the chat...", "Server " & DateTime.Time(DateTime.Now), "server","server")
   JoinLeftMessage(pageID, LeaveBubble)
End Sub

Public Sub HasIdentified(pageID As String) As Boolean
   Return connections.ContainsKey(pageID)
End Sub

Relevant Code ChatPage class:

Private Sub WebSocket_Disconnected
   If ABMPageId <> "" Then CallSubDelayed3(ChatShared, "Disconnected", Me, ABMPageId)
End Sub

public Sub BuildTheme()
   ' start with the base theme defined in ABMShared

   ' add additional themes specific for this page
   theme.Chat("myChat").Bubble("server").Color = ABM.COLOR_BLACK
   theme.Chat("myChat").Bubble("server").TextColor = ABM.COLOR_WHITE

   theme.Chat("myChat").Bubble("me").Color = ABM.COLOR_LIGHTBLUE
   theme.Chat("myChat").Bubble("me").TextColor = ABM.COLOR_WHITE

   theme.Chat("myChat").Bubble("them").Color = ABM.COLOR_GREY
   theme.Chat("myChat").Bubble("them").ColorIntensity = ABM.INTENSITY_LIGHTEN2
   theme.Chat("myChat").Bubble("them").TextColor = ABM.COLOR_BLACK
End Sub

public Sub ConnectPage()
'   connecting the navigation bar    

'   init all your own variables (like a List, Map) and add your components
   Dim chat As ABMChat
   chat.Initialize(page, "conversation",600, 450, 255, "mychat")
   chat.AddBubble("Server", "I'm Server, what is your name?", "The Server", "server", "server")


   Dim ChatInput As ABMInput
   ChatInput.Initialize(page, "ChatInput", ABM.INPUT_TEXT, "", False, "")
   ChatInput.PlaceHolderText = "Type your message here...."
   ChatInput.RaiseChangedEvent = True

   ' refresh the page
   ' Tell the browser we finished loading
   ' restoring the navigation bar position
End Sub

Sub ChatInput_Changed(value As String)
   If ChatShared.HasIdentified(ABMPageId) Then
     CallSubDelayed3(ChatShared, "IsTyping", ABMPageId, value)
   End If
End Sub

Sub ChatInput_EnterPressed(value As String)
   DateTime.TimeFormat = "HH:mm"

   If ChatShared.HasIdentified(ABMPageId) Then
     Dim bubble As ABMChatBubble
     bubble.Initialize(ChatName, value, ChatName & " " & DateTime.Time(DateTime.Now), "me", "them")
     CallSubDelayed3(ChatShared, "NewMessage", ABMPageId, bubble)
     CallSubDelayed3(ChatShared, "IsTyping", ABMPageId, "")
     If value = "" Then Return
     If ChatShared.AvoidDuplicates.ContainsKey(value.ToLowerCase) Then
       Dim chat As ABMChat = page.Component("conversation")
       chat.AddBubble("Server", "Sorry, but " & value & " is already taken!", "The Server", "server", "server")
       ChatName = value
       ChatShared.AvoidDuplicates.Put(value.ToLowerCase, value)
       Dim chat As ABMChat = page.Component("conversation")
       chat.AddBubble("Server", "Welcome to ABMChat " & ChatName & "!", "The Server", "server", "server")
       CallSubDelayed3(ChatShared, "NewConnection", Me, ABMPageId)
     End If
   End If

   Dim ChatInput As ABMInput = page.Component("ChatInput")
   ChatInput.Text = ""
End Sub

public Sub NewMessage(bubble As ABMChatBubble)
   Dim chat As ABMChat = page.Component("conversation")
   chat.ScrollToBottom   ' scroll to the end of the chat
End Sub

public Sub IsTyping(pageID As String, typeBool As Boolean)
   Dim chat As ABMChat = page.Component("conversation")
   If pageID <> ABMPageId Then
     If typeBool = False Then
       chat.ThemAreTyping(False, "") 'hide the 'is typing' bubble
       chat.ThemAreTyping(True, "") ' show the 'is typing' bubble if not yet shown
     End If
     chat.ScrollToBottom   ' scroll to the end of the chat
   End If
End Sub

public Sub CurrentConversation(LastMessages As List)
   If LastMessages.Size > 0 Then
     Dim chat As ABMChat = page.Component("conversation")
     chat.ScrollToBottom   ' scroll to the end of the chat
   End If
End Sub

So, there you have it. A complete modern looking multichat webapp that runs on all platforms in just about 150 lines of code!


Click here to Donation and support ABMaterial

B4J: ABMaterial 2.20 with ABMGridBuilder


Responsive design is great, but at the same time something that is not so easy to grasp as it works over different levels.  ABMaterial has been using this concept from the very beginning using Materialize CSS and to make it easier, I created a builder for it.

A great start was the implementation done for Bootstrap: Shoelace.io.  I liked the idea of ‘drawing’ your grid.  However, for ABMaterial, I did want more.


  1. Needed to generate B4J source code, not html
  2. I wanted a way to return B4J code back to the builder
  3. Some extra functionalities to insert cells before/after another
  4. Save/Load to re-use designs.
  5. The generated code should include a ‘description’ of the grid.
  6. A ‘light’ and a ‘dark’ theme for night owls like me

Here is a little demonstration on how to work with it:

It generates code something like this:

'║ 1,1                                                                               ║
'║ 2,1                       | 2,2                       | 2,3                       ║
'║ 2,4                       | 2,5                       | 2,6                       ║
'║ 2,7                       | 2,8                       | 2,9                       ║
'║ 2,10                      | 2,11                      | 2,12                      ║
'║ 3,1                                                                               ║
'║ 4,1                                                                               ║

'║ 1,1                                                                                                       ║
'║ 2,1                      | 2,2                      | 2,3                      | 2,4                      ║
'║ 2,5                      | 2,6                      | 2,7                      | 2,8                      ║
'║ 2,9                      | 2,10                     | 2,11                     | 2,12                     ║
'║ 3,1                                                                                                       ║
'║ 4,1                                                                                                       ║

'║ 1,1                                                                                                                               ║
'║ 2,1      | 2,2      | 2,3      | 2,4      | 2,5      | 2,6      | 2,7      | 2,8      | 2,9      | 2,10     | 2,11     | 2,12     ║
'║ 3,1                                                                                                                               ║
'║ 4,1                                                                                                                               ║

cont1.AddRows(1, True,"").AddCells12(1,"")
cont1.AddRows(1, True,"").AddCellsOS(12,0,0,0,4,3,1,"")
cont1.AddRows(2, True,"").AddCells12(1,"")
cont1.BuildGrid ' IMPORTANT!

This description of the grid is very handy when you start adding your components. As you can see in the video, copying the AddRows lines and parsing them back into the builder makes it easy to make changes.

The functionalities are all in the grid itself so you can work fast:


ABMGridBuilder is completely written in B4J, so it runs on all desktop platforms (Windows, OSX, Linux).

Tools like this make creating WebApps with ABMaterial and B4J even easier.  It is astonishing how versatile the B4X programming suite from Anywhere Software really is.  It is unique, fast, super stable and full of features nowhere else to be found. I’m so glad I discovered this great tool!

Donators will receive their mail with the download link shortly.

See ya!


Click here to Donation and support ABMaterial


B4J: ABMaterial building a QR Code component


On the B4J forum, liulifeng77 asked the question how he could draw a QR Code in an ABMaterial web page.

I had 5 minutes to spare, so I created the QRCode component using ABMCustomComponent. It is using this javascript library: https://github.com/davidshimjs/qrcodejs

The component class CompQRCode:

'Class module
Sub Class_Globals
     Public ABMComp As ABMCustomComponent
     Public myText As String
   Public myWidth As Int
   Public myHeight As Int
   Public myColorDark As String
   Public myColorLight As String
   Public myCorrectLevel 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, text As String, width As Int, height As Int, colorDark As String, colorLight As String, correctLevel As String)
     ABMComp.Initialize("ABMComp", Me, InternalPage, ID)
     myText = text
   myWidth = width
   myHeight = height
   myColorDark = colorDark
   myColorLight = colorLight
   myCorrectLevel = correctLevel
End Sub
Sub ABMComp_Build(internalID As String) As String
  Return $"
<div id="${internalID}svg"/></div>
<script>var qrcode${internalID};</script>"$
End Sub
' Is useful to run some initalisation script.
Sub ABMComp_FirstRun(InternalPage As ABMPage, internalID As String)
   Dim script As String = $"qrcode${internalID} = new QRCode(document.getElementById("${internalID}svg"), {
  text: "${myText}",
  width: ${myWidth},
  height: ${myHeight},
  colorDark : "${myColorDark}",
  colorLight : "${myColorLight}",
  correctLevel : ${myCorrectLevel}
     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 = $"qrcode${internalID}.clear();
     InternalPage.ws.Eval(script, Array As Object(ABMComp.ID))
End Sub
' do the stuff needed when the object is removed
Sub ABMComp_CleanUp(InternalPage As ABMPage, internalID As String)
End Sub

Usage (if you want to change its text at run time, put the declaration in Class_globals):

Dim QRCode As CompQRCode

In BuildPage() load the javascript library:


In ConnectPage():

QRCode.Initialize(page, "qrcode", "Alwaysbusy's ABMaterial", 128,128, "#000000", "transparent", "QRCode.CorrectLevel.H")

Et voila, another new component!


Click here to Donation and support ABMaterial

B4J: ABMaterial Public 1.22/Donators 2.00 now released

It has been a long night to get everything ready, but I’m happy to release the new version(s) of ABMaterial for B4J!

2.00 is a major release.  Lots of new stuff and some changes that were really needed in preparation of the upcoming (ABMaterial Abstract Desinger) ABMAD tool.

Highlights of the releases:

1.22: New component ABMPatternLock

ABMPatternLock is yet another authentication method you can use based on the popular Pattern Lock you see in e.g. Android.


1.22: ABMContainer can collapse

ABMContainer can be initialized as a collapsable component.  As a collapsable has a header and a body and clicking on the header will collapse/expand the body.  This can be used as an alternative for an ABMCard so you can build your own.

1.22: Support for Font-Awesome icons

If you set: page.UseFontAwesome = true then you can, next to the material icons, also use the Font Awesome Icon library (634 icons in used version Font Awesome 4.6.3)
See fontawesome.io for a list of icons.


2.00: New component ABMChronologylist

The ABMChronologyList is a vertical timeline component. Useful to give an overview of a limited period. It is device aware so e.g. on a phone, all items will be one under each other.


2.00: New helper ABMSideBar

In ABMaterial 2.00, you will be able to define multiple side bars (sliding in from the right). I’ve tried to mimic the usage of ABMSideBar as much of the existing Side Navigation menu. The tricky part was making it work well with the NavigationBar, on all screensizes.

Check out the online demo abmaterial.com or download your free copy from the B4J website.  Make sure you read the included README.TXT files.  It contains some important info and shows some tips and tricks.

That’s it for now.  Next big release will be all about ABMAD. Until next time!


Click here to Donation if you like my work

B4J: Future steps for ABMaterial


As ABMaterial has grown to a production ready framework for WebApps using B4J, I recently started a poll on the B4X forum to find out what could be the next steps in its evolution.

I came up with 3 things to do. They would require about the same develop time, but it could be useful to see what the actual users of ABMaterial would like me to work on next.

The possible choices were:

1. ABMXPlay
This is the component you have seen some demos of and will be more for game developers. It still needs a lot of attention before it can be released.

2. B4JS
The Basic to Javascript transpiler is now in its very early stages. It is a tough one to write without finishing ABMXplay first, as the ABMXPlay component would be the first to really benefit from it. The B4J core functions have been covered, but big parts of all ABMComponents need to be refactored to start using B4JS.

3. ABMaterialAbstractDesigner
A full blown Abstract Visual Designer like the one in B4J. This is NOT a code generator but the real deal. Code generators may look nice at first sight, but  have a huge disadvantage: they go one way. ABMAbstractDesigner would work like the B4X ones: you create a layout, develop, change the layout, continue to develop and so on without losing what you have written in code. I’ll use the grid designer I’ve shown as a proof of concept in another post + the possibility to put ABMComponents on them. I’ve done some tests this weekend and it would work very similar as the other B4X tools.

4. Other (please specify)

The poll is now nearly ended and besides some interesting messages with excellent pointers, it was quite obvious from the beginning that the ABMaterialAbstractDesigner (will need a working title for this project, lets call it ABMAD for now…) would be the next part I’ll be working on. (Almost 75% in the poll at the time of writing).  This was also my preferred choice as one user did not really have the patience to wait for me and started to write some kind of code generator.  However, his limited knowledge of the ABMaterial framework has the potential of starting to reflect bad on ABMaterial itself so it is time for me to jump in.

Making ABMAD will happen in a couple of logical steps:

  1. Making the ABMaterial components ready for a designer.  This will ask for some (minor) changes to them so they can be serialized and expose their properties to the designer.
  2. Making a grid builder.  This is the biggest step new users have to overcome nowadays.
  3. Making a properties view for the components.
  4. Drag/Drop facilities to add components to specific cells
  5. Let the ABMaterial library communicate with the Designer output in a similar way as B4X does now.

This will not happen overnight, but the benefits for ABMaterial programmers will be substantial.  This will NOT be a simple code generator. ABMAD will work bidirectional.  And you’ll be able to make layouts in ABMAD and mix them with manually written code like you can do in B4X/ABMaterial now.

Note: To avoid future misunderstandings, the license of ABMaterial 2.0 will include the following statement:


Let me explain. The philosophy of ABMaterial (and myself) is giving anyone the chance to easily build great WebApps without having to spend any money. One of the reasons I picked B4J was because it is free. Those who can spare something, donate to stimulate my continuation on the project and in so help giving other not so fortunate people an equal chance to grow. Any derived development tool or generator that uses ABMaterial must follow this philosophy so the tool must be available with 100% of its features for free (not even a ‘limited’ free version is acceptable).

This has no impact on whatever WebApp you make with ABMaterial, but is solely a protection against some ‘money-grubber’ who sees the potential of the ABMaterial framework and thinks of making some easy money using my engine by simply writing a layer above it. Just to be clear, ‘FEE’ here means ANY form of payment or compensation, donations included.

So now that this is out of the way, I think it’s time to step up to the drawing board and start to make some decisions on how ABMAD will look like.

Fun times ahead!


Click here to Donation and support ABMaterial








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: 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