ActivityResultListener on Android

930 views
Skip to first unread message

Hubert Soyer

unread,
Feb 21, 2014, 4:03:52 PM2/21/14
to kivy-...@googlegroups.com
Hi!

I would like to let users of my Android app choose photos with the native Android gallery application.
To do that, I have to create an ACTION_PICK intent and launch an action.
That seems to work fine and the gallery gets launched. 
But to finally receive a URI for the location of the selected image, I need to register an ActivityResultListener.
That is an interface that forces me to implement the onActivityResult method which is called as soon as the user has selected an image in the gallery.

There seems to be support for this functionality in pyjnius already: PythonActivity$ActivityResultListener

So I wrote the following code to test this functionality.
The following code should invoke the android gallery and register an ActivityResultListener which displays the path of the selected picture in a popup.

from kivy.app import App

from kivy.uix.scatter import Scatter
from kivy.uix.label import Label
from kivy.uix.floatlayout import FloatLayout
from jnius import cast
from jnius import autoclass
from kivy.uix.popup import Popup

class TutorialApp(App):
    def build(self):
        f = FloatLayout()
        
        ActivityResultListener = autoclass('org.renpy.android.PythonActivity$ActivityResultListener')

        class ResultListener(ActivityResultListener):
            def onActivityResult(self, requestCode, resultCode, data):
                popup = Popup(title='Test popup', content=Label(text='Hello world'),
                  auto_dismiss=True)
                popup.open()

        PythonActivity = autoclass('org.renpy.android.PythonActivity')
        Intent = autoclass('android.content.Intent')

        intent = Intent()
        intent.setAction(Intent.ACTION_PICK)
        intent.setType("image/*")
        current_activity = cast('android.app.Activity', PythonActivity.mActivity)
        current_activity.startActivity(intent)
        PythonActivity.registerActivityResultListener(ResultListener())
        
        return f

if __name__ == "__main__":
    TutorialApp().run()

When I deploy this to Android and run it, I get

I/python  ( 7886):  Traceback (most recent call last):
I/python  ( 7886):    File "/home/ogh/tmp/apptest/.buildozer/android/app/main.py", line 37, in <module>
I/python  ( 7886):    File "/home/ogh/tmp/apptest/.buildozer/android/platform/python-for-android/build/python-install/lib/python2.7/site-packages/kivy/app.py", line 766, in run
I/python  ( 7886):    File "/home/ogh/tmp/apptest/.buildozer/android/app/main.py", line 18, in build
I/python  ( 7886):    File "jnius_export_class.pxi", line 36, in jnius.jnius.MetaJavaClass.__new__ (jnius/jnius.c:11778)
I/python  ( 7886):    File "jnius_export_class.pxi", line 49, in jnius.jnius.MetaJavaClass.resolve_class (jnius/jnius.c:12049)
I/python  ( 7886):  jnius.jnius.JavaException: __javaclass__ definition missing
I/python  ( 7886): Python for android ended.

I think the line saying "jnius.jnius.JavaException: __javaclass__ definition missing" is the important one.
How can I implement this interface?

Any ideas?

Ben Rousch

unread,
Feb 21, 2014, 4:11:48 PM2/21/14
to kivy-...@googlegroups.com
I think pyjnius cannot subclass a Java class yet.


--
You received this message because you are subscribed to the Google Groups "Kivy users support" group.
To unsubscribe from this group and stop receiving emails from it, send an email to kivy-users+...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.



--
 Ben Rousch
   bro...@gmail.com
   http://clusterbleep.net/

Hubert Soyer

unread,
Feb 21, 2014, 4:30:43 PM2/21/14
to kivy-...@googlegroups.com
Thank you for your reply.
That was what I thought at first, too.

But the API documentation for the Android Activity clearly features a registerActivityResultListener method and a ActivityResultListener class.
Sorry for forgetting to mention it before.

Here is a link
http://python-for-android.readthedocs.org/en/latest/javaapi/#activity

Hubert Soyer

unread,
Feb 23, 2014, 11:18:05 AM2/23/14
to kivy-...@googlegroups.com
I dug a little deeper and I think I found out how to launch an activity and register a result listener.
The app does not crash anymore, but unfortunately the result listener callback does not get called for some reason.

Here is my code, if anyone wants to test it, simply put it into a file and run buildozer.
It launches the android gallery and after the user selects a picture the on_activity_result 
method should get called and show a pop up and write to the android log.
The gallery is shown, however the on_activity_result_method never gets called.

__version__='0.1'

from kivy.app import App

from kivy.uix.scatter import Scatter
from kivy.uix.label import Label
from kivy.uix.floatlayout import FloatLayout
from jnius import cast
from jnius import autoclass
from kivy.uix.popup import Popup
from android import activity

class TutorialApp(App):
    def build(self):
        f = FloatLayout()
        

        PythonActivity = autoclass('org.renpy.android.PythonActivity')
        Intent = autoclass('android.content.Intent')

        intent = Intent()
        intent.setAction(Intent.ACTION_PICK)
        intent.setType("image/*")
        current_activity = cast('android.app.Activity', PythonActivity.mActivity)
        current_activity.startActivity(intent)
        activity.bind(on_activity_result=self.on_activity_result)
        
        return f
    
    def on_activity_result(self, requestCode, resultCode, data):
        print "### ACTIVITY CALLBACK ###"
        popup = Popup(title='Test popup', content=Label(text='Hello world'),
                  auto_dismiss=True)
        popup.open()
        

if __name__ == "__main__":
    TutorialApp().run()

This is part of the adb logcat log. I think the gallery part works out fine.

I/ActivityManager(  588): Displayed android/com.android.internal.app.ResolverActivity: +289ms
I/PackageManager(  588):   Action: "android.intent.action.PICK"
I/PackageManager(  588):   Category: "android.intent.category.DEFAULT"
I/PackageManager(  588):   Type: "image"
I/PackageManager(  588):   mPriority=0, mHasPartialTypes=true
I/PackageManager(  588): Adding preferred activity ComponentInfo{com.google.android.gallery3d/com.android.gallery3d.app.GalleryActivity} for user 0 :

This is the android activity class:
I am calling the bind method from there.

Does anyone have an idea what I am doing wrong?
I have been trying to get this working for 2 days now and am getting quite desperate so any hint would be appreciated.

Hubert Soyer

unread,
Feb 24, 2014, 5:41:42 AM2/24/14
to kivy-...@googlegroups.com
I tried registering an on_new_intent listener as well.
The following code shows how I did it. The on_new_intent method is never called, probably the same problem as with on_activity_result.

__version__='0.1'

from kivy.app import App

from kivy.uix.floatlayout import FloatLayout
from jnius import cast
from jnius import autoclass
from android import activity

class TutorialApp(App):
    def build(self):
        f = FloatLayout()
        
        activity.bind(on_new_intent=self.on_new_intent,on_activity_result=self.on_activity_result)

        PythonActivity = autoclass('org.renpy.android.PythonActivity')
        Intent = autoclass('android.content.Intent')

        intent = Intent()
        intent.setAction(Intent.ACTION_PICK)
        intent.setType("image/*")
        current_activity = cast('android.app.Activity', PythonActivity.mActivity)
        current_activity.startActivity(intent)
        
        return f
    
    def on_activity_result(self, requestCode, resultCode, data):
        print "### ACTIVITY CALLBACK ###"
    
    def on_new_intent(self, intent):
        print "#### INTENT", intent
        

if __name__ == "__main__":
    TutorialApp().run()



Here is the Python for Android documentation for the method that I use to bind register the callback:


I can't figure out what I am doing wrong. Is there any way to debug this properly?

Hubert Soyer

unread,
Feb 26, 2014, 2:39:28 AM2/26/14
to kivy-...@googlegroups.com
I will post this over at the python for android mailing list, I think this is a python for android problem rather than a kivy problem.
If I can resolve the issue I will report back here.

Hubert Soyer

unread,
Feb 26, 2014, 12:41:52 PM2/26/14
to kivy-...@googlegroups.com
I solved it! This feeling when you fix something after thinking about it for a long time.

Instead of 
current_activity = cast('android.app.Activity', PythonActivity.mActivity)
current_activity.startActivity(intent)

I had to use the mActivity dirctly with startActivityForResult

PythonActivity.mActivity.startActivityForResult(intent, 0x123)

without the startActivityForResult it does not work.

Stupid mistake in hindsight.
Here is the full working code

__version__='0.1'

from kivy.app import App

from kivy.uix.floatlayout import FloatLayout
from jnius import cast
from jnius import autoclass
from android import activity

class TutorialApp(App):
    def build(self):
        f = FloatLayout()
        
        activity.bind(on_new_intent=self.on_new_intent,on_activity_result=self.on_activity_result)

        PythonActivity = autoclass('org.renpy.android.PythonActivity')
        Intent = autoclass('android.content.Intent')

        intent = Intent()
        intent.setAction(Intent.ACTION_PICK)
        intent.setType("image/*")
        PythonActivity.mActivity.startActivityForResult(intent, 0x123)
        
        return f
    
    def on_activity_result(self, requestCode, resultCode, data):
        print "### ACTIVITY CALLBACK ###"
    
    def on_new_intent(self, intent):
        print "#### INTENT", intent
        

Arlo

unread,
Jun 5, 2015, 1:19:10 AM6/5/15
to kivy-...@googlegroups.com
You need a lot more code to actually get the file path. Also, this is asynchronous, so I used Kivy's Clock with a callback. Here's my code after consulting various documentation. Links to documentation are in Python comments.



from kivy.logger import Logger
from kivy.clock import Clock

from jnius import autoclass
from jnius import cast

# python-for-android provides this
from android import activity


PythonActivity = autoclass('org.renpy.android.PythonActivity')
Intent = autoclass('android.content.Intent')
Uri = autoclass('android.net.Uri')

# Value of MediaStore.Images.Media.DATA
MediaStore_Images_Media_DATA = "_data"

# Custom request codes
RESULT_LOAD_IMAGE = 1

Activity = autoclass('android.app.Activity')
# Activity is only used to get these codes. Could just hardcode them.
# /** Standard activity result: operation canceled. */
# public static final int RESULT_CANCELED = 0;
# /** Standard activity result: operation succeeded. */
# public static final int RESULT_OK = -1;
# /** Start of user-defined activity results. */
# Not sure what this means
# public static final int RESULT_FIRST_USER = 1;

def user_select_image(callback):
"""Open Gallery Activity and call callback with absolute image filepath of image user selected.
None if user canceled.
"""

# PythonActivity.mActivity is the instance of the current Activity
# BUT, startActivity is a method from the Activity class, not from our
# PythonActivity.
# We need to cast our class into an activity and use it
currentActivity = cast('android.app.Activity', PythonActivity.mActivity)

# Forum discussion: https://groups.google.com/forum/#!msg/kivy-users/bjsG2j9bptI/-Oe_aGo0newJ
def on_activity_result(request_code, result_code, intent):
if request_code != RESULT_LOAD_IMAGE:
Logger.warning('user_select_image: ignoring activity result that was not RESULT_LOAD_IMAGE')
return

if result_code == Activity.RESULT_CANCELED:
Clock.schedule_once(lambda dt: callback(None), 0)
return

if result_code != Activity.RESULT_OK:
# This may just go into the void...
raise NotImplementedError('Unknown result_code "{}"'.format(result_code))

selectedImage = intent.getData(); # Uri
filePathColumn = [MediaStore_Images_Media_DATA]; # String[]
# Cursor
cursor = currentActivity.getContentResolver().query(selectedImage,
filePathColumn, None, None, None);
cursor.moveToFirst();

# int
columnIndex = cursor.getColumnIndex(filePathColumn[0]);
# String
picturePath = cursor.getString(columnIndex);
cursor.close();
Logger.info('android_ui: user_select_image() selected %s', picturePath)

# This is possibly in a different thread?
Clock.schedule_once(lambda dt: callback(picturePath), 0)

# See: http://pyjnius.readthedocs.org/en/latest/android.html
activity.bind(on_activity_result=on_activity_result)

intent = Intent()

# http://programmerguru.com/android-tutorial/how-to-pick-image-from-gallery/
# http://stackoverflow.com/questions/18416122/open-gallery-app-in-android
intent.setAction(Intent.ACTION_PICK)
# TODO internal vs external?
intent.setData(Uri.parse('content://media/internal/images/media'))
# TODO setType(Image)?

currentActivity.startActivityForResult(intent, RESULT_LOAD_IMAGE)
Reply all
Reply to author
Forward
0 new messages