====== Trying to re-implement HomeCtrl android app with kivy ======
{{tag>dev python android kivy kivymd}}
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:
;#;
{{ youtube>7Y2-W6v2kck?large }}
;#;
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 [[https://realpython.com/mobile-app-kivy-python/|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:
{{ blog:2023:001_kivy_hello_world.png }}
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:
{{ blog:2023:002_buildozer_build_for_android.png }}
=> 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):
{{ blog:2023:003_buildozer_downloads.png }}
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 ;-):
{{ blog:2023:004_minimal_kivy_app.png }}
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:
{{ blog:2023:005_kivymd_toast.png }}
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:
* https://github.com/kulothunganug/Example-Kivy-Apps
* https://github.com/kivymd/KitchenSink
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.
/*
==== Kivy Experiment 2.0 ====
==== TODO ====
* Install https://github.com/kivy/plyer in project to get access to notifications, etc ?
Should check:
* https://kivy.org/doc/stable/examples/gen__canvas__canvas_stress__py.html
* https://kivy.org/doc/stable/tutorials/pong.html
I just found this list of examples: https://storage.googleapis.com/google-code-archive-downloads/v2/code.google.com/kivy/kivydemo-for-android.zip
But cannot install Kivy launcher on phone anyway.
Uninstalling java: sudo apt remove openjdk-11-jdk-headless openjdk-11-jre-headless
Installing version 13: sudo apt install openjdk-13-jdk
Installing more packages: sudo apt install git zip unzip python3-pip autoconf libtool pkg-config zlib1g-dev libncurses5-dev libncursesw5-dev libtinfo5 cmake libffi-dev libssl-dev
*/