test

Test

>>> a = 1
>>> b = 2
>>> print(a)
1
>>> print(b)
2
>>> print(a+b)
3
>>> print(b-a)
1
>>> print(a<b)
True

Advertisement

Permission problems with Kivy/Android

Kivy is not very helpful in helping you track down permission problems.  If everything else seems working fine, make sure that you have the right Android permissions to access the relevant Android services/hardware.  This is the log file for not having the CAMERA permission:

I/python  (11009):    File “[]/android_zbar_qrcode_master/.buildozer/android/app/main.py”, line 170, in start
I/python  (11009):    File “jnius_export_class.pxi”, line 830, in jnius.jnius.JavaMultipleMethod.__call__ (jnius/jnius.c:21088)
I/python  (11009):    File “jnius_export_class.pxi”, line 561, in jnius.jnius.JavaMethod.__call__ (jnius/jnius.c:17854)
I/python  (11009):    File “jnius_export_class.pxi”, line 727, in jnius.jnius.JavaMethod.call_staticmethod (jnius/jnius.c:19696)
I/python  (11009):    File “jnius_utils.pxi”, line 43, in jnius.jnius.check_exception (jnius/jnius.c:3233)
I/python  (11009):  JavaException: JVM exception occured

Which I experienced when, last week, I added the INTERNET permission to the spec file, but mistyped a . for a ,

I forgot about it and came back to it today and nothing worked – despite me not being able to see any substantive difference between my current py file and the known good one.

Deploying Web2py apps to Google App Engine (GAE)

Last time I tried to deploy a web2py app to GAE was (apparently) years ago now.  I remember it being pretty easy at the time, so I figured I could get it up and running pretty quickly yesterday.  Not so 😦

There are a couple of tricks to it.

The process is (install the app engine sdk and copy of web2py):

1. create your application on your app engine developer Application Overview page

2. create an application skeleton using the web2py dashboard.

3. cp  examples/appengine_config.example.py ./app.yaml

Ie make a copy in the root directory of your web2py directory tree.  This is main trick #1.

4. Edit app.yaml to refer to the id that you got from creating the app on app engine (ie not the local web2py)

5. cp handlers/gaehandler.py .
So, copy the gaehandler to the root directory of web2py. This is main trick #2.

Then you should be ready to roll*

* use <path>/dev_appserver.py web2py to test,

use <path>/appcfg.py update  web2py to deploy

The app will be available from <the Google app id you registered>.appspot.com/<the local web2py name of your application>/

 

Resetting a Text Input (Kivy)

I have written an app which takes a barcode scan from a book, checks whether it is already in my book catalog[ue] and then flashes green or red depending on whether it is in or not.  I have a lot of books to catalog (about 1600) and about 90% of them are already done but I don’t know which 10%.  So I need to sort out that 10% and process them.   I scan the ISBN using a cuecat barcode reader into a Kivy textinput then check it against my list of ISBNs dumped from my existing database (maintained using Readerware).  I want to scan one book after another and separate them into two (ish) piles depending on whether it needs to be entered in the database. The Kivy textinput defaults to multiline.  In that mode you need to press a separate button to invoke a callback.  If multiline is set to False, each “enter” received will call a callback (on_text_validate).  So that means I can scan, check the book and scan again…

…or it would mean that if Kivy didn’t defocus the textinput widget whenever it calls on_text_validate.  After each can the widget needs to be refocussed. The widget can be refocussed by  setting it’s .focus attribute to True.  The trick however is to make sure that happens _after_ Kivy has defocussed the widget.  To do that you need to schedule a callback to occur after the next Kivy “frame”:

        Clock.schedule_once(self._refocus_text_input, 0)
        #from kivy.clock import Clock

    def _refocus_text_input(self,  arg1):
        self.scan_entry.focus=True

Awesome.  Now I don’t have to tap the widget to reset it after each scan.

Fetching a scan from ZXing on Android (Kivy)

Did a bit of mucking around to fetch a scan from ZXing using an intent.  Turned out to be more trouble than it was worth unfortunately because ZXing takes a long time to recognise a barcode. Intent code is:

        Intent = autoclass('android.content.Intent')
        intent = Intent()
        intent.setAction("com.google.zxing.client.android.SCAN")
        Logger.debug("build: about to start activity for result")
        PythonActivity.mActivity.startActivityForResult(intent, 0x123)

It goes without saying that you need to have ZXing already installed on the device. Apparently it will also work with other scanners, but I haven’t tried.

You also need to have a callback to handle the returned result:

    def on_activity_result(self, requestCode, resultCode, data):
        isbn = data.getStringExtra("SCAN_RESULT")
# do other stuff...

And this callback must first be bound:

    activity.bind(on_new_intent=self.on_new_intent,on_activity_result=self.on_activity_result)
# earlier: from android import activity

Kivy – Problems with on_new_intent in Apps

In an earlier post I wrote about the wonderful on_new_intent hook which allows your application to receive intents from other apps on an Android device.  When the other app sends an intent your app will process it in whatever callback you bound to on_new_intent (which I will call on_new_intent() for the sake of simplicity).  To receive an intent from another app will probably mean that the user is actively using that other app.  That means that your app has been paused.  So, when Android passes the intent to your app it’s going to want to on_resume your app.   Can you see where this is going?

The problem with using on_new_intent in an app is that on_resume() and on_new_intent() are both fired at more or less the same time.  There is no guarantee that one of them will be called first and no guarantee that the one called first will return before the second one is called (in my experiments they can fire in either order and can fire in the middle of the other).  In other contexts  this might not be a problem.  Here it is.  The purpose of on_resume is to reinitialise the App state to where it was as at the last call to on_pause().  The purpose of on_new_intent is to initialise or update the App state based on the data provided by the intent.   This is not a problem for services because services are running in the background and on_resume need not be called.

The future solution is that I’ve logged an issue.  My current solution is a hackish workaround – delay the execution of on_new_intent to give on_resume a chance to start, and use flags for on_new_intent to wait for it to finish.  If the timing is bad, this will fail, but seems to be ok in practice so far (ie last 24 hours).

Sample code (init the attributes to False when you instantiate the app too btw):

class GridderApp(App):
#[stuff deleted]
    def on_resume(self):
        if self.on_new_intenting: # let it run, don't bother about initialising to saved
            return
        self.resuming = True
        self.uiwidget.load(path=".",   filename=PAUSE_FILE_NAME,  store_save= False)
        self.resuming = False

    def on_new_intent(self, intent):
        self.on_new_intenting = True
        sleep(0.25) # give on_resume a chance to fire
        # if it fires after on_new_intent has finished, it will reinitialise to the saved state
        # and we want to avoid that!
        while self.resuming:
            sleep(0.1)

        intent_data = intent.getData()
        try:
            file_uri= intent_data.toString()
        except AttributeError:
            file_uri = None
        if file_uri is None:
            return
        else:
            if file_uri[:7]=="file://":
                self.uiwidget.load(path="",  filename=url2pathname(file_uri[7:]))
            else:
                return
        self.on_new_intenting= False

Kivy – Receiving Android Intents

In theory Kivy has a way to receive intents generated from other apps on Android – it’s the on_new_intent callback hook.  To use it you first need to write a method to handle the intent.  The method will be passed the intent, but you’re going to have to unpack data from it.  What data is to be unpacked and how you go about it will be different depending on the intent.  You’ll need to dig into the Android Java docs/stack exchange to get details.  This is a sample unpacking of a file uri which has been provided by a file manager app.  The file uri is not in a format that Kivy can use so it’s got to first be unescaped (here I use url2pathname from urllib2):

class GridderApp(App):
# [stuff deleted]
    def on_new_intent(self, intent):
        intent_data = intent.getData()
        try:
            file_uri= intent_data.toString() # isn't Java awesome?
        except AttributeError:
            file_uri = None
        if file_uri is None:
            return # give up
        else:
            if file_uri[:7]=="file://":
                self.uiwidget.load(path="",  filename=url2pathname(file_uri[7:]))
            else:
                return # again, give up

That’d all be lovely if that’s how it actually worked. Unfortunately on_new_intent seems to be designed on the assumption that it’s being run as the method of a Service not an app. This code won’t work for an app for reasons to be covered in a later post.

After you’ve defined this method you need to bind it:

if __name__ == "__main__":
    if platform=="android":
        import android.activity
        app = GridderApp(file_uri=file_uri)
        android.activity.bind(on_new_intent=app.on_new_intent)

After which, you’re good to go! Except that apps suffer from a problem with receiving intents – see my next post.

Apparently (as at July 2014) on_new_intent does not work on the application’s first start, so you can’t get (eg) a file uri from an intent if your app has not been run before or has been run, but stopped.  I’m told this is a bug so it may be fixed in the future.

 

Kivy – Android Life Cycle

If your app is a stand alone app which doesn’t interact with the rest of the Android environment, then you don’t have to worry about life cycles.  For everyone else, welcome to the Android way of doing things.  The Android life cycle reflects a design decision by the Android developers that any app needs to be able to be interrupted at any time without warning for no reason – originally I think this was in order to receive phone calls and/or account for low phone resources.  However, it still applies to devices which don’t even have a cell phone radio.

Android deals with this primarily through calling on_pause and on_resume methods on apps (actually onPause and onResume in Java).  If the app wants to do something there (eg save/restore current data!), well and good.  Android calls on_pause() when it returns Android suspends the app until someone brings the app to the foreground again, at which time on_resume() is called.   The Android lifecycle is actually more complicated than this but let’s not sweat the details.

Kivy allows you to implement the on_pause and on_resume simply by adding these methods to your app instance. Some sample code:

class GridderApp(App):
# stuff deleted

    def on_pause(self):
        self.uiwidget.save(path =".", filename=PAUSE_FILE_NAME)
        return True  # app sleeps until resume return False to stop the app 

    def on_stop(self):
        pass

    def on_resume(self):
        self.uiwidget.load(path=".",   filename=PAUSE_FILE_NAME,  store_save= False)

 

 

Kivy Android Intent Filters

Intent filters are Android’s equivalent of file associations.  When you click on a file in a file manager on Android it starts the associated application by using an intent.  Applications can support the emission of intents with different categories.  Other apps which have registered to receive intents of that category can then receive and process them.

In order to make use of these intents from Kivy/buildozer you need to add an intent-filter to your a file (by default called intent_filters.xml) in your app’s root directory.  You also need to edit the buildozer.spec file to point to the file (look for this line and uncomment):

# (str) XML file to include as an intent filters in <activity> tag
android.manifest.intent_filters=intent_filters.xml

The intent filter will differ depending on the intent.  Intents based on mimetypes are better than those based on paths.  Apparently there’s a problem with Android’s file search algorithm so you need to specify every relevant directory level if you want to filter on the file’s extension (seriously!).  Sample intent (from my art gridder app, which uses files with a .bag extension:

<intent-filter>
<action android:name=”android.intent.action.VIEW” />
<category android:name=”android.intent.category.DEFAULT” />
<data android:mimeType=”*/*” />
<data android:scheme=”file” />
<data android:host=”*” />
<data android:port=”*” />
<data android:pathPattern=”.*..*..*..*..*.bag” />
<data android:pathPattern=”.*..*..*..*.bag” />
<data android:pathPattern=”.*..*..*.bag” />
<data android:pathPattern=”.*..*.bag” />
<data android:pathPattern=”.*.bag” />
</intent-filter>

Finally, your app needs to be able to receive and extract the intent (don’t expect to find this in the docs!):

if __name__ == "__main__":
    if platform=="android":
        from jnius import cast
        from jnius import autoclass
        import android
        import android.activity

        # test for an intent passed to us
        PythonActivity = autoclass('org.renpy.android.PythonActivity')
        activity = PythonActivity.mActivity
        intent = activity.getIntent()
        intent_data = intent.getData()
        try:
            file_uri= intent_data.toString()
        except AttributeError:
            file_uri = None

Then pass the file uri to the app when you instantiate it.

Kivy – Basics

To create a Kivy application you:

  • create a file called main.py as your entry point
  • subclass from kivy.app.App
  • include a build() method
  • make sure the build method returns a widget (in this case a Label)
  • instantiate an instance of the subclass
  • invoke the run() method of that instance

Example, from the kivy site:

import kivy
kivy.require('1.0.6') # replace with your current kivy version !

from kivy.app import App
from kivy.uix.label import Label

class MyApp(App):

    def build(self):
        return Label(text='Hello world')

if __name__ == '__main__':
    MyApp().run()

Then run python main.py…