Write once, run anywhere: An Android game using JavaFX and the GraalVM

Get it into the Google Play Store! Lately I was reasoning on how to provide Java applications as launchers, bundles or installers1 as well as native executables2 . Now I go for an Android game programmed using Java >= 16 and JavaFX which runs besides on Android smartphones also on GNU Linux as well as on MS Windows machines (and presumably others3) using a single code base. No Android Studio required4! See how to setup an according Maven project yourself! Would be great to get that game into the Google Play Store!

Using Java >= 16 and JavaFX alongside Maven indeed seems to be an unusual tech-stack for getting an Android game into the Google Play Store instead of going for Kotlin + Gradle, though I want to explore how far you can get with the “write once, run anywhere” promise of the Java language whilst reusing my code throughout all different kinds of platforms.5

A while ago I coded a toolkit for programming cellular automatons and visualizing them using JavaFX6. While doing so, I realized that the game Boulder Dash alongside its amoebas, butterflies, diamonds, fireflies, magic walls and steel walls might as well have been implemented by the principle of a cellular automaton. My reasoning resulted in the DingDash POC proving that in fact it is possible to implement Boulder Dash with the means of a cellular automaton.

The name “DingDash” is a neologism of the terms “Dingbat”, i.e. symbols that would be appropriate for fitting into the uniform cells of the POC’s engine, and “Boulder Dash”, a game having inspired the POC’s mechanics.

The DingDash POC is implemented using Java>= 16 and JavaFX, so it runs on MS Windows as well as on GNU Linux (and presumably also on Apple Macs as well) directly without any mobile phone emulator.

After having gathered all the resources and maps of the original game, my next action was to port that POC to Android. Actually it is not a port in terms of a modified and different code base, moreover the same code base now also serves as input for building an *.apk Android package! The goal here is to create an *.apk Android package which can be distributed by the Google Play Store.

Write once, run anywhere: A single code base executes on MS Windows and GNU Linux (x86-64 and ARM) as well as on Android (and presumably on Apple Macs and on Apple iPhones, too3)!

Fellow companions […]

Getting DingDash into the Google Play Store would be great, be it just to accompany the entire process of developing and publishing an Android app! If you are interested in being part of the DingDash project and would like to participate getting it into the Google Play Store, please let me know.7

Before diving into the details on how to setup such a multi platform project, lets look at the DingDash POC. As DingDash is a POC, some bits and pieces are still missing and some efforts yet got to be invested to get it into the Google Play Store, though the feasibility of the undertaking has been proven.

Nevertheless, this looks quite promising, doesn’t it? For the undertaking not to disappear into oblivion, I’m looking for fellow companions! My favorite plan would be to use the DingDash cellular automaton engine and create a game not being a Boulder Dash clone, though being inspired by the Boulder Dash mechanics: Same engine, different game. Any help and participation would be very much appreciated!

If you are interested in participating to get the game into the Google Play Store, some issues are on the wish list which I hardly can accomplish all by myself, which I will outline below:

You may skip this section and continue with the Toolchain section if you want to directly jump into your own multi platform project! For ease of reading just the tutorial, you may collapse the Fellow companions heading above.

Same engine, different game?

Not only as of the unclear status of Boulder Dash actually being abandonware or not and the risk of copyright issues, I am also very keen with the idea of using the cellular automaton engine to tinker with DingDash like giving it another look and differing mechanics.

To not overcomplicate things, an idea would be to use common semigraphics dingbats for the game’s elements: The idea of combining dingbats alike characters with a Boulder Dash alike cellular automaton engine has actually inspired me to call the POC DingDash. There are many (long forgotten) semigraphics fonts out there which would be worth looking at …

04b_21 Font […]
Atari 8-Bit Font […]
C64 Font […]
Unifont Font […]
Unicode pseudographics characters […]

Give it some colors, give it some animations, this could result in a reduced but still charming look & feel!

Yet another Boulder Dash clone?

For the POC, I pulled together all the Boulder Dash pixel graphics and cave maps, making a Boulder Dash clone feasible. A plan could be to implement all the missing bits and pieces such as a level menu, a high score list and some missing logic for the amoebas, butterflies, diamonds, fireflies, magic walls and steel walls as well as adding sound.

Boulder Dash pixmaps […]
Caves 1 and 2 […]
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
W...... ..d.r .....r.r....... ....r....W
W.rXr...... .........rd..r.... ..... ..W
W.......... ..r.....r.r..r........r....W
Wr.rr.........r......r..r....r...r.....W
Wr. r......... r..r........r......r.rr.W
W... ..r........r.....r. r........r.rr.W
Wwwwwwwwwwwwwwwwwwwwwwwwwwwwwww...r..r.W
W. ...r..d. ..r.r..........d.rd...... .W
W..d.....r..... ........rr r..r....r...W
W...r..r.r..............r .r..r........W
W.r.....r........rrr.......r.. .d....r.W
W.d.. ..r.  .....r.rd..d....r...r..d. .W
W. r..............r r..r........d.....rW
W........wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwW
W r.........r...d....r.....r...r.......W
W r......... r..r........r......r.rr..PW
W. ..r........r.....r.  ....d...r.rr...W
W....rd..r........r......r.rd......r...W
W... ..r. ..r.rr.........r.rd...... ..rW
W.d.... ..... ......... .r..r....r...r.W
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
W.r..r..w.r...d.w... .r.wr......w..rr..W
W.......w......rwrr. ...w ..d...w....r.W
W                                      W
Wd......w.r....rw.r. .. w..r..d.w..r.r.W
W.......w.r....rw.r. r..w.....r.w... ..W
Wwwwwwwwwwwwwwwwwwww wwwwwwwwwwwwwwwwwwW
W....rr.w..r....w... ..rw....r..w.....rW
W.......w.. ....w... ...w....r. w.....rW
W                                      W
Wr..r...w....r..w..r ...w......dwr.....W
Wr....r.w..r..r.w... . rw.......wr...r.W
W.r.....w...r...w... . rw.......w r..r.W
Wwwwwwwwwwwwwwwwwwww wwwwwwwwwwwwwwwwwwW
Wr.  q..w....r.rw... ...w.rd..r.w......W
W.....r.wr......w..d ...w ..r...w.r.rr.W
W                                      W
Wd.. .r.wr....r.w.r. ..rw.r.r...w......W
W.....r.wr..d...w... r..w..r....w...rr W
W.d... rw..r....w.Xd r..w. .....w...rr W
W.r.... w.. ..r.w.P. ...w....r.rw.... .W
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
Caves 3 till 20 […]
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
Wr.ww.wrr.w...rwr..r....w...r.....rw.d.W
W..Xw.d.r.w...www..w.r....r..r.r...w.wrW
W....w..rd..r....w.....r.wwr.......w.wwW
Wd.w..wrwr..r....w...r......r.rr......wW
Wr.w...w..r.ww..r.wwd.......r.rr......wW
Wrr..r....w...r......r.rr......r..dww..W
W..r.ww..r.rr...w....r.rr......w..r.w.rW
W..w...d......d.r..wwr..r.w.wr..wr..d.rW
Wr.r....w.ww..d.r..wwr..r..d.w...w..r.wW
W.r.ww.....rrwr..d.w.wr..wr...wr..d.r..W
Ww.ww......rrwr..r.w.ww...w..r.ww..r.wwW
W.w.r.r.w...wwr..r....w...r.....ww.r.wwW
W.w.r.r.w.d.w.wr..wr....r..r.rr....w...W
Ww..wrwr..r....w...d...w.rw......w.ww.dW
Ww...wwr..w.d...wr..r.r...r.wr......w..W
Ww.d....r.ww..r.wwr.......r.wr......w..W
W..r....w...r......r.rr......w..r.w...wW
Wr.ww..r.ww...w....r.rr......w..rd..r..W
Ww...r......r.rd......r...ww..wr..d.w..W
Wrr...w.....r.rd......w..r.wd.d.rw.r...W
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WX.....r....................r........r.W
W.....r..............r.................W
W........r..r..........................W
Wr.....................................W
W...................r..................W
W.r.....................r.........r....W
W..r.....r...........r..r.............rW
W......r......r.....................r..W
W.......  B ..r.  B ....  B ....  B ...W
W.......    ..r.    ....    ....    r..W
W......................................W
W...r..............................r...W
W...r.....r............................W
W......r...........r..................rW
W...........r.......r..................W
W..r..............r....................W
W.....................r.........r......W
W................................r..r..W
W....r......r.rr..................r....W
W...........r.rr.........r..r.r.......PW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WX.....................................W
W......................................W
W......................................W
W......................................W
W......................................W
W......................................W
W......................................W
W.......  q.....  q.....  q.....  q....W
W.......   .....   .....   .....   ....W
W....... d ..... d ..... d ..... d ....W
W......................................W
W......................................W
W......................................W
W.......  q.....  q.....  q.....  q....W
W.......   .....   .....   .....   ....W
W....... d ..... d ..... d ..... d ....W
W......................................W
W......................................W
W......................................W
W......................................W
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
Wwwwwwwwww....r.r..r........r.wwwwwwwwwW
Ww        ...........r....r...        wW
Ww dq     ..r..........r...r..     qd wW
Wwwwwwwwww..r........r......r.wwwwwwwwwW
Ww        ......r...r.......r.        wW
Ww dq     ....r......r.rr.....     qd wW
Wwwwwwwwww.rr........r.rr.....wwwwwwwwwW
Ww        ....r.r....r..r.....        wW
Ww dq     ....r.r....r..r..r..     qd wW
Wwwwwwwwww.rr.r..r....r...r...wwwwwwwwwW
Ww        .rr.r..r............        wW
Ww dq     ....r..r........r...     qd wW
Wwwwwwwwww.....r...r....r..r..wwwwwwwwwW
W....r.r..r........r.....r............rW
W......r....r....r..r.r...r..r.........W
W..r....r.....r...r.......r..r.........W
W..r........r......r.rr.........r......W
Wr.X...r...........r.rr.........rr..r.PW
W....r......r.rr......r........r..r....W
Wrr.........r.rr.........r..r.r.r..r...W
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
W. .. .rr..... ..r. X.... rr r..r. .  .W
W ..r. .. .  .... .r.r. ...  r..r.d.. .W
Wr.....  .q.  ... .r.r. ... wwwwwwwwwwwW
W.r.d... .  ...... ..rr..r.... . ... . W
Wwwwwwwwwwwww.r. ..   r.. .... ...r....W
Wr. r...... ..r. ... ..r.  ..r.  q.....W
Wr. r...... .. r..r.... ...r......r.rr.W
W... ..r  ... ..r.  ..r.  ... ....r.rr.W
W... ..r. .r.... ...q......r.r..  r..r.W
W  .. r.... ..r.r.... .  .......  d.. .W
W. ... .. .  .. .  .....rr r..r. . r.. W
W.. d..r.r.... .  ......r  r..r. .  ...W
W.r.  ..r.  ... .r.r. ...  r.. .... ...W
W....  .r.  ... .r.r. .r. . r.. r.... .W
W.  .... ....  .. r r..r.... ...r... .rW
W..... .  .rr. ...  r.. .r... r..r.r...W
W r...... ..r. .r.... .  ..r.  r.......W
W r...... .. r..r.... ...r......r.rr...W
W. ..r. ... ..r.  .aa.  ... ....r.rr...W
W. .drq..r.... ...r......r.rq.....dr...W
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
W . r.. . .. ..r. ..X ..r.  ..r. r... .W
W.r.rr...... ..r...r.... ...r.....dr.r.W
W r..r...  ...r..r. ..r.r...wwwwwwwwwwwW
W...d ..r. q.....r..... ........rr r..rW
Wwwwwwwwwwwww..r.r.... .  ......r  r..rW
W.  ... ..r.  ..r.  .... rrr.....  r.. W
W... r... q.. ..r.  .....r.rr..r. . r..W
W..r. ..r. r.... ..... ...r r..r.... ..W
W.....r ...... .  qrr. ...  r.. .r....rW
Wr.r... . r...... ..r...r....r....dr.  W
W......r. r......... r..r...wwwwwwwwwwwW
W.rr...... ..r. ... ..r.  ..r.  ... r..W
Wwwwwwwwwwwwwr........ ...r......r.rr..W
W..r...  ...d..r. ..r.rr.........r.rr..W
W.. ..r. .r...mmmmmmmm.........  r..r..W
Wr.. r....r..r r...d .. .......  r..r..W
W ... ..r. ...r.  .....rrrr..r. . r.. rW
W. r..q.r.... .  ......rr r..r...  ...rW
Wr.  ..r.  .....r.r. ...  r..r.... ...rW
W...  .r.r .....r.r.....   .. .r....r..W
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
Wdddrrddrddr.rrrrdrdd.ddrddrddddrrdrdrrW
Wdrrdddrrrdrddrrrrrrdrrd.drdrrrrdrddrrdW
Wddrrrrrrrdrddrr.rrrdrrdddrdr.rrdrrrddrW
Wrrdrddrrrrrrdrrddd..ddrrdrddrrdrdd.rrdW
Wrrdrddrrrrrrdrrd.drdrrrrdrdrdrrddrrdrdW
Wdddrrdrd.ddrrddrrdddrrdrdrrr.drddrrdrdW
Wrrrrrdrrdddd..rrrdrdd.rdrddr.rrddddddrW
Wdrddwwwwwww.wwwwwdrrrrdrwwwwww.wwwwwwrW
Wd.dd             rddrrrd             rW
Wdrdr  XP         rddrrrd             rW
Wdrrd             r.rrddr             rW
Wdrrd             ddddrdr             dW
Wrddd             drrd.dr             dW
Wrrrr             drrddrr             rW
Wdrdd             .rdrrdr             rW
Wdrdd            wwwwwwwww            rW
Wrrrd                                 rW
Wrrrd             dd.rdrd             rW
Wddrr             rrrdrdd             rW
Wdd..wwwwwwwwwwwwwdrrrdddwwwwwwwwwwwwwdW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
W............X.........................W
Wwwwwwwwwwwww wwwwwwww.................W
Ww....d.............dw.................W
Ww.w w.wwwwww wwwwww.w.................W
Ww.qqq.wd.........dw.w.................W
Ww.qqq.w.wwww wwww.w.w.................W
Ww.qqq.w.wd.....dw.w.w.................W
Ww.qqq.w.w.ww ww.w.w.w.................W
Ww.qqq w w w   w w w w.................W
Ww.qqqqwqwqwqqqwqwqwqw.................W
Ww.qqq w w w   w w w w.................W
Ww.qqq.w.w.wwwww.w.w.w.................W
Ww.qqq.w.wd.....dw.w.w.................W
Ww.qdq.w.wwwwwwwww.w.w.................W
Ww.qdq.wd.........dw.w.................W
Ww.qdq.wwwwwwwwwwwww.w.................W
Ww.qdqd.............dw.................W
Wwwwwwwwwwwwwwwwwwwwww.................W
W......................................W
W......................................W
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
Wr.rd.rrr.w...drr..rw...d...r.w...dr.r.W
W... .r.r.w...r r..rwr....r..rwr...r.rrW
W.... ..rrw.r....r..w..r. rr..w....r.rrW
Wr.r.. rrrw.r.... ..wr......r.wr......rW
Wr. ...r..w.  ..r.rrw.......r.wr...... W
Wrr..r....w...r.....wr.rr.....wr..r r..W
W..r.rr..rwrr...r...wr.rr.....wr..r. .wW
W..r...r..w...r.r..rwr..r. .rrw. r..qwrW
Wr.r.wwwwwwwwwqwwwwwwwwwrwwwwwwwww..w. W
W.r.  .....rrrr..r.r.rr..rr... r..rwr..W
Wr.rr......rrrr..r. . r...r..r.rr.wr.rrW
W. .r.r. w..rrr..r.... ...r.....rw.r.rrW
W. .r.r. wr.wwwwwwwwwwwwwwwwwrr.w..r...W
Wr.. rrr.wr....r...r... .rr....w.r.rr.rW
Wr...rrr.wr.r... r..r.r...r.rrw.....r.PW
W .r....rw  ..r.rrr.......r.rw...... ..W
W..r.... w..r......r.rr.....wr..r.r...rW
Wr.rr..r.wr...r....rXrr......r..rq..r..W
Wr...r...w..r.rq......r... r.. r..rdr..W
Wrr.d. ..w..r.rr......r..r. r.q.rr.r...W
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
Wr. ...rr.....r.r..r........r.....r..d.W
W.....d.r....... ....r....r..r..... ..rW
W.......rdw.r.w.. w...wr...r..  q . .. W
Wdwwwwwwwwwww.w...w..rw.....r.    .....W
Wr........w...w.r.w d.w.....r..........W
Wrr..r....w...w...w..rwrr......r..d....W
W..r.....rwrr.w...w..rwrr.........r...rW
W.wwwwwwwwwww.w.r.w .rw.r....r  q ..d.rW
Wr.r......w...w.r.w..rw.r..d..    ..r..W
W.r.......wrr w..dw.. w...r.......d.r..W
W ........wrr w..rw...w... ..r.....r...W
W.wwwwwwwwwwwwwwwrw...w...r........r.  W
W...r.r...w...wr..wr..w.r..r.r  q .....W
W....r r..w...w...wd..w..r ...    ....dW
W.... .r..w.d.w..rw.r.w...r. r.........W
W.wwwwwwwwwww.w...w...w...r. r.........W
W..r......w.r.w...wr.rw...... ..r......W
Wr.X...r. w...w...wr.rw.........rd..r..W
W....r....w.r.wd..w...w.... ...r..d. ..W
Wrr.......w.r.wd..w...w..r..d.d.r..r...W
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
Wr.....rr.P.....r.Xra.......r........r.W
W.....r.r............r....r..r.r.......W
W........r..r..............r...........W
Wr.......r...........r......r..r.......W
Wr........r.....r...r.......r..r.......W
W.r..r........r......r.rr.........r....W
W..r.....r...........r.rr.........r...rW
W......r......r.r....r..r........r..r..W
Wr.r..........r.r..........r...........W
W..........rr.r..r....r...r....r..r.r..W
W..........r..r..r...........r.....r...W
W...r.r.......r...........r........r...W
W...r.r...r....r...r.......r...........W
W....r.r..r........r.....r............rW
W......r....r....r..r.r......r.........W
W..r.wwwwwwwwwwwwwwwwwwwwwwwwwwwwww....W
W..r.BBBBBBBBBBBBBBBBBBBBBBBBBBBBBB....W
Wr...rrrrrrrrrrrrrrrrrrrrrrrrrrrrrr.r..W
W......................................W
W.r................................r...W
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
W..X...................................W
W......................................W
W......................................W
W......................................W
W.....................q.q.q.q.q.q......W
W.....................r.r.r.r.r.r......W
W......................................W
W......................................W
W......... B. . . . . .................W
W.........  . . . . . .................W
W.........  .B. . . . .................W
W.........  . . . . . .................W
W.........  . .B. . . .................W
W.........  . . . . . .................W
W.........  . . .B. . .................W
W.........  . . . . . .................W
W.........  . . . .B. .................W
W.........  . . . . . .................W
W.........  . . . . .B.................W
W......................................W
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
Wr.rr..  r..r..r.r..Xr..r.rr..r.rr...r.W
W.w.rr......r..r...r....w...r......r.r.W
Wrrw.r... r.. r..r.r..rwr.... .. ..r.rqW
W...wr..r. q.....r. ..wr.  .....rrrr..rW
W.rr.wrr... r..r.r...wr. r......rrrr..rW
W. r..wr..r.r ..r.rrw... rrr. ...rrr..rW
W...rr.w. q..r..r.rw.....r.rr..r.r.rr..W
W..r.r..w.rr.... .w...r.. rrr..r....r..W
W... .rr.w....r. wqrr. ...rrr..r.r... rW
Wr.r...r.rw.....wr..r. .r....r.  ..r.rrW
W......r.rrw...w. ..rr..r.... ...r.....W
W.rr......r....r...r..r.r ..r.rr... r..W
W.rr......r.mmm..r....r...r......r.rr..W
W..r... r...r..r.r..r.rr... .....r.rr..W
W..r..r. .r....r.....r.  ......rrr..r. W
Wr.. r....r..r.r....r.  .......rrr..r..W
Wr...r..r.  ..r.  .... rrrr..r.r.rr..rrW
W. r..q r....r.rr......rrrr..r. .rr.. rW
Wr.rr..r.rr... .r.r. ...rrr..r.... ...rW
W...rr.r.rr... .r.r.P...r r..r.r....r..W
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WX..r..........r........r.....r..r.....W
W.r.rr.........r...r........r......r.r.W
W.r..r........r..r....r.r..........r.rrW
W.......r..r.....r..............rr.r..rW
W..r...r....r..r.r..............r..r..rW
W.........r.....r........rrr.......r...W
W....r....r.....r........r.rr..r....r..W
W..r...mmmmmm..mmmmmm.....r.r..r.......W
W.....rw....w..w..rrw.......r....r....rW
Wr.r...w..r.w..w....w...r....r.....r...W
W......w..r.w..w....wr..r........r.....W
W.rr...w....wr.w....w.r.....r.......r..W
W.rr...w....wrrw.r..w.....r......r.rr..W
W..r...w....w..w....w.rr.........r.rr..W
W.....rwwwwww..wwwwww............r..r..W
Wr...r....r..r.r.................r..r..W
W.............r........r.....r........rW
W..r..r.  q ....  q ...r  q .r..  q ..rW
Wr.....r    ....    ....    .r..    ..rW
W......r.......................r....r..W
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
W                   WWWWWWWWWWWWWWWWWWWW
W         r         WWWWWWWWWWWWWWWWWWWW
W  X      .         WWWWWWWWWWWWWWWWWWWW
W                   WWWWWWWWWWWWWWWWWWWW
W                   WWWWWWWWWWWWWWWWWWWW
W                   WWWWWWWWWWWWWWWWWWWW
W                   WWWWWWWWWWWWWWWWWWWW
W                   WWWWWWWWWWWWWWWWWWWW
W                   WWWWWWWWWWWWWWWWWWWW
W         B       P WWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
Wrq...............r.WWWWWWWWWWWWWWWWWWWW
WXrq.............rP.WWWWWWWWWWWWWWWWWWWW
Wd.rq...........r.d.WWWWWWWWWWWWWWWWWWWW
Wrd.rq.........r.dr.WWWWWWWWWWWWWWWWWWWW
W.rd.rq.......r.dr..WWWWWWWWWWWWWWWWWWWW
W..rd.rq.....r.dr...WWWWWWWWWWWWWWWWWWWW
W...rd.rq...r.dr....WWWWWWWWWWWWWWWWWWWW
W....rd.rq.r.dr.....WWWWWWWWWWWWWWWWWWWW
W.....rd.rr.dr......WWWWWWWWWWWWWWWWWWWW
W......rd..dr.......WWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
W       X           WWWWWWWWWWWWWWWWWWWW
W                   WWWWWWWWWWWWWWWWWWWW
W                   WWWWWWWWWWWWWWWWWWWW
W                   WWWWWWWWWWWWWWWWWWWW
W                 P WWWWWWWWWWWWWWWWWWWW
W                   WWWWWWWWWWWWWWWWWWWW
W              qqqq WWWWWWWWWWWWWWWWWWWW
W              qqqq WWWWWWWWWWWWWWWWWWWW
W              qqqq WWWWWWWWWWWWWWWWWWWW
Wddddddddddddddqqqq WWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
W..X.......rrr......WWWWWWWWWWWWWWWWWWWW
W..........rrr......WWWWWWWWWWWWWWWWWWWW
W...................WWWWWWWWWWWWWWWWWWWW
W..........mmm......WWWWWWWWWWWWWWWWWWWW
W.......r..   ......WWWWWWWWWWWWWWWWWWWW
W........r.   ......WWWWWWWWWWWWWWWWWWWW
W.........r   ......WWWWWWWWWWWWWWWWWWWW
W........P.mmm......WWWWWWWWWWWWWWWWWWWW
W..........   ......WWWWWWWWWWWWWWWWWWWW
W..........   ......WWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW
WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW

Keen to sign in?7

Participating on the further development of DingDash would boil down to tasks such as design, programming and usability in general.7

Toolchain

The toolchain setting up such a multi platform project is very similar to the one described by the GraalVM: Native command line tools for Linux and Windows written in Java blog post. Though instead of using the official GraalVM builds, we need a GraalVM packaged by Gluon (being also the major JavaFX contributor). This GraalVM is tailored to work with the GluonFX plugin for Maven, which is also required for the undertaking.

Project

The funcodes-testbild app is a sample on how to build a minimum (multi platform) native Android app using JavaFX: You may consult its source codes providing a template for a basic multi platform project. The basic setup of the funcodes-testbild demo, even though being much more simple, is quite near to that of the DingDash app (and therewith sufficient for the below explanations).

The funcodes-testbild artifact represents a minimal JavaFX mobile app (Android) for isolating and debugging issues.

For the general setup please follow the steps as described by the GraalVM: Native command line tools for Linux and Windows written in Java blog post and apply the changes as described below by choosing the correct GraalVM and configuring the GluonFX plugin for Maven.

Initial folder layout

Below see the simplified initial folder layout of our multi platform project:

.
│
├── pom.xml
│
└── src
    │
    ├── android
    │   │
    │   ├── AndroidManifest.xml
    │   │
    │   └── res
    │       │
    │       ├── mipmap-hdpi
    │       │
    │       ├── mipmap-mdpi
    │       │
    │       ├── mipmap-xhdpi
    │       │
    │       ├── mipmap-xxhdpi
    │       │
    │       └── mipmap-xxxhdpi
    │
    ├── main
    │   │
    │   ├── java
    │   │   │
    │   │   └── ...
    │   │
    │   └── resources
    │       │
    │       └── META-INF
    │           │
    │           └── native-image
    │               │
    │               └── ...
    │ 
    └── test
        │
        └── java
            │
            └── ...

Gluon GraalVM

First of all, an according GraalVM needs to be installed, either by directly using the downloads from Gluon or by using SDKMAN!8. Make sure you have set the JAVA_HOME and the GRAALVM_HOME environment variables correctly and your GraalVM is on your system’s path9. You may verify your installation by invoking java --version in a terminal:

As we use a Gluon GraalVM with a version >= 16, make sure your GraalVM is chosen accordingly.

openjdk 17.0.3 2022-04-19
OpenJDK Runtime Environment GraalVM 22.1.0.1 (build 17.0.3+7-jvmci-22.1-b06)
OpenJDK 64-Bit Server VM GraalVM 22.1.0.1 (build 17.0.3+7-jvmci-22.1-b06, mixed mode, sharing)

The printed version information should contain the string “GraalVM” and a version >= 16 somewhere in the output (unfortunately we cannot immediately see from the output if we have a Gluon GraalVM up and running).

Main and MainFX

When looking at the funcodes-testbild app, we see that we have a Main.java file as well as a MainFx.java file.

1
2
3
4
5
6
7
8
...
public class Main {
	...
	public static void main( String args[] ) {
		MainFx.main( args );
	}
	...
}

This is a workaround for the fat JAR to work by initially invoking a main method which’s class does not(!) extend JavaFX’s Application class which then statically calls the MainFx.java’s main method.10

1
2
3
4
5
6
7
8
...
public class MainFx extends Application  {
	...
	public static void main( String args[] ) {
		launch( args );
	}
	...
}

With this workaround the app runs besides on Android smartphones also on GNU Linux as well as on MS Windows machines (and presumably others3) using a single code base. So no Android Studio required!4

Maven’s pom.xml

The pom.xml in your project’s root folder got to be adjusted to pull the JavaFX dependencies, to invoke the GluonFX plugin for Maven and for building a fat JAR file.

When having setup everything, the resulting Maven configuration should look similar to this pom.xml from the funcodes-testbild sample …

Defining all the versions

A good practice is to define all dependency and plugin versions in the <properties> section of your pom.xml:

<propertes> […]
1
2
3
4
5
6
7
8
9
10
11
12
...
<properties>
	...
	<main.module>com.comcodes.dingdash</main.module>
	<main.class>${main.module}.Main</main.class>
	<main.fx.class>${main.module}.MainFx</main.fx.class>
	<com.gluonhq.gluonfx.maven.plugin.version>1.0.15</com.gluonhq.gluonfx.maven.plugin.version>
	<org.apache.maven.plugins.shade.version>3.4.1</org.apache.maven.plugins.shade.version>
	<org.openjfx.version>20-ea+11</org.openjfx.version>
	...
<properties>
...

Adding the JavaFX dependencies

The dependencies in the <dependencies> section of the pom.xml refer to the previously defined version properties:

<dependencies> […]
1
2
3
4
5
6
7
8
9
10
11
...
<dependencies>
	...
	<dependency>
		<groupId>org.openjfx</groupId>
		<artifactId>javafx-fxml</artifactId>
		<version>${org.openjfx.version}</version>
	</dependency>
	...
</dependencies>
...

Configuring GluonFX plugin for Maven

The GluonFX plugin for Maven is added to the <plugins> section of the pom.xml and enables us to build our app for Android as well as for Apple iOS:

<plugins> […]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
...
<plugins>
	...
	<plugin>
		<groupId>com.gluonhq</groupId>
		<artifactId>gluonfx-maven-plugin</artifactId>
		<version>${com.gluonhq.gluonfx.maven.plugin.version}</version>
		<configuration>
			<target>${gluonfx.target}</target>
			<mainClass>${main.class}</mainClass>
			<resourcesList>
				<pattern>.*\\.txt</pattern>
				<pattern>.*\\.ini</pattern>
				<pattern>.*\\.png</pattern>
			</resourcesList>
		</configuration>
	</plugin>
	...
</plugins>
...

Android and iOS

To choose whether to build for Android or Apple iOS, some build profiles android and ios are included in the <profiles> section of the pom.xml:

<profiles> […]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
...
<profiles>
	...
	<!-- iOS -->
	<profile>
		<id>ios</id>
		<properties>
			<gluonfx.target>ios</gluonfx.target>
			<main.class>${main.fx.class}</main.class>
		</properties>
	</profile>
	<!-- Android -->
	<profile>
		<id>android</id>
		<properties>
			<gluonfx.target>android</gluonfx.target>
			<main.class>${main.fx.class}</main.class>
		</properties>
	</profile>
	<!-- Host -->
	<profile>
		<id>host</id>
		<properties>
			<gluonfx.target>host</gluonfx.target>
			<main.class>${main.fx.class}</main.class>
		</properties>
	</profile>
	...
<profiles>
...

Note that here we go directly for the MainFx.java class denoting the main class, as the Main.java class’ workaround is not required for the mobile phones’ builds!

Configuring the Maven Shade plugin

To get a fat JAR for running on your desktop, the pom.xml is complemented with the Maven Shade Plugin, added to the <plugins> section. Note that it is included as a build profile in the the <profiles> section, being active by default, so a mvn clean package by default creates a fat JAR:

<plugins> […]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
...
<profiles>
	...
	<profile>
		<id>fatjar</id>
		<activation>
			<activeByDefault>true</activeByDefault>
		</activation>
		<build>
			<plugins>
				<plugin>
					<groupId>org.apache.maven.plugins</groupId>
					<artifactId>maven-shade-plugin</artifactId>
					<version>${org.apache.maven.plugins.shade.version}</version>
					<executions>
						<execution>
							<phase>package</phase>
							<goals>
								<goal>shade</goal>
							</goals>
							<configuration>
								<keepDependenciesWithProvidedScope>true</keepDependenciesWithProvidedScope>
								<minimizeJar>false</minimizeJar>
								<transformers>
									<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
										<mainClass>${main.class}</mainClass>
									</transformer>
									<!-- Merge all "spring.handlers" files -->
									<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
										<resource>META-INF/spring.handlers</resource>
									</transformer>
									<!-- Merge all "spring.schemas" files -->
									<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
										<resource>META-INF/spring.schemas</resource>
									</transformer>
								</transformers>
								<filters>
									<filter>
										<artifact>*:*</artifact>
										<excludes>
											<exclude>**/bundle.properties</exclude>
											<exclude>**/module-info.class</exclude>
											<exclude>META-INF/*.DSA</exclude>
											<exclude>META-INF/*.RSA</exclude>
											<exclude>META-INF/*.SF</exclude>
											<exclude>META-INF/DEPENDENCIES</exclude>
											<exclude>META-INF/LICENSE.txt</exclude>
											<exclude>META-INF/LICENSE</exclude>
											<exclude>META-INF/MANIFEST.MF</exclude>
											<exclude>META-INF/NOTICE.txt</exclude>
											<exclude>META-INF/NOTICE</exclude>
										</excludes>
									</filter>
									<filter>
										<artifact>com.fasterxml.jackson.dataformat:jackson-dataformat-yaml</artifact>
										<excludes>
											<exclude>META-INF/services/com.fasterxml.jackson.core.JsonFactory</exclude>
											<exclude>META-INF/services/com.fasterxml.jackson.core.ObjectCodec</exclude>
										</excludes>
									</filter>
									<filter>
										<artifact>com.fasterxml.jackson.dataformat:jackson-dataformat-xml</artifact>
										<excludes>
											<exclude>META-INF/services/com.fasterxml.jackson.core.JsonFactory</exclude>
											<exclude>META-INF/services/com.fasterxml.jackson.core.ObjectCodec</exclude>
										</excludes>
									</filter>
									<filter>
										<artifact>software.amazon.awssdk:third-party-jackson-core</artifact>
										<excludes>
											<exclude>META-INF/maven/com.fasterxml.jackson.core/jackson-core/pom.properties</exclude>
											<exclude>META-INF/maven/com.fasterxml.jackson.core/jackson-core/pom.xml</exclude>
										</excludes>
									</filter>
									<filter>
										<artifact>com.pi4j:pi4j-gpio-extension</artifact>
										<excludes>
											<exclude>LICENSE.txt</exclude>
											<exclude>NOTICE.txt</exclude>
											<exclude>README.md</exclude>
										</excludes>
									</filter>
									<filter>
										<artifact>org.refcodes:*</artifact>
										<excludes>
											<exclude>runtimelogger.ini</exclude>
										</excludes>
									</filter>
								</filters>
							</configuration>
						</execution>
					</executions>
				</plugin>
			</plugins>
		</build>
	</profile>
	...
<profiles>
...

The Maven Shade Plugin is being tweaked so that it’s configuration runs with my various funcodes.club projects on Bitbucket. Note that here we go for the Main.java class denoting the main class, as the Main.java class’ workaround is required for the desktop’s fat JAR builds!10

App resources

Besides the common resources found below your project’s src/main/resources folder and the ones as discussed in the article GraalVM: Native command line tools for Linux and Windows written in Java, there are some mobile app specific resources of interest found below the src/android folder: Directly in the src/android directory you can place the AndroidManifest.xml file, in this example it has been tweaked for the label Testbild and a landscape orientation. The src/android/res folder holds your mobile app’s icons 11.

Final folder layout

Below see the simplified final folder layout of our multi platform project:

.
│
├── pom.xml
│
├── src
│   │
│   ├── android
│   │   │
│   │   ├── AndroidManifest.xml
│   │   │
│   │   └── res
│   │       │
│   │       ├── mipmap-hdpi
│   │       │
│   │       ├── mipmap-mdpi
│   │       │
│   │       ├── mipmap-xhdpi
│   │       │
│   │       ├── mipmap-xxhdpi
│   │       │
│   │       └── mipmap-xxxhdpi
│   │
│   ├── main
│   │   │
│   │   ├── java
│   │   │   │
│   │   │   └── ...
│   │   │
│   │   └── resources
│   │       │
│   │       └── META-INF
│   │           │
│   │           └── native-image
│   │               │
│   │               └── ...
│   │ 
│   └── test
│       │
│       └── java
│           │
│           └── ...
│
└── target
    │
    ├── *.jar
    │
    └── gluonfx
        │
        └── aarch64-android
            │
            └── gvm
                │
                └── *.apk

Note the target folder containing either the *.apk Android packages or the fat *.jar JAR files, depending on the build being triggered.

Building for Android

Building your project for Android3 smartphones is as easy as this:

mvn clean gluonfx:build gluonfx:package -Pandroid

Below your project’s target folder, you find the generated *.apk Android package:

target/gluonfx/aarch64-android/gvm/*.apk

For Apple iOS the process of building should be similar.3

Installing and launching on Android

On Android, you just got to copy the *.apk Android package to your smartphone, locate it with your smartphone’s file manager and install it!

Building for the desktop

Build your project as usual for a desktop experience on GNU Linux as well as on MS Windows machines (and presumably others3):

mvn clean package

Launching on the desktop

Launch your desktop application as usual, here we go for the fat fat JAR as configured by the Maven Shade Plugin:

java -jar ./target/*.jar

You can also launch the desktop application directly from your favorite IDE (as seen in the topmost video)!

Wrapping up things

The funcodes-testbild app is a fully functioning demo you can use as a template to play around. My doings very much have been inspired by the article How to Create Mobile Apps with JavaFX. As said before, getting DingDash into the Google Play Store would be great, so I am looking for you, comrade-in-arms, helping finishing off the DingDash project!

  1. For deliverables such as launcher, bundle or installer see Java deliverables as executable bundle and the launcher trick

  2. For native deliverables see GraalVM: Native command line tools for Linux and Windows written in Java

  3. As the GluonFX plugin for Maven also supports Apple iOS and the JVM also runs on Apple macOS, I presume that running on Apple iPhones and Apple Macs is also possible, though as of lack of hardware, it has not been tested.  2 3 4 5 6

  4. Did you also experience serious issues getting Android Studio’s mobile phone Android emulators up and running? For me, I had very unsatisfying experiences on Arch Linux, maybe because of an outdated graphics card or the open source nouveau drivers and on MS Windows, maybe because of a boarded up company laptop?  2

  5. I consider it being a big edge that the Java language is a late adopter as only features with a proven benefit make it into the specification, such as the record types introduced with Java version >= 14 (which lessen the need to use the Kotlin language or extensions such as Project Lombok), therewith not overloading the language with hurried features. 

  6. See also the blog post JavaFX checkerboard for fun and cellular automaton experiments

  7. You may contact me at steiner@refocdes.org 2 3

  8. As I often switch forth and back between different JVM and GraalVM installations, I preferably use SDKMAN!(e.g. sdk install java 22.1.0.1.r17-gln, verify the available candidates via sdk list java). 

  9. The basic setup of your GraalVM is described in this Quick Start Guide

  10. See Maven Shade JavaFX runtime components are missing 2

  11. To create an icon set for your mobile app, you may go for this App Icon Generator