blog:2023:0710_testing_kivy

Trying to re-implement HomeCtrl android app with kivy

So, lately I've been messing around a lot with my homectrl system since I had to replace the raspberry pi handling all this. And as a result of course, I also refactored the software layer (everything now available as part of the NervHome project, in the nvp/home folder) and broke the compatibility with my existing android app 🤣

Now it's time to try to restore this, but really, I don't want to have anything to do with the Android Studio, gradle, and IDE interactions in this process. So I'm now considering using kivy to provide a python implementation instead. Let's see where we can go with this…

Youtube video for this article available at:

First we will need a kivy development python env, so let's create that (still in NervHome):

  homectrl_client_env:
    inherit: default_env
    packages:
      - kivy

I'm using this page as initial reference, so now let's create a simple hello world app:

from kivy.app import App
from kivy.uix.label import Label

class MainApp(App):
    def build(self):
        label = Label(text='Hello from Kivy',
                      size_hint=(.5, .5),
                      pos_hint={'center_x': .5, 'center_y': .5})

        return label

if __name__ == '__main__':
    app = MainApp()
    app.run()

And let's create a script to try running this simple app:

  homectrl_client:
    notify: false
    custom_python_env: homectrl_client_env
    cwd: ${PROJECT_ROOT_DIR}/nvh/app/homectrl_client
    cmd: ${PYTHON} ${PROJECT_ROOT_DIR}/nvh/app/homectrl_client/main.py
    python_path: ["${PROJECT_ROOT_DIR}", "${NVP_ROOT_DIR}"]

Wonderfull, just running this script works just fine on windows:

Now let's try to handle the android compilation part. For this we will need to add buildozer to the python env… Oh oh, and now just having a quick check at the buildozer documentation page, the first thing I see is this:

⇒ Seriously man ? 😒

Let's try on windows first anyway, and if it's really not working we will try on linux then.

We prepare a simple script to be able to execute buildozer:

  buildozer:
    notify: false
    custom_python_env: homectrl_client_env
    # cwd: ${PROJECT_ROOT_DIR}/nvh/app/homectrl_client
    cmd: ${PYTHON} -m buildozer
    python_path: ["${PROJECT_ROOT_DIR}", "${NVP_ROOT_DIR}"]

Next we run buildozer in the homectrl_client app root folder:

PS D:\Projects\NervHome\nvh\app\homectrl_client> nvp buildozer init

And now, the moment of truth, trying to build the android app in release version on windows 🤞:

nvp buildozer -v android release
But of course, this is failing lamentably 😭:

I Check configuration tokens
Unknown command/target android
2023/07/08 11:34:29 [nvp.nvp_object] ERROR: Subprocess terminated with error code 1 (cmd=['D:\\Projects\\NervProj\\.pyenvs\\homectrl_client_env\\python.exe', '-m', 'buildozer', '-v', 'android', 'release'])
2023/07/08 11:34:29 [nvp.components.runner] ERROR: Error occured in script command:
cmd=['D:\\Projects\\NervProj\\.pyenvs\\homectrl_client_env\\python.exe', '-m', 'buildozer', '-v', 'android', 'release']
cwd=D:\Projects\NervHome\nvh\app\homectrl_client
return code=1
lastest outputs:
I Check configuration tokens
Unknown command/target android

So, for now, let's just not fight against the complete world one more time and just try to use this on linux instead.

And while I am at it, now trying to use MobaXTerm to Neptune: https://mobaxterm.mobatek.net/download-home-edition.html

Okay, so we got a bit further on linux, but this is still not quite working, because apparently we need cython to be installed:

# Check configuration tokens
# Ensure build layout
# Create directory /home/kenshin/.buildozer
# Create directory /home/kenshin/.buildozer/cache
# Create directory /mnt/data1/dev/projects/NervHome/nvh/app/homectrl_client/.buildozer
# Create directory /mnt/data1/dev/projects/NervHome/nvh/app/homectrl_client/bin
# Create directory /mnt/data1/dev/projects/NervHome/nvh/app/homectrl_client/.buildozer/applibs
# Create directory /home/kenshin/.buildozer/android/platform/android/platform
# Create directory /mnt/data1/dev/projects/NervHome/nvh/app/homectrl_client/.buildozer/android/platform
# Create directory /mnt/data1/dev/projects/NervHome/nvh/app/homectrl_client/.buildozer/android/app
# Check configuration tokens
# Preparing build
# Check requirements for android
# Search for Git (git)
#  -> found at /usr/bin/git
# Search for Cython (cython)
# Cython (cython) not found, please install it.

Okay, so it is enough to add cython as a pip package in the python environment:

  homectrl_client_env:
    inherit: default_env
    packages:
      - kivy
      - buildozer
      - cython

But then we get yet another error, this time about the missing java compiler (javac) 😞 So, I guess this means I need to provide java somehow…

# Check configuration tokens
# Ensure build layout
# Check configuration tokens
# Preparing build
# Check requirements for android
# Search for Git (git)
#  -> found at /usr/bin/git
# Search for Cython (cython)
#  -> found at /mnt/data1/dev/projects/NervProj/.pyenvs/homectrl_client_env/bin/cython
# Search for Java compiler (javac)
# Java compiler (javac) not found, please install it.

To get the available install options:

$ java -v

Command 'java' not found, but can be installed with:

sudo apt install default-jre              # version 2:1.11-72, or
sudo apt install openjdk-11-jre-headless  # version 11.0.19+7~us1-0ubuntu1~20.04.1
sudo apt install openjdk-13-jre-headless  # version 13.0.7+5-0ubuntu1~20.04
sudo apt install openjdk-16-jre-headless  # version 16.0.1+9-1~20.04
sudo apt install openjdk-17-jre-headless  # version 17.0.7+7~us1-0ubuntu1~20.04
sudo apt install openjdk-8-jre-headless   # version 8u372-ga~us1-0ubuntu1~20.04

So for now, let's install the openjdk version 11:

sudo apt update
sudo apt install openjdk-11-jre-headless

And now we hava java installed:

$ java -version
openjdk version "11.0.19" 2023-04-18
OpenJDK Runtime Environment (build 11.0.19+7-post-Ubuntu-0ubuntu120.04.1)
OpenJDK 64-Bit Server VM (build 11.0.19+7-post-Ubuntu-0ubuntu120.04.1, mixed mode, sharing)

Try the buildozer command again:

nvp buildozer -v android release

Ohhh, crap… javac is not part of the jre: it's part of the jdk 😭, so I rather need to install a jdk in fact:

$ javac -version

Command 'javac' not found, but can be installed with:

sudo apt install default-jdk              # version 2:1.11-72, or
sudo apt install openjdk-11-jdk-headless  # version 11.0.19+7~us1-0ubuntu1~20.04.1
sudo apt install openjdk-13-jdk-headless  # version 13.0.7+5-0ubuntu1~20.04
sudo apt install openjdk-16-jdk-headless  # version 16.0.1+9-1~20.04
sudo apt install openjdk-17-jdk-headless  # version 17.0.7+7~us1-0ubuntu1~20.04
sudo apt install openjdk-8-jdk-headless   # version 8u372-ga~us1-0ubuntu1~20.04
sudo apt install ecj                      # version 3.16.0-1

So installing that now:

sudo apt install openjdk-11-jdk-headless

Yooohhooo Now it seems to be starting soem build process properly 🥳, let's see how this goes. And meanwhile, let's add a dedicated script to request the build of the app with the command nvp buildozer -v android release executed in the correct folder:

  build_homectrl_client_android:
    notify: false
    custom_python_env: homectrl_client_env
    cwd: ${PROJECT_ROOT_DIR}/nvh/app/homectrl_client
    cmd: ${PYTHON} -m buildozer -v android release
    python_path: ["${PROJECT_ROOT_DIR}", "${NVP_ROOT_DIR}"]

Buildozer is installing all the shared dependencies/tools in /home/kenshin/.buildozer/ from what I see.

Important Note: when reaching the point where the Android SDk license is shown I don't see the “Accept ? (y/N)” message at first but we still need to type “y” before continuing with Enter, otherwise the setup process will be stopped.

wwhhaaooo, I'm already very surprised by all the elements that this buildozer tool seems to be taking care of: it's also downloading so many additional packages from sources (which I supposed will then get built for Android):

Arrrggghh 😖 I got an error in the build process of course:

[DEBUG]:        acinclude.m4:251: LIBFFI_CHECK_LINKER_FEATURES is expanded from...
[DEBUG]:        acinclude.m4:353: LIBFFI_ENABLE_SYMVERS is expanded from...
[DEBUG]:        configure.ac:418: the top level
[DEBUG]:        configure.ac:41: error: possibly undefined macro: AC_PROG_LIBTOOL
[DEBUG]:              If this token and others are legitimate, please use m4_pattern_allow.
[DEBUG]:              See the Autoconf documentation.
[DEBUG]:        configure:7913: error: possibly undefined macro: AC_PROG_LD
[DEBUG]:        autoreconf: /usr/bin/autoconf failed with exit status: 1

Actually, it seems this could very well be because I don't have libtool installed on my system yet, let's install it:

sudo apt install libtool libtool-bin

Next trying the build command again (eg. nvp buildozer -v android release)

⇒ Yeeppee 🥳! It seems this worked just fine, and produced a final .aab file… Except that I don't seem to be able to install that file easily on my android phone, so updating the buildozer.spec file to request the build of an apk file:

# (str) The format used to package the app for release mode (aab or apk or aar).
# android.release_artifact = aab
android.release_artifact = apk

OK! I finally got an .apk file now for my app build, except that now, the apk is not signed 🤣 so Android will refuse to install it as is. Which means I need to find how to sign this thing now 🤔.

I found this page on the subject: https://groups.google.com/g/kivy-users/c/J4Yixw1nxfM

So, from what I understand we can generate the keystore file with the first command:

keytool -genkey -v -keystore homectrl.keystore -alias homectrl -keyalg RSA -keysize 2048 -validity 10000

And then I should be able to sign my apk with the command:

jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore homectrl.keystore bin/homectrl-0.1-arm64-v8a_armeabi-v7a-release-unsigned.apk homectrl 

YYESSS! This works now! I could successfully install the apk on my phone and start the app!

Okay, okay, that's great, but, I don't want to have to execute the jarsigner command manually everytime I want to build the app, so, let's write a small script to handle all the necessary steps:

  • I've thus added an entry NV_HOMECTRL_KEYPASS in my local .envvars file
  • Added the script file build_android.sh to handle the build process:
    #!/bin/sh
    
    # Build for android
    source /mnt/data1/dev/projects/NervProj/cli.sh
    source /home/kenshin/.envvars
    
    nvp buildozer -v android release
    
    app_version="0.1"
    
    # Generate the keystore if needed:
    # keytool -genkey -v -keystore homectrl.keystore -alias homectrl -keyalg RSA -keysize 2048 -validity 10000
    
    jarsigner -verbose -keystore homectrl.keystore bin/homectrl-${app_version}-arm64-v8a_armeabi-v7a-release-unsigned.apk homectrl -storepass "$NV_HOMECTRL_KEYPASS"
    # -sigalg SHA1withRSA -digestalg SHA1
    
    # Move the signed file:
    mv bin/homectrl-${app_version}-arm64-v8a_armeabi-v7a-release-unsigned.apk /mnt/shares/master/softwares/Android/homectrl-${app_version}-arm64-v8a_armeabi-v7a-release-signed.apk
    

⇒ Alright, now I can build the app completely automatically with the command:

nvp build_homectrl_client_android

And I get the output directly delivered in my softwares/Android folder, perfect 👍!

Next I need to change the icon of the app: simply updating the following entry (and adding the corresponding file) did the trick:

# (str) Icon of the application
icon.filename = %(source.dir)s/assets/app_icon.png

Now let's add some additional visual elements on this app:

[ …Fighting for a while to understand the layout mechanism in kivy…] And eventually I discovered the AnchorLayout which is what I was looking for from the beginning 😄 (before that I was trying to center a widget using a combination of vertical/horizontal BoxLayouts, but that doesn't seem to work so well… I guess the framework is just not as mature as QT for instance, but never mind.)

And is “how far” I got before the next stop, simply showing a single button properly centered horizontally ;-):

Aarrrfff man… lol, this will never end: now I just discovered KivyMD which sounds very cool. And on the otherwise, there is alos Flutter from what I see, this one seems even better for mobile development from what I read, but it's using the Dart language, so that's a big “No no” for me: I don't want to have to learn yet another language to write this app, so i'm definitely sticking to python! But KivyMD… I guess I need to try it 😁

So, let's install that package:

  homectrl_client_env:
    inherit: default_env
    packages:
      - kivy
      - kivymd
      - buildozer
      - cython

And let's try to rebuild a simple hello world app with this new package:

from kivy.logger import Logger
from kivy.utils import platform
from kivymd.app import MDApp
from kivymd.uix.label import MDLabel


class MainApp(MDApp):
    """Main application class"""

    def build(self):
        """Application build function"""

        if platform == "android" or platform == "ios":
            Window.maximize()
        else:
            Window.size = (400, 800)
            Window.top = 80

        Logger.info("app: Window size is: %s", Window.size)

        Logger.info("App: building the app.")
        return MDLabel(text="Hello, World", halign="center")


if __name__ == "__main__":
    app = MainApp()
    app.run()

So far so good: this Material design style is pretty nice.

I have then added support for event handling on my gate activation button with:

        button = MDRaisedButton(text="Activate Gate", size=(150, 20), size_hint=(None, None))
        button.padding = [40, 0]
        button.bind(on_press=self.activate_gate)
        row.add_widget(button)

and the handler function:

    def activate_gate(self, _btn):
        """Handler to activate the gate"""
        Logger.info("App: should activate the gate here.")
        toast("Activating gate", background=[107 / 255, 208 / 255, 137 / 255, 1])

This will display the following “toast” message when triggered:

Next we need to add support for the request module: OK

… hmmm… [A few moments later…] 😒 I have been experimenting for a while now with Kivy or KivyMD and after the initial success, things seem to be getting a bit less “handy”: I don't even seem to be able to center a button with a label on the center of a page 😲! Sounds crazy… Time to find some more advanced code examples I think to improve my understanding of this framework.

I found the following interesting repositories on this topic:

And I basically tried all the changes I could think about to get this very minimal test to display correctly on my phone… even tried to change the build tool versions and all, and still it's just not working at all. To the point where I'm starting to think it's not worth it to try to use that framework.

Feeewwwww…. finally moving a bit forward: I tested with the code of the navigationdrawer example from KivyMD (https://kivymd.readthedocs.io/en/1.1.1/components/navigationdrawer/) and that one seems to be working correctly 😲! So will now try with baby steps from there…

Okay, well, no: this is just crap. Everytime I try to push it just a little bit, it will work on windows, but break lamentably when I install the app on my phone. I need to find something else.

…or… okay, let's try one last thing: downgrading everything to use python 3.7.8, kivy=1.11.1 kivymd=0.104.2… well, cannot install kivy 1.11.1 on windows already 🤣 What a joke… But let's try with updating just the buildozer config then.

⇒ Okay, so I got the app to compile with buildozer using python 3.7.9 and kivy 2.1.0, but then I get the same incorrect/unresponsive display of the main screen on my phone. Time to give up, seriously this time, this is not worth it. It's a pity this is ending like that, we had a good start on this journey lol.

  • blog/2023/0710_testing_kivy.txt
  • Last modified: 2023/07/26 18:20
  • by 127.0.0.1