Skip to content

Commit 075a465

Browse files
committed
add android_broadcast module
1 parent 55d291e commit 075a465

File tree

4 files changed

+181
-0
lines changed

4 files changed

+181
-0
lines changed

docs/source/android.rst

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,84 @@ Example without PyJNIus
348348
print android.get_dpi()
349349

350350

351+
Bridges
352+
-------
353+
354+
Some part of the Android API is not accessible from PyJNIus. For example, if
355+
you want to receive some broadcast, you need to implement a `BroadcastReceiver
356+
<http://developer.android.com/reference/android/content/BroadcastReceiver.html>`_.
357+
PyJNIus allows you to implement dynamically classes from Java interfaces, but
358+
unfortunately, the `BroadcastReceiver` is an abstract class.
359+
360+
So we started to create bridges for this case.
361+
362+
android_broadcast
363+
~~~~~~~~~~~~~~~~~
364+
365+
.. module:: android_broadcast
366+
367+
.. class:: BroadcastReceiver
368+
369+
Implementation of the android `BroadcastReceiver
370+
<http://developer.android.com/reference/android/content/BroadcastReceiver.html>`_.
371+
You can specify the callback that will receive the broadcast event, and
372+
actions or categories filters.
373+
374+
.. warning::
375+
376+
The callback will be called in another thread than the main thread. Be
377+
careful to not access to OpenGL or something like that.
378+
379+
.. method:: __init__(callback, actions=None, categories=None)
380+
381+
:param callback: function or method that will receive the event. Will
382+
receive the context and intent as argument.
383+
:param actions: list of strings that represent an action.
384+
:param categories: list of strings that represent a category.
385+
386+
For actions and categories, the string must be in lower case, without the prefix::
387+
388+
# In java: Intent.ACTION_HEADSET_PLUG
389+
# In python: 'headset_plug'
390+
391+
.. method:: start()
392+
393+
Register the receiver with all the actions and categories, and start
394+
handling events.
395+
396+
.. method:: stop()
397+
398+
Unregister the receiver with all the actions and categories, and stop
399+
handling events.
400+
401+
Example::
402+
403+
class TestApp(App):
404+
405+
def build(self):
406+
self.br = BroadcastReceiver(
407+
self.on_broadcast, actions=['headset_plug'])
408+
self.br.start()
409+
# ...
410+
411+
def on_broadcast(self, context, intent):
412+
extras = intent.getExtras()
413+
headset_state = bool(extras.get('state'))
414+
if headset_state:
415+
print 'The headset is plugged'
416+
else:
417+
print 'The headset is unplugged'
418+
419+
# don't forget to stop and restart the receiver when the app is going
420+
# to pause / resume mode
421+
422+
def on_pause(self):
423+
self.br.stop()
424+
return True
425+
426+
def on_resume(self):
427+
self.br.start()
428+
351429

352430
Old Version
353431
-----------
@@ -434,3 +512,4 @@ It has several differences from the pygame mixer:
434512
The android_mixer module hasn't been tested much, and so bugs may be
435513
present.
436514

515+
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# -------------------------------------------------------------------
2+
# Broadcast receiver bridge
3+
4+
from jnius import autoclass, PythonJavaClass, java_method
5+
6+
class BroadcastReceiver(object):
7+
8+
class Callback(PythonJavaClass):
9+
__javainterfaces__ = ['org/renpy/android/GenericBroadcastReceiverCallback']
10+
__javacontext__ = 'app'
11+
12+
def __init__(self, callback, *args, **kwargs):
13+
self.callback = callback
14+
PythonJavaClass.__init__(self, *args, **kwargs)
15+
16+
@java_method('(Landroid/content/Context;Landroid/content/Intent;)V')
17+
def onReceive(self, context, intent):
18+
self.callback(context, intent)
19+
20+
def __init__(self, callback, actions=None, categories=None):
21+
super(BroadcastReceiver, self).__init__()
22+
self.callback = callback
23+
24+
if not actions and not categories:
25+
raise Exception('You need to define at least actions or categories')
26+
27+
# resolve actions/categories first
28+
Intent = autoclass('android.content.Intent')
29+
resolved_actions = []
30+
if actions:
31+
for x in actions:
32+
name = 'ACTION_{}'.format(x.upper())
33+
if not hasattr(Intent, name):
34+
raise Exception('The intent {} doesnt exist'.format(name))
35+
resolved_actions += [getattr(Intent, name)]
36+
37+
resolved_categories = []
38+
if categories:
39+
for x in categories:
40+
name = 'CATEGORY_{}'.format(x.upper())
41+
if not hasattr(Intent, name):
42+
raise Exception('The intent {} doesnt exist'.format(name))
43+
resolved_categories += [getattr(Intent, name)]
44+
45+
# resolve android API
46+
PythonActivity = autoclass('org.renpy.android.PythonActivity')
47+
GenericBroadcastReceiver = autoclass('org.renpy.android.GenericBroadcastReceiver')
48+
IntentFilter = autoclass('android.content.IntentFilter')
49+
HandlerThread = autoclass('android.os.HandlerThread')
50+
51+
# create a thread for handling events from the receiver
52+
self.handlerthread = HandlerThread('handlerthread')
53+
54+
# create a listener
55+
self.context = PythonActivity.mActivity
56+
self.listener = BroadcastReceiver.Callback(self.callback)
57+
self.receiver = GenericBroadcastReceiver(self.listener)
58+
self.receiver_filter = IntentFilter()
59+
for x in resolved_actions:
60+
self.receiver_filter.addAction(x)
61+
for x in resolved_categories:
62+
self.receiver_filter.addCategory(x)
63+
64+
def start(self):
65+
Handler = autoclass('android.os.Handler')
66+
self.handlerthread.start()
67+
self.handler = Handler(self.handlerthread.getLooper())
68+
self.context.registerReceiver(self.receiver, self.receiver_filter, None,
69+
self.handler)
70+
71+
def stop(self):
72+
self.context.unregisterReceiver(self.receiver)
73+
self.handlerthread.quit()
74+
75+
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package org.renpy.android;
2+
3+
import android.content.BroadcastReceiver;
4+
import android.content.Intent;
5+
import android.content.Context;
6+
7+
public class GenericBroadcastReceiver extends BroadcastReceiver {
8+
9+
GenericBroadcastReceiverCallback listener;
10+
11+
public GenericBroadcastReceiver(GenericBroadcastReceiverCallback listener) {
12+
super();
13+
this.listener = listener;
14+
}
15+
16+
public void onReceive(Context context, Intent intent) {
17+
this.listener.onReceive(context, intent);
18+
}
19+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package org.renpy.android;
2+
3+
import android.content.Intent;
4+
import android.content.Context;
5+
6+
public interface GenericBroadcastReceiverCallback {
7+
void onReceive(Context context, Intent intent);
8+
};

0 commit comments

Comments
 (0)