Buildozer compiles apk, but it crashes on android - android

I am able to build an .apk, but after I install it on my android phone it simply crashes at startup. My thoughts for failing is that I am using 3rd party libraries e.g(beautifulsoup).
This is how my imports look in main.py:
from kivy.app import App
from kivy.properties import ListProperty, StringProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.scrollview import ScrollView
from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition
import time, os, random, urllib2, re, cookielib as cj
from bs4 import BeautifulSoup as bs
from functools import partial
I'm running mavericks 10.9.3
Does it have something to do with buildozer.spec file?
I've tried adding BeautifulSoup to app requirements, but it doesn't change a thing.
Any help would be appreciated.

I ran into this problem as well, but I was (apparently) able to get everything working fine with a workaround. Since it doesn't look like you posted a logcat, I'll assume you ran into the same issue I did.
Yes, you do need to list beautifulsoup4 as a requirement in your spec. From looking into bs4's code, it looks like bs4 is willing to use any of several "builders." It supports HTMLParser, html5lib, or lxml. I have no idea why we can't load HTMLParser, but it's actually the least preferred library of the three, and if it weren't for the fact that there's no try block around the import, it seems that everything would work fine (as long as one of the other parsing libraries was available).
With this in mind, I included one of the other libraries, and I decided to hack the import process so that Python would pretend _htmlparser loaded okay :)
This article was instructive: http://xion.org.pl/2012/05/06/hacking-python-imports/
The end result was something like this:
import imp
import sys
class ImportBlocker(object):
def __init__(self, *args):
self.black_list = args
def find_module(self, name, path=None):
if name in self.black_list:
return self
return None
def load_module(self, name):
module = imp.new_module(name)
module.__all__ = [] # Necessary because of how bs4 inspects the module
return module
sys.meta_path = [ImportBlocker('bs4.builder._htmlparser')]
from bs4 import BeautifulSoup
I also added html5lib to the requirements in buildozer.spec.
Now, is this the right way to solve the problem? I don't know. The best approach would probably be to request that the author fix it. It might be as simple as to put the import in a try block. Nevertheless, this is the approach I've gone with for the moment, and it is at least an interesting exercise, and a way to test your app until a better fix comes along.
Additionally, I should warn you that I only did this recently, so I can't 100% guarantee that it won't cause any problems, but at a minimum, it's worked well enough to get my app running and scraping the particular website I was interested in. Good luck!

I understand that this question is some years old. I think the answer #Will gave was fantastic but unfortunately, in my case, I couldn't use it because html5lib was too slow for what I was making. So this is for reference; for anyone who absolutely must use the built-in parser. It's not pretty but it's fairly manageable.
The issue
After much investigation, I nailed down the cause of the problem. In the buildozer log, I noticed that there was a problem compiling the _htmlparser file with the log reading as follows (with my project path replaced with <project-path>):
Compiling <project-path>/.buildozer/android/platform/build/dists/mypackage/private/lib/python2.7/site-packages/bs4/builder/_htmlparser.py ...
SyntaxError: ("(unicode error) \\N escapes not supported (can't load unicodedata module)", ('<project-path>/.buildozer/android/platform/build/dists/mypackage/private/lib/python2.7/site-packages/bs4/builder/_htmlparser.py', 135, None, 'data = u"\\N{REPLACEMENT CHARACTER}"\n'))
Because it was failing to compile, it wasn't being included in the built apk file. So I looked at the file and at the line that was causing the problem: data = u"\N{REPLACEMENT CHARACTER}" which should actually be replaced by data = u"\ufffd"
Quick-fix
Now the easy way out would be to modify the file right in the package. You could just edit the file mentioned in the error above which should, in theory, work but it's not recommended because every time the package gets re-installed or the code gets built from another machine the problem will be back.
Marginally better fix
It would be nice if the fix was all packaged into our code, so drawing inspiration from #Will's answer, here is the code you would need to put in before your bs4 import:
import sys
class ImportFixer(object):
def __init__(self, mname):
self.mname = mname
def find_module(self, name, path=None):
if name == self.mname:
return self
return None
def load_module(self, name):
import _htmlparser as module
module.__name__ = name
return module
sys.meta_path = [ImportFixer('bs4.builder._htmlparser')]
from bs4 import BeautifulSoup
The main difference is that you need to copy the _htmlparser.py file from the bs4 package to your current directory and fix the before mentioned line data = u"\N{REPLACEMENT CHARACTER}" with data = u"\ufffd"
Then, every time the bs4 module is imported, the import is magically intercepted and the local file is used.
Warning: if you ever update Beautiful Soup, you may need to use a more recent _htmlparser.py file in your project with the same fix made as needed.
Final comment
If I made some typos, grammar mistakes or didn't make sense at all, keep in mind that I worked most of the weekend to fix this and quite frankly I'm not thinking straight. Comment if you have any questions and I'll reply and/or edit my answer when I get a chance.

Related

Is it possible to do Android development using the R programming language?

I'm doing digital signal processing work in R using R Studio and think it would be a neat idea to expand this into an Android app. I know people have used Kivy to develop Android Apps in Python and I know it's possible to wrap R code in Python, so is this feasible? I read that it's possible to run R code on Android, just not sure if adding it to my project is a possibility. I also don't need to have the entire app to be written in R. Are there any examples/documentation related to this?
Turns out you can! I've built a more complex app using Kivy and the rpy2 package, but I'll demonstrate a simple example here.
You can define your R code like so:
r_code.py
from rpy2.robjects.packages import SignatureTranslatedAnonymousPackage
r_code = """
add <- function(x, y) {
return(x + y)
}
"""
r_lib = SignatureTranslatedAnonymousPackage(r_code, "r_lib")
Your Kivy App will look something like this:
main.py
from kivy.app import App
from r_code import r_lib
class MainApp(App):
def build(self):
window = BoxLayout(orientation='vertical')
label = Label(text=str(r_lib.add(3, 4)[0]))
b.add_widget(label)
return b
if __name__ == "__main__":
MainApp().run()
And if you want to package this into an Android App, you can follow the directions from the official doc. If you run into an error with rpy2, you might have to install the correct version for your system from here.
Happy coding!

RAPT - android module's check_pause, accelerometer, etc. does not work

I know people have asked questions along these lines here, but I haven't found an answer that helps me.
I've made a simple game in pygame, I'm using RAPT to port it to android. I've gotten the game to compile and run successfully on my tablet. Great.
It goes something like this:
import android
android.init()
import pygame
pygame.init()
def main():
#does stuff for the game
The standard stuff.
But as soon as I add these lines below the import android:
enable = True
android.accelerometer_enable(enable)
or
android.check_pause()
or anything in the android module other than init(), the game refuses to run. ADB logcat shows that the code throws an AttributeError: 'module' object has no attribute of (whatever function I tried)
I've even tried copy-pasting code from https://github.com/codetricity/accel/blob/master/main.py
and trying to compile the example code given from the RAPT download page...
https://github.com/renpytom/rapt-pygame-example/blob/master/main.py
and it returns the same kind of errors once compiled.
What's going on?
In RAPT, accelerometer is seen as a 3-axis joystick. So you can use the pygame joystick module.
pygame.joystick.init()
num_joy_presenti = pygame.joystick.get_count()
accel = None
for i in range(num_joy_presenti):
generico_joy = pygame.joystick.Joystick(i)
generico_joy.init()
if generico_joy.get_name() == "Android Accelerometer":
accel = pygame.joystick.Joystick(i)
accel.init()
Then, "accel" will be your accelerometer and you can use the joystick module to get its values. Documentation here http://www.pygame.org/docs/ref/joystick.html#pygame.joystick.Joystick
Instead of android.check_pause you can detect pygame.APP_WILLENTERBACKGROUND and pygame.APP_WILLENTERFOREGROUND events as in the example at https://github.com/renpytom/rapt-pygame-example/blob/master/main.py

back button functionality on android with kivy

in Kivy, when I press the back button on my android device it throws me out of the application. is there a way to return back to the previous screen using the Kivy language and not python? this is what I have written in kivy:
<MyAppClass>:
AnchorLayout:
anchor_x : 'center'
anchor_y : 'top'
ScreenManager:
size_hint : 1, .9
id: _screen_manager
Screen:
name:'screen1'
StackLayout:
# irrelevant code
Screen:
name:'screen2'
StackLayout:
# irrelevant code
I need to manipulate the screen manager and its screens from python... if I can do so I will be ok with python.
Kivy on android binds the back button to the esc button so binding and listening to esc button in your app would help you handle how your app behaves when the back button is pressed.
In other words in your app when testing it on your desktop listen to the escape key from the system keyboard, this will be automatically be translated to being the back button on your android device. Something like::
def on_start():
from kivy.base import EventLoop
EventLoop.window.bind(on_keyboard=self.hook_keyboard)
def hook_keyboard(self, window, key, *largs):
if key == 27:
# do what you want, return True for stopping the propagation
return True
i guess that i have solved it but should thank both #inclement and #qua-non! your answers guys led me to the right way! so in kv i assume that i gave an id to my screen manager (please refer to my question where i have written the kv code) , in python i should do the following:
from kivy.core.window import Window
from kivy.properties import ObjectProperty
class MyAppClass(FloatLayout):#its a FloatLayout in my case
_screen_manager=ObjectProperty(None)
def __init__(self,**kwargs):
super(MyAppClass,self).__init__(**kwargs)
#code goes here and add:
Window.bind(on_keyboard=self.Android_back_click)
def Android_back_click(self,window,key,*largs):
if key == 27:
self._scree_manager.current='screen1'#you can create a method here to cache in a list the number of screens and then pop the last visited screen.
return True
class MyApp(App):
def build(self):
return MyAppClass()
if __name__=='__main__':
MyApp().run()
This is certainly possible. Here's a short example app with the method I use to do this:
from kivy.utils import platform
from kivy.core.window import Window
class ExampleApp(App):
manager = ObjectProperty()
def build(self):
sm = MyScreenManager()
self.manager = sm
self.bind(on_start=self.post_build_init)
return sm
def post_build_init(self, *args):
if platform() == 'android':
import android
android.map_key(android.KEYCODE_BACK, 1001)
win = Window
win.bind(on_keyboard=self.my_key_handler)
def my_key_handler(self, window, keycode1, keycode2, text, modifiers):
if keycode1 in [27, 1001]:
self.manager.go_back()
return True
return False
This should give the right basic idea, but a few notes:
ScreenManager doesn't keep track of the previous screens, it's up to you to implement this how you like. My example assumes you defined a class MyScreenManager with a go_back method.
It might not be necessary to bind to on_start and run post_build_init, this is just how the example I originally used did it (see below). It might be important sometimes though, possibly if the window is not initialised when build() is run, and the original mailing list post suggests the author needed it for some reason.
The example listens for keycodes 27 or 1001. As qua-non said while I was writing this, the former listens for esc, so you can get the same behaviour on desktop.
I didn't try without the android.map_key line, but it seems like it may not be necessary.
You mention you want to use kivy language and not python. You need to do some python to get this result, and I don't see a way around that (it's not really the domain of the kv language). I guess you could shift some stuff to kv by defining a 'go_back' event somewhere and triggering this when the key is pressed, along with binding your screenmanager to watch that event, but it seems like a long way around.
I based my code on the mailing list thread at https://groups.google.com/forum/#!topic/kivy-users/7rOZGMMIFXI . There might be a better way, but this is quite functional.
Now all the way in 2020 I'm using:
Clock.schedule_once(lambda x: Window.bind(on_keyboard=self.hook_keyboard))
in combination with a similar hook_keyboard method to the other answers, to delay the bind in my build method. Works fine, but none of these other ways ways seemed to work for me anymore.

Accessing Android camera through Kivy

Please i am looking for a work around to get access Android camera through kivy, or a library that i can integrate with kivy in order to access the Camera.
I am developing an application for android but using python-kivy for the UI,
anything will be really appreciated,
thanks alot.
Here's my sample code, that works on Android. Just import that file https://github.com/kivy/plyer/blob/master/plyer/platforms/android/camera.py
Also, don't forget to add CAMERA permissions to manifest.
main.py:
__version__ = '1.0'
import kivy
# importing file from https://github.com/kivy/plyer/blob/master/plyer/platforms/android/camera.py
# I downloaded it and saved it in the same directory:
from camera import AndroidCamera
from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ObjectProperty, StringProperty
import base64
class MyCamera(AndroidCamera):
pass
class BoxLayoutW(BoxLayout):
my_camera = ObjectProperty(None)
# /sdcard means internal mobile storage for that case:
image_path = StringProperty('/sdcard/my_test_photo.png')
def __init__(self, **kwargs):
super(BoxLayoutW, self).__init__()
self.my_camera = MyCamera()
def take_shot(self):
self.my_camera._take_picture(self.on_success_shot, self.image_path)
def on_success_shot(self, loaded_image_path):
# converting saved image to a base64 string:
image_str = self.image_convert_base64
return True
#converting image to a base64, if you want to send it, for example, via POST:
def image_convert_base64(self):
with open(self.image_path, "rb") as image_file:
encoded_string = base64.b64encode(image_file.read())
if not encoded_string:
encoded_string = ''
return encoded_string
if __name__ == '__main__':
class CameraApp(App):
def build(self):
main_window = BoxLayoutW()
return main_window
CameraApp().run()
camera.kv:
<BoxLayoutW>:
Button:
text: 'shot'
on_release: root.take_shot()
Kivy has some native support for calling the camera. Check out this page from the new programming guide for a core provider or this page from the new programming guide for a uix widget. In theory, the core should be able to adapt between platforms and the widget should then be able to use the camera.
This links to a discution where a custom implementation can be found. It is based on PyJNIus's automatic wrapping of the android API's Camera class.
Didn't try myself but you can give it a try...
thanks to this post i was able to solve a critical problem in my app thanks a lot guys here is my code that i used i hope that you guys can use it somewhere.
I made a screen and used the plyer camera function
from os import getcwd
from os.path import exists
from os.path import splitext
import kivy
kivy.require('1.8.0')
from kivy.app import App
from kivy.properties import ObjectProperty
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.popup import Popup
from kivy.logger import Logger
from plyer import camera
i also used some other imports for the screens and the labels and popups etc which you can definatly look into depending upon your requirment
class ScreenFive(Screen): #camera screen
def gg1back(self):
self.parent.current = 'First'
def do_capture(self):
filepath = 'IMG_1.jpg'
ext = splitext(filepath)[-1].lower()
try:
camera.take_picture(self.camera_callback,filepath)
except NotImplementedError:
popup = MsgPopup(
"The Face_rec_image feature has not yet \n been implemented for this platform :(")
popup.open()
def camera_callback(self, filepath):
if(exists(filepath)):
popup = MsgPopup("Picture saved!")
popup.open()
else:
popup = MsgPopup("Could not save your picture!")
popup.open()
As it was hard for me to find the answer how to use camera on android I thought I'll share my journey to the answer to save next person's time.
I couldn't find the way to make work Camera class straight from Kivy:
https://kivy.org/docs/examples/gen__camera__main__py.html
finally I found the solution posted above, and after wasting some time implementing it in my app it turned out it was impossible for me to return to the app after photo being taken - the app was terminated, so I couldn't go back to the app to make use of the picture (I was using Kivy Launcher).
Just recently I found out this way of accessing Camera was abandoned (https://github.com/kivy/plyer/issues/16#issuecomment-54094174 )
But then I found the solution below and by just running the example code it looks like I will be able to get results I want (it just needs a little tweaking not to crash when android camera is canceled/no photo has been taken)
https://github.com/kivy/kivy/tree/master/examples/android/takepicture
EDIT:
appears my app was terminated because I didn't implement on_pause: return True in topmost widget. Nevertheless the text above still might be helpful
Some years later, the Android API has changed as to how it deals with permissions and storage providers.
I have a full working example for the Android camera through Kivy here. It basically requires some tweaking of the compiled manifest file in python-for-android, as well as working directly with the FileProvider.

Phonegap Local Notification Plugin for Android

I'm working on a "Reminders" application on Android using Phonegap[Cordova 2.2].
The user enters a specific date for his reminder and I'm supposed to notify him on time.
I used this plugin to Just show a notification in the status bar & they are working fine.
But I want the notifications to show at specific times. Is there some method to do it ?
I found this plugin that's supposed to do what I want but it's not working, it shows errors at :
import com.phonegap.api.Plugin;
import com.phonegap.api.PluginResult;
The import com.phonegap.api.Plugin cannot be resolved
So, how can I fix this error ? I know it might be easy, but I never made native Android Apps before so I'm kind of confused.
Thanks
Looks like a difference between 2.0.0 and 2.2.0, and like the plugin needs updating.
import org.apache.cordova.api.CordovaPlugin;
import org.apache.cordova.api.PluginResult;
This should give you a jumping off point:
http://docs.phonegap.com/en/2.2.0/guide_plugin-development_android_index.md.html#Developing%20a%20Plugin%20on%20Android
For the new Phonegap Release you must also change some stuff:
The LocalNotification class must "extends CordovaPlugin" now
Import classes like eomer says
The execute method of LocalNotification.java must return a boolean now
Change all return arguments that are affected (from PluginResult) to boolean of your choice
Get the context in a new way ctx = this.cordova.getActivity(); and give it the ...AlarmHelper(ctx)

Categories

Resources