python ImportError using PyAL with kivy - android

To be able to use 3D sound, I am trying to use the OpenAl sound library (PyAL) in kivy. Here's the python header of the main.py:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import kivy
kivy.require('1.7.2')
from kivy.app import App
from kivy.uix.button import Button
from openal.loaders import load_wav_file
from openal.audio import SoundSink, SoundSource, SoundData, SoundListener
[...]
OpenAl is not included in the standard options provided by kivys python for android, so I created a pyal/recipe.sh for it.
#!/bin/bash
# version of your package
VERSION_pyal=${VERSION_pyal:-0.1.0}
# dependencies of this recipe
DEPS_pyal=()
# url of the package
URL_pyal=https://bitbucket.org/marcusva/py-al/downloads/PyAL-0.1.0.tar.gz
# md5 of the package
MD5_pyal=862a9345004c76651dcf91b7d990fe84
# default build path
BUILD_pyal=$BUILD_PATH/pyal/$(get_directory $URL_pyal)
# default recipe path
RECIPE_pyal=$RECIPES_PATH/pyal
# function called for preparing source code if needed
# (you can apply patch etc here.)
function prebuild_pyal() {
true
}
# function called to build the source code
function build_pyal() {
true
}
# function called after all the compile have been done
function postbuild_pyal() {
true
}
Compiling the toolchain works:
Download package for pyal
[...]
Call prebuild_pyal
[...]
Call build_pyal
[...]
Call postbuild_pyal
[...]
All done !
Building the apk works, and so does installing. But when running the app on the device (Nexus 4), it closes immediately. Netcat complains:
[...]
I/python (13175): ImportError: No module named openal.loaders
I/python (13175): Python for android ended.
[...]
Somehow the inclusion of PyAL in the toolchain went wrong. Any ideas what the problem could be?
On my Laptop (Ubuntu 14.04), everything works fine. Not a huge surprise, since it should use the python from the repository, not kivys python for android.

Your python-for-android recipe is for PyAL (Python bindings for OpenAL), but PyAL requires that OpenAL be installed to do anything. You probably don't need a recipe for PyAL - which appears to be a pure Python module - but you will have to create a recipe for OpenAL. Creating that recipe will require knowing how to build OpenAL, so you may need help from their maintainers.

Related

Custom command requires calling CMake three times to have desired output

My project is fairly simple. I have an app that includes a library written based on integration platfrom so it looks something like this:
My app
----> Extension
----> Main library
The app is cross-compiled to Android. All of them are based on CMake. I'm using CLion as an IDE. I've added a profile with custom toolchain required to build for Android (that is clang, clang++ for apropiate platform, cmake from Android SDK and make from Android NDK r20b). Problem is I have to call CMake 3 times (reload it in CLion) before it generates all of the files needed. Whole file is listed below.
On the first run it ignores the fact it is build for android (doesn't go into if (ANDROID) ) and copies the prebuilt sub-executable for linux (default)
On the second run it ignores the needed android.toolchain.cmake and when build on this stage it fails to add android headers to search path BUT chooses correct prebuilt sub-executable
On the the third run it uses the android.toolchain.cmake file AND chooses correct sub-executable.
After that I can hit build and everything compiles correctly.
The CMake I have:
add_executable(androidApp main.cpp)
target_include_directories(androidApp
PUBLIC
../submodules/exten/src/libraries/main_lib/lib/include
../submodules/exten/src/libraries/main_lib/sdk/rlogging/include) //Headers from main library
target_link_libraries(androidApp
PRIVATE
exten) //Extension
install(TARGETS androidApp DESTINATION ${CMAKE_SOURCE_DIR}/bin)
install(FILES ../submodules/exten/src/libraries/main_lib/integration_app/resources/someFile.zip DESTINATION ${CMAKE_SOURCE_DIR}/bin)
install(FILES ../resources/someFile2.zip DESTINATION ${CMAKE_SOURCE_DIR}/bin/resources)
file(MAKE_DIRECTORY ${CMAKE_SOURCE_DIR}/bin/bootsafe)
file(WRITE ${CMAKE_SOURCE_DIR}/bin/bootsafe/dummy "") //Required dir
add_custom_command(TARGET dongleApp POST_BUILD
COMMAND ${CMAKE_SOURCE_DIR}/ndk/android-ndk-r20b/toolchains/llvm/prebuilt/linux-x86_64/arm-linux-androideabi/bin/strip --strip-debug dongleApp
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Stripping debug symbols")
if (ANDROID)
target_link_libraries(androidApp
PRIVATE
log)
endif (ANDROID) //Linking android lib
On different CMake (on the main library level) here's how the choosing on correct prebuilt is written
if(NOT ANDROID)
set(PREBUILD_NAME "linux")
else()
if(ANDROID_ABI STREQUAL "x86_64")
set(PREBUILD_NAME "android-x86")
else()
set(PREBUILD_NAME "android-arm")
endif()
endif()
The command that is used to generate makefiles:
/home/gravlok/companyName/app_v2/sdk/cmake/3.10.2.4988404/bin/cmake
-DCMAKE_BUILD_TYPE=Debug
-DCMAKE_MAKE_PROGRAM=/home/gravlok/companyName/app_v2/ndk/android-ndk-r20b/prebuilt/linux-x86_64/bin/make
-DCMAKE_C_COMPILER=/home/gravlok/companyName/app_v2/ndk/android-ndk-r20b/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi28-clang
-DCMAKE_CXX_COMPILER=/home/gravlok/companyName/app_v2/ndk/android-ndk-r20b/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi28-clang++
-DANDROID=ON
-DCMAKE_TOOLCHAIN_FILE=/home/gravlok/companyName/app_v2/ndk/android-ndk-r20b/build/cmake/android.toolchain.cmake -DANDROID_PLATFORM=android-28
-G "CodeBlocks - Unix Makefiles"
/home/gravlok/companyName/app_v2
Why do I need to run it 3 times in order to configure correctly?

jnius.find_javaclass , jnius.JavaException: Class not found 'android/webkit/WebView'

I am getting this error while trying to use jnius's autoclass to get the android webview inside Kivy app. What could be the fix?
Thanks
My code looks like this:
import kivy
from kivy.app import App
from kivy.lang import Builder
from kivy.utils import platform
from kivy.uix.widget import Widget
from kivy.clock import Clock
from jnius import autoclass
#from android.runnable import run_on_ui_thread
WebView = autoclass('android.webkit.WebView')
WebViewClient = autoclass('android.webkit.WebViewClient')
activity = autoclass('org.renpy.android.PythonActivity').mActivity
class Wv(Widget):
def __init__(self, **kwargs):
super(Wv, self).__init__(**kwargs)
# Clock.schedule_once(self.create_webview, 0) `
# #run_on_ui_thread
# def create_webview(self, *args):
# webview = WebView(activity)
# webview.getSettings().setJavaScriptEnabled(True)
# wvc = WebViewClient();
# webview.setWebViewClient(wvc);
# activity.setContentView(webview)
# webview.loadUrl('http://www.google.com')
class ServiceApp(App):
def build(self):
return Wv()
if __name__ == '__main__':
ServiceApp().run
First, you should check if you are running the code in an Android enviroment as the android.webkit.WebView runs only under Android and not on a PC under Windows or Linux.
Recommended is an Android device and run the app using Buildozer command buildozer android debug deploy run with debugging options.
In case, you want to run it on a PC then you can install the Android VM from Kivy and follow the following steps on https://kivy.org/docs/guide/packaging-android-vm.html:
Download the Kivy / Buildozer VM, in the Virtual Machine section. The
download is 1.2GB. Extract the file and remember the location of the
extracted directory.
Download the version of VirtualBox for your machine from the VirtualBox download area and install it.
Start VirtualBox, click on “File”, “Import Appliance”.
Select the extracted directory, file should be named “Buildozer VM.ovf”
Start the Virtual machine and click on the “Buildozer” icon.
and let in run in kivy.org/docs/guide/packaging-android-vm.html
If you still face the problem of a jnius.JavaException: Class not found 'android/webkit/WebView' exception, then you have to add that jar file to the classpath:
import os
os.environ['CLASSPATH'] = 'absolute/path/file.jar'
In case of the Class not found 'android/webkit/WebView' exception you could either locate the jar on your device or download it and add the path where you store it to the classpath.
Use Pydroid3 to test your apps without compiling them

Python: Detect Android

I'm surprised to find very little info on this topic. I want to detect if a user is running Android. I'm using:
platform.dist()
This perfectly detects all all OSs and different Linux distros. However, when running this on Android, it system name is always returned as "Linux". Is there any way to detect Android? I do not want to include any 3rd party modules, since this has to be as portable as possible. Is there maybe some specific Android function that I can call in a try-catch?
You can do this with Kivy
from kivy.utils import platform
if platform == 'android':
# do something
EDIT:
As I said, I cannot use any 3rd party libs
Take a look at how Kivy implemented it.
def _get_platform():
# On Android sys.platform returns 'linux2', so prefer to check the
# presence of python-for-android environment variables (ANDROID_ARGUMENT
# or ANDROID_PRIVATE).
if 'ANDROID_ARGUMENT' in environ:
return 'android'
from os import environ
if 'ANDROID_BOOTLOGO' in environ:
print("Android!")
else:
print("!Android")
System name is returned 'Linux' because Android is also based on the Linux Kernel.Android is the name of the distro but deep down its linux.
This is a very easy you can do it without kivy or with kivy module using hpcomt module
import hpcomt
os_name = hpcomt.Name()
print(os_name)
# Output
# Android
As of Python 3.7, you can check for the existence of sys.getandroidapilevel():
import sys
if hasattr(sys, 'getandroidapilevel'):
# Yes, this is Android
else:
# No, some other OS

SSL in Kivy for android development

I was developing a mobile app that can send mqtt messages to AWS Iot. It needs paho-mqtt library. It can be included in buildozer.spec requirements. My problem is in the SSL part, because I need to import SSL in the code which seems to have a problem with the python version running in python-for-android, which is 2.7.2.
The code is below, which works fine on the PC, but on th phone it is not working.
from kivy.lang import Builder
from kivy.app import App
from kivy.uix.label import Label
import paho.mqtt.publish as mqtt
import paho.mqtt.client as mqttclient
#
class MqttTest(App):
def build(self):
topic = "topic1"
my_ca_cert = "RootCA.pem"
my_pri_cert = "my.cert.pem"
my_key_cert = "my.private.key"
try:
import ssl
mqttc = mqttclient.Client("Python_Ex_Pub")
mqttc.tls_set(my_ca_cert,
certfile=my_pri_cert,
keyfile=my_key_cert,
cert_reqs=ssl.CERT_REQUIRED,
tls_version=ssl.PROTOCOL_TLSv1_2,
ciphers=None)
mqttc.connect("myaddress", 8883)
mqttc.publish(topic, "This is a test pub from Python.")
return Label(text="Hi it works!")
except Exception as e:
import traceback
a=traceback.format_exc()
try:
f1=open("/storage/emulated/0/Download/err.txt","w")
f1.write(str(a))
f1.close()
except:
pass
return Label(text=str (a))
if __name__ == '__main__':
MqttTest().run()
without adding anything related to SSL in buildozer.spec requirements, I get the following error:
no module named _ssl
If I added openssl as one of the requirements, then I get the following error:
'module' object has no attribute 'PROTOCOL_TLSv1_2'
As noted PROTOCOL_TLSv1_2 was added in later Python 2 version than p4a provides.
You can try to build apk with Python 3:
Change your buildozer's requirements line replacing python2 with python3crystax
Download and unpack crystax ndk here
Change your buildozer's android.ndk_path to point unpacked crystax ndk directory
Run buildozer android debug
If you're lucky enough you'll be able to build apk with Python 3 without any other actions.
I encountered the similar issue. I am trying to import pydrive without doing anything at first. Below is from logcat.
I tried 3 os environment, osx, ubuntu, bulldozer vm. All give me the same error.
I/python (13323): File "/Users/macuser/test/.buildozer/android/platform/build/dists/myapp/private/lib/python2.7/site-packages/httplib2/init.py", line 960, in
I/python (13323): AttributeError: 'module' object has no attribute 'HTTPSConnection'
In httplib.py, i found below code.
try:
import ssl
except ImportError:
pass
else:
class HTTPSConnection(HTTPConnection):
"This class allows communication via SSL."
So I suspect ssl not import successfully. Then, check ssl.py and found PROTOCOL_SSLv3 cannot import successfully.
from _ssl import PROTOCOL_SSLv3, PROTOCOL_SSLv23, PROTOCOL_TLSv1
Until here i am not able to proceed further. I am using python3crystax and it still not work as claim in this post.
Since buildozer will download all library separately, i suspect python automatically downloaded by bulldozer is a python version that doesn't include ssl3 support.

Building Python packages succeeds, but Scikit-learn is improperly built

I created an app using Scikit-learn and Kivy and it was built with Buildozer.
This is the code of main.py :
# coding=utf-8
import kivy
import sys
kivy.require('1.9.0')
from kivy.app import App
from kivy.uix.label import Label
class MyApp(App):
def build(self):
try:
from sklearn import svm, datasets
except:
return Label(text=str(sys.exc_info()[1]))
else:
return Label(text='Scikit-learn OK')
if __name__ == '__main__':
MyApp().run()
I specified Scikit-learn in the requirements in buildozer.spec :
[app]
title = Kivyris
package.name = kivyris
package.domain = org.test
source.dir = .
source.include_exts = py,png,jpg,kv,atlas
version = 0.1
requirements = kivy,numpy,scipy,scikit-learn
orientation = landscape
fullscreen = 1
log_level = 2
warn_on_root = 1
I ran buildozer android_new debug deploy run (no error, APK file created and deployed) but I have the following error when the app is launched :
Cannot load library:
Contents of /data/data/org.test.kivyris/files/app/lib/python2.7/site-packages/sklearn/check_build:
__init.pyo setup.pyo _check_build.so
It seems that sckikit-learn has not been built correctly.
If you have installed scikit-learn from source, please do not forget to build the package before using it; run python setup.py install or make in the source directory.
If you have used an installer, please check that it is suited for your Python version, your operating system and your platform.
On Windows and Ubuntu using python main.py it works well :
Scikit-Learn OK
I installed Scikit-learn using sudo apt-get install python-scikits-learn on Ubuntu 16.04 LTS. This is some informations from the device on which I ran the app :
import platform failed; platform.platform() : I couldn't get this info (app failed to launch), but it's an Android 5.1.1
import sys OK; sys.version : 2.7.2 (default, Mar 6 2017, 06:05:36) [GCC 4.8]
import numpy OK; Numpy.version : 1.9.2
import scipy OK; Scipy.version : 0.18.1
import sklearn : error (see above).
I tried a few things unsuccessfully, so I looked online :
https://unix.stackexchange.com/questions/240239/building-python-packages-succeeds-but-package-is-improperly-built/240260 : didn't work for me.
Import scikit in C# application : not really what I was looking for.
https://github.com/scikit-learn/scikit-learn/issues/433 : works for Mac, not for Ubuntu.
Cannot import Scikit-Learn : scipy works for me.
I didn't find anything useful and I have no idea how to solve that. Any idea please ?
Thank you.
When you're trying to build your app for Android with module that uses pure Python, everything is ok: this module would be run by Python interpreter built for Android and shipped with your app.
Things much worse when your trying to build app for Android with module contains C extension or dependencies with C extensions (such as Scikit-learn). On Windows or Linux C Extensions would be compiled using distutils, but for Android it's problem: C Extensions can't be compiled this way.
python-for-android handles this problem providing mechanism of recipes:
recipe: A recipe is a file that defines how to compile a requirement.
Any libraries that have a Python extension must have a recipe in p4a,
or compilation will fail. If there is no recipe for a requirement, it
will be downloaded using pip.
You can see list of existing recipes here. As you can see no one did one for Scikit-learn, so I assume it can't be build for Android right now.
You can try to create recipe for this module manually or ask someone to help in kivy Google group. Note, that it's probably not trivial task.

Categories

Resources