Skip to content

Commit a0fbab6

Browse files
committed
feat: Update Android permissions to allow passing a callback with each request.
1 parent 8122126 commit a0fbab6

File tree

2 files changed

+72
-20
lines changed

2 files changed

+72
-20
lines changed

pythonforandroid/bootstraps/sdl2/build/src/main/java/org/kivy/android/PythonActivity.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -633,16 +633,20 @@ public boolean checkCurrentPermission(String permission) {
633633
/**
634634
* Used by android.permissions p4a module to request runtime permissions
635635
**/
636-
public void requestPermissions(String[] permissions) {
636+
public void requestPermissionsWithRequestCode(String[] permissions, int requestCode) {
637637
if (android.os.Build.VERSION.SDK_INT < 23)
638638
return;
639639
try {
640640
java.lang.reflect.Method methodRequestPermission =
641641
Activity.class.getMethod("requestPermissions",
642642
java.lang.String[].class, int.class);
643-
methodRequestPermission.invoke(this, permissions, 1);
643+
methodRequestPermission.invoke(this, permissions, requestCode);
644644
} catch (IllegalAccessException | NoSuchMethodException |
645645
InvocationTargetException e) {
646646
}
647647
}
648+
649+
public void requestPermissions(String[] permissions) {
650+
requestPermissionsWithRequestCode(permissions, 1);
651+
}
648652
}

pythonforandroid/recipes/android/src/android/permissions.py

Lines changed: 66 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import threading
12

23
try:
34
from jnius import autoclass, PythonJavaClass, java_method
@@ -428,42 +429,89 @@ class onRequestPermissionsCallback(PythonJavaClass):
428429
"""
429430
__javainterfaces__ = ['org.kivy.android.PythonActivity$PermissionsCallback']
430431
__javacontext__ = 'app'
431-
_callback = None # To avoid garbage collection
432432

433433
def __init__(self, func):
434434
self.func = func
435-
onRequestPermissionsCallback._callback = self
435+
onRequestPermissionsCallback._java_callback = self
436436
super().__init__()
437437

438438
@java_method('(I[Ljava/lang/String;[I)V')
439439
def onRequestPermissionsResult(self, requestCode, permissions, grantResults):
440440
self.func(requestCode, permissions, grantResults)
441441

442442

443-
def register_permissions_callback(callback):
444-
"""Register a callback. This will asynchronously receive arguments from
445-
onRequestPermissionsResult on PythonActivity after request_permission(s)
446-
is called.
443+
class onRequestPermissionsManager:
444+
"""Class for requesting Android permissions via requestPermissions,
445+
including registering callbacks to requestPermissions.
447446
448-
The callback must accept three arguments: requestCode, permissions and
449-
grantResults.
447+
Permissions are requested through the method 'request_permissions' which
448+
accepts a list of permissions and an optional callback.
449+
450+
Any callback will asynchronously receive arguments from
451+
onRequestPermissionsResult on PythonActivity after requestPermissions is
452+
called.
453+
454+
The callback supplied must accept two arguments: 'permissions' and
455+
'grantResults' (as supplied to onPermissionsCallbackResult).
450456
451457
Note that calling request_permission on SDK_INT < 23 will return
452-
immediately (as run-time permissions are not required), and so this
453-
callback will never happen.
458+
immediately (as run-time permissions are not required), and so the callback
459+
will never happen. Therefore, request_permissions should only be called
460+
with a callback if calling check_permission has indicated that it is
461+
necessary.
462+
463+
The attribute '_java_callback' is initially None, but is set when the first
464+
permissions request is made. It is set to an instance of
465+
onRequestPermissionsCallback, which allows the Java callback to be
466+
propagated to the class method 'python_callback'. This is then, in turn,
467+
used to call an application callback if provided to request_permissions.
454468
"""
455-
java_callback = onRequestPermissionsCallback(callback)
456-
python_activity = autoclass('org.kivy.android.PythonActivity')
457-
python_activity.addPermissionsCallback(java_callback)
469+
_java_callback = None
470+
_callbacks = {1: None}
471+
_callback_id = 1
472+
_lock = threading.Lock()
458473

474+
@classmethod
475+
def register_callback(cls):
476+
"""Register Java callback for requestPermissions."""
477+
cls._java_callback = onRequestPermissionsCallback(cls.python_callback)
478+
python_activity = autoclass('org.kivy.android.PythonActivity')
479+
python_activity.addPermissionsCallback(cls._java_callback)
459480

460-
def request_permissions(permissions):
461-
python_activity = autoclass('org.kivy.android.PythonActivity')
462-
python_activity.requestPermissions(permissions)
481+
@classmethod
482+
def request_permissions(cls, permissions, callback=None):
483+
"""Requests Android permissions from PythonActivity.
484+
If 'callback' is supplied, the request is made with a new requestCode
485+
and the callback is stored in the _callbacks dict. When a Java callback
486+
with the matching requestCode is received, callback will be called
487+
with arguments of 'permissions' and 'grantResults'.
488+
"""
489+
with cls._lock:
490+
if not cls._java_callback:
491+
cls.register_callback()
492+
python_activity = autoclass('org.kivy.android.PythonActivity')
493+
if not callback:
494+
python_activity.requestPermissions(permissions)
495+
else:
496+
cls._callback_id += 1
497+
python_activity.requestPermissionsWithRequestCode(
498+
permissions, cls._callback_id)
499+
cls._callbacks[cls._callback_id] = callback
500+
501+
@classmethod
502+
def python_callback(cls, requestCode, permissions, grantResults):
503+
"""Calls the relevant callback with arguments of 'permissions'
504+
and 'grantResults'."""
505+
if cls._callbacks.get(requestCode):
506+
cls._callbacks[requestCode](permissions, grantResults)
507+
508+
509+
def request_permissions(permissions, callback=None):
510+
onRequestPermissionsManager.request_permissions(permissions, callback)
463511

464512

465-
def request_permission(permission):
466-
request_permissions([permission])
513+
def request_permission(permission, callback=None):
514+
request_permissions([permission], callback)
467515

468516

469517
def check_permission(permission):

0 commit comments

Comments
 (0)