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 releaseBut 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}"]
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:
#!/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.