pyjnius and bluetooth low energy

1,979 views
Skip to first unread message

am...@prodeo.es

unread,
Nov 23, 2013, 8:11:39 PM11/23/13
to pyjni...@googlegroups.com
Hello I'm trying using pyjnius for new bluetooth low energy (androidapi 18)
I've done compiling kivylauncher with BLUETOOTH and BLUETOOTH_ADMIN privileges.

For detecting devices, we need to define a LeScanCallback....

from jnius import autoclass
from jnius import PythonJavaClass, java_method, JavaClass, MetaJavaClass
BluetoothAdapter = autoclass('android.bluetooth.BluetoothAdapter')

btAdapter = BluetoothAdapter.getDefaultAdapter()

class LeScanCallback(PythonJavaClass):
    __javacontext__ = 'app'
    __javainterfaces__ = ['android.bluetooth.BluetoothAdapter$LeScanCallback']
    
    def __init__( self, callback ):
        super( LeScanCallback, self).__init__()
        self.callback = callback
    
    @java_method( '(Landroid/bluetooth/BluetoothDevice;IB[])V' )
    def onLeScan( self, device, rssi, scanRecord):
        print device, rssi, scanRecord

        @run_on_ui_thread
        def run():
            self.callback( device, rssi, scanRecord )
        return

def cb_devices( device, rssi, scanRecord ):
    print 'callback'
    print device, rssi, scanRecord
    
# when I call this, app hangs, without error. logcat doesn't say anything.
mybtLeScan = LeScanCallback( cb_devices )

# never goes here
btAdapter.startLeScan( mybtLeScan )

What can be the problem?, the pyjnius api usage is similar as other examples I've seen.

Best regards.
Angel

Michael Kinzl

unread,
Dec 28, 2013, 8:43:14 PM12/28/13
to pyjni...@googlegroups.com
I am having exactly the same problem implementing the LeScanCallback with pyjnius PythonJavaClass. My code is very similar to yours, I also use API 18 and the app is built with buildozer. It seems the app starts hanging when super(LeScanCallback, self).__init__() is called, but I cannot figure out what the reason is.

Did you already find a solution for your problem. If yes, I would very much appreciate if you could share it.

Best regards,
Michael

Arnaud Waels

unread,
Jan 20, 2014, 6:16:02 PM1/20/14
to pyjni...@googlegroups.com
Hi

I'm trying to do exactly the same, and i have indeed the same issue. Instanciation of LeScanCallback() class freezes the app.

Mathieu Virbel

unread,
Jan 21, 2014, 3:41:41 AM1/21/14
to pyjni...@googlegroups.com
Hey guys,

There is few issues with the code itself:
- __javacontext__ = 'app' -> not needed, as the interface LeScanCallback is declared in android, the java file doesn't exist in your application itself
- the definition for onLeScan is wrong (that's why it's stuck, it goes into an infinite loop)
  declaring a "Byte []" in java = "[B" for the definition. So the full signature is: @java_method('(Landroid/bluetooth/BluetoothDevice;I[B)V')
- you probably don't need an inner @run_on_ui_thread for calling the callback. Whatever you do, the scan method will be called in another thread than the main thread i guess, so don't change your UI directly.
- after btAdapter.startLeScan(...), you need to sleep a little, otherwise the application will leave directly, and you might never see the result
- during the execution, the LeScanCallback needed to implement an hashCode method.
- i'm not sure about the getDefaultAdapter, the android documentation used something else: http://developer.android.com/about/versions/android-4.3.html#Wireless

Anyway, here is the corrected example:

__version__ = '1.0'

from jnius import autoclass
from jnius import PythonJavaClass, java_method

Context = autoclass('android.content.Context')
PythonActivity = autoclass('org.renpy.android.PythonActivity')
BluetoothAdapter = autoclass('android.bluetooth.BluetoothAdapter')

activity = PythonActivity.mActivity
btManager = activity.getSystemService(Context.BLUETOOTH_SERVICE)
btAdapter = btManager.getAdapter()


class LeScanCallback(PythonJavaClass):
    __javainterfaces__ = ['android/bluetooth/BluetoothAdapter$LeScanCallback']
    
    def __init__(self, callback):
        super(LeScanCallback, self).__init__()
        self.callback = callback

    @java_method('()I')
    def hashCode(self):
        return id(self)

    @java_method('(Landroid/bluetooth/BluetoothDevice;I[B)V')
    def onLeScan( self, device, rssi, scanRecord):
        print device, rssi, scanRecord
        self.callback(device, rssi, scanRecord)


def cb_devices( device, rssi, scanRecord ):
    print 'callback'
    print device, rssi, scanRecord


mybtLeScan = LeScanCallback(cb_devices)
btAdapter.startLeScan( mybtLeScan )

import time
time.sleep(3)

Enjoy!

Mathieu

Arnaud Waels

unread,
Jan 21, 2014, 1:36:40 PM1/21/14
to pyjni...@googlegroups.com
Great !
Indeed it works like a charm, Thanks a lot !


But i'm now facing another issue with __javainterfaces__

I'm trying to go further, performing Rssi update using connectGatt. Which needs the following callback at setup http://developer.android.com/reference/android/bluetooth/BluetoothGattCallback.html
So i'm obviously doing it the same way for the callback, but BluetoothGattCallback is an Abstract Class so i get the error : 'android.bluetooth.BluetoothGattCallback is not an interface'. I assume the pb lies there but i can't find how to deal with abstract classes with pyjnius..


BluetoothGattCallback = autoclass('android.bluetooth.BluetoothGattCallback')


class BluetoothGattCallback2(PythonJavaClass):
    __javainterfaces__ = ['android/bluetooth/BluetoothGattCallback']

    def __init__(self, callback):
        super(BluetoothGattCallback2, self).__init__()
        self.callback = callback

    @java_method('(Landroid/bluetooth/BluetoothGatt;II)V')
    def onReadRemoteRssi(self, device, rssi, status):
        print device, rssi, status
        self.callback(device, rssi, status)



W/System.err( 8927): java.lang.IllegalArgumentException: android.bluetooth.BluetoothGattCallback is not an interface

Best regards,
A


2014/1/21 Mathieu Virbel <m...@kivy.org>:
> --
> You received this message because you are subscribed to a topic in the
> Google Groups "PyJNIus development ML" group.
> To unsubscribe from this topic, visit
> https://groups.google.com/d/topic/pyjnius-dev/yId8td6YZiI/unsubscribe.
> To unsubscribe from this group and all its topics, send an email to
> pyjnius-dev...@googlegroups.com.
> For more options, visit https://groups.google.com/groups/opt_out.

Mathieu Virbel

unread,
Jan 21, 2014, 2:54:19 PM1/21/14
to pyjni...@googlegroups.com
There is no way to implement abstract class with pyjnius, and it wont
have a way ever i guess.

What you need to do is to write an implementation of the class in Java.
The goal is to register a method to call for each method that we could
extend. The steps would be:

1. Create yourself the interface, with the exact same definitions as the
abstract class (let's call it OnBluetoothGattCallback)
2. Implement the abstract class BluetoothGattCallback with a method for
setting a callback object of the type OnBluetoothGattCallback)
3. Implement each method and call the correct method of the callback
object instead.

So at the end, you should have something like that:

public class BluetoothGattImplem extends BluetoothGattCallback {

public interface OnBluetoothGattCallback {
// ... all the methods from BluetoothGattCallback here ...
// like:
void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic);
}

// private storage for the callback object
private OnBluetoothGattCallback callback = null;

// method to set the callback object
void setCallback(OnBluetoothGattCallback callback) {
this.callback = callback;
}

// implement all the methods, and redirect to the callback object
void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
if (this.callback)
this.callback.onCharacteristicChanged(gatt, characteristic);
}

// ... do the same of all the next methods ...


4. Put the java into src/org/myapp, don't forget the "package
org.myapp;" in the java file too.
5. Then, you can... implement a
"org/myapp/BluetoothGattImplem$OnBluetoothGattCallback" instead, as you
wanted to do it.
6. Don't forget to use __javacontext__ = 'app', because you're going to
implement a java class that you defined, not a java class available on
the system api.
7. To use it, just do:

class PyBluetoothGattCallback(...):
__javainterfaces__ =
["org/myapp/BluetoothGattImplem$OnBluetoothGattCallback"]
__javacontext__ = 'app'
# ... implem here

BluetoothGattImplem = autoclass('org/myapp/BluetoothGattImplem')
pycallback = PyBluetoothGattCallback()
bg = BluetoothGattImplem()
bg.setCallback(pycallback)

8. You're done.



That's a general way that works for all the abstract class. It would be
nice for us to have a way for auto generating this java code / python
code anyway. If somebody hear me... :)


Good luck!

Mathieu





Le 21/01/2014 19:36, Arnaud Waels a �crit :
> 2014/1/21 Mathieu Virbel <m...@kivy.org <mailto:m...@kivy.org>>:
> <mailto:am...@prodeo.es> a �crit :
> <mailto:pyjnius-dev%2Bunsu...@googlegroups.com>.
>> For more options, visit https://groups.google.com/groups/opt_out.
>
> --
> You received this message because you are subscribed to the Google
> Groups "PyJNIus development ML" group.
> To unsubscribe from this group and stop receiving emails from it, send

Arnaud Waels

unread,
Jan 21, 2014, 4:54:41 PM1/21/14
to pyjni...@googlegroups.com
oh ok
it sounds like a journey but i'm trying!
Thanks a lot!




2014/1/21 Mathieu Virbel <txp...@gmail.com>
> <mailto:am...@prodeo.es> a écrit :
To unsubscribe from this group and all its topics, send an email to pyjnius-dev...@googlegroups.com.

Gabriel Pettier

unread,
Jan 26, 2014, 7:36:17 PM1/26/14
to pyjni...@googlegroups.com
Would this repos be the right place to scrap for classes to
auto-generate code for?
https://github.com/android/platform_frameworks_base/search?q=abstract&ref=cmdform

(i tried to use kivy-remote-shell/pyjnias to have some way to introspect
that, but i didn't find one)
> > <mailto:am...@prodeo.es> a écrit :

Gabriel Pettier

unread,
Feb 6, 2014, 8:04:10 PM2/6/14
to pyjni...@googlegroups.com
This is WIP (and untested), but feedback welcome:

i cloned g...@github.com:android/platform_frameworks_base.git

installed plyj and jinja2 ind a virtualenv, and wrote this:

https://gist.github.com/8855677

(need to create an `interfaces` dir where you run it, that is next to
your clone of platform_frameworks_base, implementations will be produced
into this dir)

then

python parse.py

Gabriel Pettier

unread,
Feb 8, 2014, 11:20:52 AM2/8/14
to pyjni...@googlegroups.com
So a few versions more, after much fixin', i'm actually trying to make
buildozer build the generated code.

first, i generate the classes using this command:

./parse.py platform_frameworks_base ~/kivy-remote-shell/.buildozer/android/platform/python-for-android/dist/default/src/ Impl org.kivy.android_interfaces

this make files/classes with an `Impl` suffix, as an
`org.kivy.android_interfaces` package, which are placed in the src files
of my default dist for buildozer's default distrubution for
kivy-remote-shell.

then i run:

buildozer android debug

and it sadly fails:

http://paste.ubuntu.com/6897865/

I don't have much idea for the first errors yet, if you guys have ideas.

Mathieu Virbel

unread,
Feb 8, 2014, 11:58:39 AM2/8/14
to pyjni...@googlegroups.com
Hey,

I think some class cannot be used at all.


1/

The very first error is about "package com.android.internal.widget does
not exist":
[javac] import com.android.internal.widget.*;

-> i guess com.android.internal is hidden to us.

2/

cannot find symbol "symbol: class Trace"
[javac] import android.os.Trace;

-> no os.trace availadble for us too.


ETC.

You need to see what is accessible from user or not. :)

Gabriel Pettier

unread,
Feb 9, 2014, 6:07:38 PM2/9/14
to pyjni...@googlegroups.com
So, i've been looking into this a bit more, and it's looking a bit tidy.

A lot of packages fail to be found by the build system, and some of them
are actually needed by the defined interfaces, i'm not sure why they
aren't found, but it's problematic.

I updated the parser, code is there:
https://gist.github.com/8907228

and the currently used blacklist look like this, i'm pretty sure some of
it shouldn't be there, though (android.os and android.util should be
found, no?) and i'm still getting 100+ errors.

https://gist.github.com/8907320

i use:

./parse.py platform_frameworks_base ~/kivy-remote-shell/.buildozer/android/platform/python-for-android/dist/default/src/ Impl org.kivy.android_interfaces -b blacklist.txt -c

to build.

I'm thinking about putting the whole plateform_framework_base dir into
src/ but i guess it's not the best idea ever.

Maybe it's something about the order of compilation, i don't know.

https://gist.github.com/tshirtman/8907413

ideas welcome.

Mike

unread,
Feb 28, 2014, 5:38:45 AM2/28/14
to pyjni...@googlegroups.com
Do you had success?

Same problem here and I'm searching for some kivy code.

Regards, Mike.

Mike

unread,
Mar 10, 2014, 4:36:11 AM3/10/14
to pyjni...@googlegroups.com
I appreciate your general approach, but what about abstract classes which extend an abstract class with abstract methods? You have to process the latter, too.

(I tried your parse.py, with additional check if having a return value or not, and with "if (this.implem != null)" instead of "if (this.implem)").

Gabriel Pettier

unread,
Mar 10, 2014, 5:25:57 AM3/10/14
to pyjni...@googlegroups.com
Ah, that's a fair point, didn't think about going recursive for that,
i'll try to have a look into this soonish.
> > > >>> 2014/1/21 Mathieu Virbel <txp...@gmail.com <javascript:>>
> > > >>>>> 2014/1/21 Mathieu Virbel <m...@kivy.org <javascript:> <mailto:
> > m...@kivy.org <javascript:>>>:
> > > >>>>>> pyjnius-dev...@googlegroups.com <javascript:>
> > > >>>>> <mailto:pyjnius-dev%2Bunsu...@googlegroups.com <javascript:>>.
> >
> > > >>>>>> For more options, visit https://groups.google.com/groups/opt_out.
> >
> > > >>>>>
> > > >>>>> --
> > > >>>>> You received this message because you are subscribed to the Google
> > > >>>>> Groups "PyJNIus development ML" group.
> > > >>>>> To unsubscribe from this group and stop receiving emails from it,
> > send
> > > >>>>> an email to pyjnius-dev...@googlegroups.com <javascript:>.
> > > >>>>> For more options, visit https://groups.google.com/groups/opt_out.
> > > >>>>
> > > >>>> --
> > > >>>> You received this message because you are subscribed to a topic in
> > the
> > > >>>> Google Groups "PyJNIus development ML" group.
> > > >>>> To unsubscribe from this topic, visit
> > > >>>>
> > https://groups.google.com/d/topic/pyjnius-dev/yId8td6YZiI/unsubscribe.
> > > >>>> To unsubscribe from this group and all its topics, send an email to
> > > >>>> pyjnius-dev...@googlegroups.com <javascript:>.
> > > >>>> For more options, visit https://groups.google.com/groups/opt_out.
> > > >>>>
> > > >>>
> > > >>> --
> > > >>> You received this message because you are subscribed to the Google
> > Groups "PyJNIus development ML" group.
> > > >>> To unsubscribe from this group and stop receiving emails from it,
> > send an email to pyjnius-dev...@googlegroups.com <javascript:>.
> > > >>> For more options, visit https://groups.google.com/groups/opt_out.
> > > >
> > >
> > > --
> > > You received this message because you are subscribed to the Google
> > Groups "PyJNIus development ML" group.
> > > To unsubscribe from this group and stop receiving emails from it, send
> > an email to pyjnius-dev...@googlegroups.com <javascript:>.
> > > For more options, visit https://groups.google.com/groups/opt_out.
> >
>
> --
> You received this message because you are subscribed to the Google Groups "PyJNIus development ML" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to pyjnius-dev...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.
Reply all
Reply to author
Forward
0 new messages