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…

Put your child’s work on line with Web2py and Google Apps

A fantastic way to show off your child’s work is to load it up online.  There are plenty of Python web frameworks out there.  The one I use is Web2py – basically because the first time I went looking for a framework I was able to download it and get it up and running in under 5 minutes (in fact, probably less).

If you also sign up for an account with Google (at the time I think I could do it for free or for $25 or something) you can upload the framework with your app onto Google’s cloud servers.

Here is a very basic app I put up a long time ago, off the back of the silly sentences tutorial.  In fact, so long ago I’ve lost my login details :’-(

Hello world!

Welcome to Python4Dads (and other responsible adults).  The primary motivation of this blog is as a companion to Python4Kids, covering aspects which are either too difficult, or which I don’t want to go to as much trouble explaining.  Python4Dads is also intended to cover additional projects which are complementary to Python4Kids.  Finally, this blog will also be a dumping ground for notes to self on Python stuff.