====== 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 */