Skip to content

Update broadcast.py #3167

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 76 additions & 40 deletions pythonforandroid/recipes/android/src/android/broadcast.py
Original file line number Diff line number Diff line change
@@ -1,72 +1,108 @@
# -------------------------------------------------------------------
# Broadcast receiver bridge
# Broadcast receiver bridge for Kivy + Pyjnius
# Safe start/stop with Android lifecycle integration
# -------------------------------------------------------------------

from jnius import autoclass, PythonJavaClass, java_method
from android.config import JAVA_NAMESPACE, JNI_NAMESPACE, ACTIVITY_CLASS_NAME, SERVICE_CLASS_NAME


class BroadcastReceiver(object):

class BroadcastReceiver:
class Callback(PythonJavaClass):
__javainterfaces__ = [JNI_NAMESPACE + '/GenericBroadcastReceiverCallback']
__javacontext__ = 'app'

def __init__(self, callback, *args, **kwargs):
self.callback = callback
PythonJavaClass.__init__(self, *args, **kwargs)
super().__init__(*args, **kwargs)

@java_method('(Landroid/content/Context;Landroid/content/Intent;)V')
def onReceive(self, context, intent):
self.callback(context, intent)

def __init__(self, callback, actions=None, categories=None):
super().__init__()
self.callback = callback

if not actions and not categories:
raise Exception('You need to define at least actions or categories')

def _expand_partial_name(partial_name):
if '.' in partial_name:
return partial_name # Its actually a full dotted name
else:
name = 'ACTION_{}'.format(partial_name.upper())
if not hasattr(Intent, name):
raise Exception('The intent {} does not exist'.format(name))
return getattr(Intent, name)
self.callback = callback
self._is_registered = False
self._handlerthread_started = False

# resolve actions/categories first
# Expand intent names
Intent = autoclass('android.content.Intent')
resolved_actions = [_expand_partial_name(x) for x in actions or []]
resolved_categories = [_expand_partial_name(x) for x in categories or []]

# resolve android API
GenericBroadcastReceiver = autoclass(JAVA_NAMESPACE + '.GenericBroadcastReceiver')
IntentFilter = autoclass('android.content.IntentFilter')
HandlerThread = autoclass('android.os.HandlerThread')

# create a thread for handling events from the receiver
self.handlerthread = HandlerThread('handlerthread')

# create a listener
def _expand(partial):
if '.' in partial:
return partial
name = 'ACTION_' + partial.upper()
if not hasattr(Intent, name):
raise Exception(f'Intent {name} does not exist')
return getattr(Intent, name)

self.resolved_actions = [_expand(a) for a in actions or []]
self.resolved_categories = [_expand(c) for c in categories or []]

# Java classes
self.GenericBroadcastReceiver = autoclass(JAVA_NAMESPACE + '.GenericBroadcastReceiver')
self.IntentFilter = autoclass('android.content.IntentFilter')
self.Handler = autoclass('android.os.Handler')
self.HandlerThreadClass = autoclass('android.os.HandlerThread')

# Build filter
self.receiver_filter = self.IntentFilter()
for action in self.resolved_actions:
self.receiver_filter.addAction(action)
for category in self.resolved_categories:
self.receiver_filter.addCategory(category)

# Receiver and callback
self.listener = BroadcastReceiver.Callback(self.callback)
self.receiver = GenericBroadcastReceiver(self.listener)
self.receiver_filter = IntentFilter()
for x in resolved_actions:
self.receiver_filter.addAction(x)
for x in resolved_categories:
self.receiver_filter.addCategory(x)
self.receiver = self.GenericBroadcastReceiver(self.listener)

# Init thread placeholder
self.handlerthread = None
self.handler = None

def start(self):
Handler = autoclass('android.os.Handler')
self.handlerthread.start()
self.handler = Handler(self.handlerthread.getLooper())
self.context.registerReceiver(
self.receiver, self.receiver_filter, None, self.handler)
if self._is_registered:
print("[BroadcastReceiver] Already registered.")
return

if not self._handlerthread_started:
self.handlerthread = self.HandlerThreadClass('BroadcastReceiverThread')
try:
self.handlerthread.start()
self._handlerthread_started = True
except Exception as e:
print(f"[BroadcastReceiver] HandlerThread start failed: {e}")
return

try:
self.handler = self.Handler(self.handlerthread.getLooper())
self.context.registerReceiver(self.receiver, self.receiver_filter, None, self.handler)
self._is_registered = True
print("[BroadcastReceiver] Registered.")
except Exception as e:
print(f"[BroadcastReceiver] registerReceiver failed: {e}")

def stop(self):
self.context.unregisterReceiver(self.receiver)
self.handlerthread.quit()
if self._is_registered:
try:
self.context.unregisterReceiver(self.receiver)
print("[BroadcastReceiver] Unregistered.")
except Exception as e:
print(f"[BroadcastReceiver] unregisterReceiver failed: {e}")
self._is_registered = False

if self._handlerthread_started:
try:
self.handlerthread.quitSafely()
print("[BroadcastReceiver] HandlerThread quit safely.")
except Exception as e:
print(f"[BroadcastReceiver] thread quit failed: {e}")
self._handlerthread_started = False
self.handlerthread = None
self.handler = None

@property
def context(self):
Expand Down
Loading