Category Archives: Android

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!



B4i (iOS): Yup, you read that right!

(Not official logo)

(Not official logo)

Amazing news from Anywhere software: Erel is working on B4i! This development suite for cross development (Android/Mac/Windows/Linux) will be joined by iOS.

After the great success of Basic4Android (simply the best development tool for Android, by far), Erel suprised us all with Basic4Java last year. A free! tool to create cross platform software for Mac, Windows and Linux.

And now, he has an ever bigger suprise: Basic4iOS!
Just like it’s brother B4A, B4i will compile to native code (Objective C in this case). Combined with the powerful IDE (Xojo, check this one out, it’s bug free and blazing fast) with its unique debugging capabilities and features, this is a certain winner, again!

As a Windows developer, you won’t even need a Mac to compile:

…The current plan is to host a cloud of Macs for the compilation which means that developers will not need to have access to a Mac computer. There will also be an option to host the remote Mac compiler locally instead of the cloud compilers…

And the clever debugging tricks well known from B4A will also be at your fingertips:

…The rapid debugger will allow modifying the code without rebuilding the package making the “testing cycle” much quicker…

It’s simply amazing how Anywhere Software can build such powerful (and most of all stable and reliable) development tools in such a short time span. And at a price that dwarfs the competition!

Well done Erel! We are looking forward for this little beauty by the end of this year. It’s gonna be great, I’m sure of that!


QuiQRun! launched

Barcelona Skin

Barcelona Skin

QuiQRun!, the social, fast barcode scanner and creator!

I’m very happy to release the first version of QuiQRun! for Android! In the next weeks I may do a couple of articles on the blog on how some things are done. (Basic4Android and Java).

I want to thank everyone that did test and translate the app!

Download free version

Please rate QuiQRun! if you like it!

More information and screenshots can be found at


QuiQRun! Localization

QuiQRun! Skin Italia

Skinning QuiQRun!

Hi all,

I’m looking for people who can translate about 150 terms and some short sentences for my new app QuiQRun! If you can help me, send me a mail to

I already have English, Dutch, Italian, French, German, Spanish and Portuguese.

What can I offer?

1. The full version of QuiQRun!
2. A skin in QuiQRun! with your favorite colors
3. Full credit in the apps credits screen
4. Eternal gratitude!

How do I use it in B4A?
The translations are stored in a language file, eg. en-us.lng.

They are stored in a predefined format:

{0003};Setting new skin, one moment...
{0004};Tap to get more info!
{0005};This QRCode is expired!

In Basic4Android, It checks if it has a translation, if not it uses the default you put in the GetString function. Here is the snippet I use:

Sub Process_Globals
    Public LangStrings As Map
    Public BOM As Int = 65279 ' Byte Order Mark for Windows UTF-8 files
End Sub

Sub Activity_Create(FirstTime As Boolean)
    Dim R As Reflector
    R.Target = R.RunStaticMethod("java.util.Locale", "getDefault", Null, Null)
    Dim tmpLang As String = R.RunMethod("getLanguage")
    Select Case tmpLang
        Case "nl"
        Case "fr"
        Case "it"
        Case "es"
        Case "de"
        Case Else
    End Select

    ' using it:
    ToastMessageShow(GetString("{0003}","Setting new skin, one moment..."), true)
End Sub

Public Sub LoadLanguageFile(setLanguageFile as String)
    If File.Exists(File.DirAssets, setLanguageFile) Then
        Dim txtR As TextReader
        txtR.Initialize(File.OpenInput(File.DirAssets, setLanguageFile))
        Dim line As String
        line = txtR.ReadLine
        Do While line  Null
            line = line.Replace(Chr(BOM), "")
            LangStrings.Put(line.SubString2(0,6), line.SubString(7))
            line = txtR.ReadLine
    End If   
End Sub

Public Sub GetString(iCode As String, iDefault As String) As String
    If LangStrings.ContainsKey(iCode) Then
        Return LangStrings.Get(iCode)
        Return iDefault
    End If
End Sub



QuiQRun! A glimpse…

I’m currently working on my new Android app called QuiQRun and this is the first time it is presented to the public. It is currently in the testing phase so it won’t be long until I will release it to Google Play.

What is QuiQRun?

QuiQRun consists of two main parts:

1. The White side: Save – Share – Scan QR Codes

• Scan QR Codes and barcodes with our fast build in scanner
• Decrypt codes created with QuiQRun Security and read codes generated by other apps
• A human readable summary and the raw QR data
• Save codes for later use. All codes are organized in a nice list for easy retrieval.
• Share your QR Code on Facebook, Twitter, Email, Dropbox and many more…
• Use the QR Code immediately on your smartphone

2. The Black side: Make – Share – Protect QR Codes

• Make QR Codes for Websites, Wifi, Contacts, locations, coupons and many more…
• Protect your QR Code with QuiQRun Security
– Encryption
– Prevent saving the QR Code
– Password or PIN protect
– Set an expiration date
• Share your QR code on Facebook, Twitter, Email, Dropbox and many more…
• Use the QR Code immediately on your smartphone

And here are the first screenshots!


QuiQrun on Android

QuiQRun is written in Basic4Android. If you are a licensed user of B4A you can download an exclusieve preview version from the B4A ‘Test my App!’ forum! I would definitely appreciate the feedback 🙂

More information and screenshots can be found at QuiQRun

The coming weeks I’ll keep you up to date. Follow me on Twitter: QuiQRun


Basic4Android v3.20 is released!

B4A Anchors

B4A Anchors

Again, another great update of B4A! Erel has continued to improve the visual designer, this time with a great new feature you may know from typical PC languages: Anchors. (Xojo/Realbasic users may recognize them as the LockLeft, LockTop, LockRight and LockBottom properties)

Anchors allow you to define a constant distance between the view and one or more of the parent bounds. By using anchors, you can design screens and B4A will ‘stretch’ them according to the device size. This is different than scalling because it takes more relative positions into account.

Here is a short video demonstrating the anchore feature:

Important designer improvements:
* Copy & paste – work both inside the layout or between different layouts.
* Undo / redo feature.
* The views are organized in a tree for easier navigation.
* AutoScaleAll keyword now works with all variants, not just the “standard” variant.
* Colors fields can be copied and the colors values can be pasted or directly typed.
* Designer script find / replace dialog.
* The grid is saved in the layout file.
* Landscape / Portrait designer keywords to test the current orientation.
* UI Cloud threshold reduced to 10 seconds.

Other great new features and improvements:
* #AdditionalRes attribute
* Sync button in the Files tab – Syncs the project files with the Files folder.
* Modules added to the Find Sub / Module tool (Ctrl + E).
* Tabs order in the IDE is preserved.
* Modules files that were not modified will not be saved thus preserving the correct time stamp.
* DateTime.SetTimeZone now accepts a Double instead of Int.
* Shortcuts: F11 – Restart (rapid debugger), F2 in the designer connects to the device.
* JavaObject v1.00 – new RunMethodJO / GetFieldJO methods.
* Bug fixes and other minor improvements.

So head to the B4A website to check it out and if you don’t have a license yet (hard to believe because it is worth every penny), now is a great time to get one!


Basic4Android: A first look at ABPlay, a game engine library

ABPlay Screenshot

ABPlay Screenshot

It has been a while since I’ve written a new article on this blog because it have been busy months both at work and in my personal life. One of the things I wanted to do was writing an easy to use Game Engine for B4A. I have written the ABPhysics engine in the past and recently Informatix pointed out I had started another engine (ABGameEngine) before that. Development on ABGameEngine was stopped early for several reasons: time was one of them, but also because several other developers were working on an engine themselves. It seemed a little bit pointless to continue.

However, the other engines were not further developed either. Until recently Erel came up with the GameView. This is an excellent View that will cover the needs of a lot of beginning programmers. I definitely would like to urge starting game developers to take a look at this. Registered users of B4A can download the GameView lib from here.

But still, I wanted something more. I looked at ABgameEngine again, as it had some great ideas like layers, animated sprites, gamepads etc. But it was also very old code and not written very well. And it crashed all the time 🙂

I decided to restart from scratch a couple of weeks ago. Some weekends and evenings later ABPlay was born! And with a lot of goodies!

Here is a small demo video of what I got so far. it demonstrates the following:

1. Layers
     (Layer 1) the moving background with the Odies as animated sprites
     (Layer 2) the black foreground layer with a weird dancing creature that passes now and then
2. Animated sprites, not bound to a layer
     (our hero Garfield is back!)
3. A sprite can have different animations.
     (like one for standing, one for walking, one for fighting, etc)
4. A sprite can have different predefined 'Walks'
     (A walk can be build like you would build a path. It's a sequence of lines, bezier curves, wait periods etc. It's like a simple flash movie)
5. Gamepad controls
     (the 'joystick' pad on the left)
     (the 'action' pad on the right)
     (the 'direction' pad as an alternative to the joystick, not shown in this video)
6. In the demo I cannot demonstrate it with a mouse, but it is completely multi touch
     (You can control Garfield AND press the Action button X AND do a swipe anywhere at the same time)

But let’s have a look (beware this is running on the Emulator. On a real device it is much smoother):

I’m actually very pleased with the result. The graphics and handling is very smooth.
Above all, it’s still very easy to program. Here is the whole code for the demo app:

#Region  Project Attributes
	#ApplicationLabel: ABPlayTest
	#VersionCode: 1
	'SupportedOrientations possible values: unspecified, landscape or portrait.
	#SupportedOrientations: landscape
	#CanInstallToExternalStorage: False
#End Region

#Region  Activity Attributes
	#FullScreen: true
	#IncludeTitle: false
#End Region

Sub Process_Globals

End Sub

Sub Globals
	Dim myPlay As ABPlay
	' the panel that will hold the ABPlay
	Dim myPanel As Panel

	' a background and foregroud layer
	Dim bgLayer As ABLayer
	Dim fgLayer As ABLayer

	' some colors to show the multi touch points in the demo
	Dim myColors() As Int = Array As Int(Colors.Red, Colors.Green, Colors.Blue, Colors.Cyan, Colors.Yellow, Colors.Gray, Colors.White, Colors.Magenta, Colors.LightGray, Colors.DarkGray)

	' our hero
	Dim Hero As ABSprite
	' our enemies
	Dim Enemies As List

	' param to set the set the speed
	Dim Speed As Float = 0.2

	' some variables to hold the current state of the hero
	Dim currentAction As String
	Dim currentDirection As String
	Dim currentIsStanding As Boolean
End Sub

Sub Activity_Create(FirstTime As Boolean)
	' initialize ABPlay with myPanel
	Activity.AddView(myPanel, 0,0,100%x,100%y)
	myPlay.Initialize(myPanel, "myPlay")

	'////////////// BEGIN loading sprite sequences
	' Load Sprite Sequences
	myPlay.LoadSpriteSequence("GarStandingLeft",LoadBitmap(File.DirAssets, "garleftstill.png"),6, 1, 1000)
	myPlay.LoadSpriteSequence("GarStandingLeft",LoadBitmap(File.DirAssets, "garleftstill.png"),6, 1, 1000)
	myPlay.LoadSpriteSequence("GarStandingRight",LoadBitmap(File.DirAssets, "garrightstill.png"),6, 1, 1000)
	myPlay.LoadSpriteSequence("GarWalkingLeft",LoadBitmap(File.DirAssets, "garleft.png"),8, 1, 1000)
	myPlay.LoadSpriteSequence("GarWalkingRight",LoadBitmap(File.DirAssets, "garright.png"),8, 1, 1000)
	myPlay.LoadSpriteSequence("GarStandingLeftFight",LoadBitmap(File.DirAssets, "garleftstillfight.png"),2, 1, 250)
	myPlay.LoadSpriteSequence("GarStandingRightFight",LoadBitmap(File.DirAssets, "garrightstillfight.png"),2, 1, 250)
	myPlay.LoadSpriteSequence("GarWalkingLeftFight",LoadBitmap(File.DirAssets, "garleftfight.png"),7, 1, 1000)
	myPlay.LoadSpriteSequence("GarWalkingRightFight",LoadBitmap(File.DirAssets, "garrightfight.png"),7, 1, 1000)
	' and the ones for Odie
	myPlay.LoadSpriteSequence("OdieStandingLeft", LoadBitmap(File.DirAssets, "odieleftstill.png"),7,1,Rnd(800,1200))
	myPlay.LoadSpriteSequence("OdieStandingRight", LoadBitmap(File.DirAssets, "odierightstill.png"),7,1,Rnd(800,1200))
	myPlay.LoadSpriteSequence("OdieWalkingLeft", LoadBitmap(File.DirAssets, "odieleft.png"),5,1,Rnd(800,1200))
	myPlay.LoadSpriteSequence("OdieWalkingRight", LoadBitmap(File.DirAssets, "odieright.png"),5,1,Rnd(800,1200))
	' and the one for the creature
	myPlay.LoadSpriteSequence("CreaturePassToRight", LoadBitmap(File.DirAssets, "creature.png"), 3, 4, 1200)
	'////////////// END loading sprite sequences

	'////////////// BEGIN building the Hero Garfield
	' initialize the hero
	Hero.Initialize("Hero", 50%x, 50%y)
	' add sprite sequences
	Hero.StartSpriteSequence("GarStandingLeft", True)
	'//////////////  END building the Hero Garfield

	'////////////// BEGIN building the background layer with Odies
	' initalize a background layer
	bgLayer.Initialize("background", 0,0)
	bgLayer.SetBackground(LoadBitmap(File.DirAssets, "bga.jpg"), 1.0)

	' initialize some enemies
	Dim a As Int
	For a = 1 To 10
		Dim Odie As ABSprite

		' initialize an Odie with some animation sequences
		Odie.Initialize("Odie" & a, Rnd(10%x,90%x), Rnd(10%y, 90%y))
		' add multiple sprite sequences
		Odie.AddSpriteSequences(Array As String("OdieStandingLeft","OdieStandingRight","OdieWalkingLeft","OdieWalkingRight"))

		' create a random walk to the right
		Dim WalkRight As ABSpriteWalk
		WalkRight.Initialize("ToTheRight", True)
		Dim newX As Int = Odie.x+Rnd(20%x, 80%x)
		WalkRight.AddLine("OdieWalkingRight",Odie.x, Odie.y, newX, Odie.y, Rnd(90,100))
		WalkRight.AddWait("OdieStandingRight",newX, Odie.y, Rnd(10,50))
		WalkRight.AddLine("OdieWalkingLeft",newX, Odie.y, Odie.x, Odie.y, Rnd(90,100))
		WalkRight.AddWait("OdieStandingLeft",Odie.x, Odie.y, Rnd(10,50))

		' create a random walk to the left
		Dim WalkLeft As ABSpriteWalk
		WalkLeft.Initialize("ToTheLeft", True)
		Dim newX As Int = Odie.x-Rnd(20%x, 80%x)
		WalkLeft.AddLine("OdieWalkingLeft",Odie.x, Odie.y, newX, Odie.y , Rnd(90,100))
		WalkLeft.AddWait("OdieStandingLeft",newX, Odie.y, Rnd(10,50))
		WalkLeft.AddLine("OdieWalkingRight",newX, Odie.y, Odie.x, Odie.y, Rnd(90,100))
		WalkLeft.AddWait("OdieStandingRight",Odie.x, Odie.y, Rnd(10,50))

		' pick random a walk, left or right
		Dim GoLeft As Int = Rnd(0,2)
		If GoLeft = 0 Then
		End If
	'////////////// END building the background layer with Odies

	'////////////// BEGIN building the foreground layer with creature
	' initalize a foreground layer
	Dim fgLayer As ABLayer
	fgLayer.Initialize("foreground", 0,0)
	fgLayer.SetBackground(LoadBitmap(File.DirAssets, "fga.png"), 100%y/400)

	' and a weird creature...
	Dim creature As ABSprite
	creature.Initialize("creature", -100%x, 100%y-240)
	' with a walk
	Dim pass As ABSpriteWalk
	pass.Initialize("DanseToTheRight", True)
	pass.AddLine("DansingRight",creature.x, creature.y, 200%x, creature.y, 300)
	' and start the walk
	' add the creature to the foreground layer
	'////////////// END building the foreground layer with creature

	'////////////// BEGIN Add game pads
	' initialize and start a Joystick Gamepad
	myPlay.InitializeJoystickPad(18, 100%y-210, 192, 192 , LoadBitmap(File.DirAssets, "joystick_bg.png"),LoadBitmap(File.DirAssets, "joystick.png"))

	' initialize and start a Action Gamepad
	myPlay.InitializeActionPad(100%x-210, 100%y-210, 192, 192 , LoadBitmap(File.DirAssets, "action_active.png"),LoadBitmap(File.DirAssets, "action_inactive.png"),LoadBitmap(File.DirAssets, "action_mask.png"),True, False, True, False)
	'////////////// END Add game pads
End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)
End Sub

Sub Activity_KeyPress (KeyCode As Int) As Boolean 'Return True to consume the event
	Select Case KeyCode
		Case KeyCodes.KEYCODE_BACK
			' NEEDED: stop the drawing thread and recycle stuff
			Return True
		Case KeyCodes.KEYCODE_HOME
			Return True
	End Select
End Sub

Sub myPlay_Draw(c As Canvas, State As ABState)
	' the actual drawing, do NOT set a debug stop in here!

	'////////////// BEGIN Calculation stuff
	' calculate hero stuff depending on what buttons we pressed on the gamepad
	currentAction = ""
	If State.UsingActionPad Then
		' go into fight state
		If State.ACTION_X Then
			currentAction = "X"
		End If
		' go back to center
		If State.ACTION_Y Then
		End If
	End If

	Dim XMovement As Int
	Dim YMovement As Int
	If State.UsingJoystickPad Then
		XMovement = State.JOYSTICK_X*Speed
		YMovement = State.JOYSTICK_Y*Speed
		' update the hero's position and direction
		If XMovement<0 Then
			currentDirection = "LEFT"
			currentDirection = "RIGHT"
		End If
		Hero.SetPostition(Hero.x+XMovement, Hero.y+YMovement)
		currentIsStanding = False
		currentIsStanding = True
	End If

	' set the animation type
	Select Case currentDirection
		Case "LEFT"
			Select Case currentAction
				Case "X"
					If currentIsStanding Then
						Hero.StartSpriteSequence("GarStandingLeftFight", False)
						Hero.StartSpriteSequence("GarWalkingLeftFight", False)
					End If
				Case Else
					If currentIsStanding Then
						Hero.StartSpriteSequence("GarStandingLeft", False)
						Hero.StartSpriteSequence("GarWalkingLeft", False)
					End If
			End Select
		Case "RIGHT"
			Select Case currentAction
				Case "X"
					If currentIsStanding Then
						Hero.StartSpriteSequence("GarStandingRightFight", False)
						Hero.StartSpriteSequence("GarWalkingRightFight", False)
					End If
				Case Else
					If currentIsStanding Then
						Hero.StartSpriteSequence("GarStandingRight", False)
						Hero.StartSpriteSequence("GarWalkingRight", False)
					End If
			End Select
	End Select	

	' update the hero animation

	' move our backgrounds, does not make sense but shows the possibilities
	Dim newX, newY As Int
	Dim newMovementX, newMovementY As Int
	If currentIsStanding = False Then
		If currentDirection = "LEFT" Then
			newX = bgLayer.ViewX
			newY = bgLayer.ViewY
			If newX-newMovementX >= 0 Then
				newX = newX-newMovementX
			End If
			If newY-newMovementY >= 0 Then
				newY = newY-newMovementY
			End If
			bgLayer.SetLayerPostion(newX, newY)
			newX = bgLayer.ViewX
			newY = bgLayer.ViewY
			If newX+newMovementX <= bgLayer.OuterWidth - myPlay.Width Then
				newX = newX+newMovementX
			End If
			If newY+newMovementY <= bgLayer.OuterHeight - myPlay.Height Then
				newY = newY+newMovementY
			End If
			bgLayer.SetLayerPostion(newX, newY)
		End If
	End If

	'////////////// END Calculation stuff

	'////////////// BEGIN Drawing stuff
	'ok, all the calulations are done, let's draw!

	' draw the background layer with all its sprites on it and advance all sprite animations and walks
	myPlay.DrawLayer("background", c)

	Dim a As Int
	' draw the multitouch points that are not on the gamepad, no action here but just to show the possibilities
	For a = 0 To State.touchPoints.Size - 1
		Dim tmpP As ABTouchPoint
		tmpP = State.touchPoints.GetValueAt(a)
		c.DrawCircle(tmpP.X, tmpP.Y, 50dip, myColors(, True, 1dip)

	' draw the hero

	' draw the foreground layer with all its sprites on it and advance all sprite animations and walks
	myPlay.DrawLayer("foreground", c)
	'////////////// END Drawing stuff

	' NEEDED: very last line of the Draw event. Let ABPlay know it may process touches again!
End Sub

I’m not there yet, but it’s a good start. It needs a lot more testing and a lot of new features.

Until next time!

Click here to Donation if you like my work

%d bloggers like this: