Skip to content
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

feat: Allows registering the onRequestPermissionsResult callback. #1818

Merged
merged 6 commits into from
Aug 4, 2019
Merged
Show file tree
Hide file tree
Changes from 4 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
Original file line number Diff line number Diff line change
Expand Up @@ -581,7 +581,34 @@ public void onWindowFocusChanged(boolean hasFocus) {
// call native function (since it's not yet loaded)
}
considerLoadingScreenRemoval();
}
}

/**
* Used by android.permissions p4a module to register a call back after
* requesting runtime permissions
**/
public interface PermissionsCallback {
void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults);
}

private PermissionsCallback permissionCallback;
private boolean havePermissionsCallback = false;

public void addPermissionsCallback(PermissionsCallback callback) {
permissionCallback = callback;
havePermissionsCallback = true;
Log.v(TAG, "addPermissionsCallback(): Added callback for onRequestPermissionsResult");
}

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
Log.v(TAG, "onRequestPermissionsResult()");
if (havePermissionsCallback) {
Log.v(TAG, "onRequestPermissionsResult passed to callback");
permissionCallback.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}

/**
* Used by android.permissions p4a module to check a permission
Expand All @@ -592,9 +619,9 @@ public boolean checkCurrentPermission(String permission) {

try {
java.lang.reflect.Method methodCheckPermission =
Activity.class.getMethod("checkSelfPermission", java.lang.String.class);
Activity.class.getMethod("checkSelfPermission", java.lang.String.class);
Object resultObj = methodCheckPermission.invoke(this, permission);
int result = Integer.parseInt(resultObj.toString());
int result = Integer.parseInt(resultObj.toString());
if (result == PackageManager.PERMISSION_GRANTED)
return true;
} catch (IllegalAccessException | NoSuchMethodException |
Expand All @@ -606,16 +633,20 @@ public boolean checkCurrentPermission(String permission) {
/**
* Used by android.permissions p4a module to request runtime permissions
**/
public void requestPermissions(String[] permissions) {
public void requestPermissionsWithRequestCode(String[] permissions, int requestCode) {
if (android.os.Build.VERSION.SDK_INT < 23)
return;
try {
java.lang.reflect.Method methodRequestPermission =
Activity.class.getMethod("requestPermissions",
java.lang.String[].class, int.class);
methodRequestPermission.invoke(this, permissions, 1);
java.lang.String[].class, int.class);
methodRequestPermission.invoke(this, permissions, requestCode);
} catch (IllegalAccessException | NoSuchMethodException |
InvocationTargetException e) {
}
}

public void requestPermissions(String[] permissions) {
requestPermissionsWithRequestCode(permissions, 1);
}
}
97 changes: 91 additions & 6 deletions pythonforandroid/recipes/android/src/android/permissions.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import threading

try:
from jnius import autoclass
from jnius import autoclass, PythonJavaClass, java_method
except ImportError:
# To allow importing by build/manifest-creating code without
# pyjnius being present:
Expand Down Expand Up @@ -422,13 +423,97 @@ class Permission:
)


def request_permissions(permissions):
python_activity = autoclass('org.kivy.android.PythonActivity')
python_activity.requestPermissions(permissions)
class _onRequestPermissionsCallback(PythonJavaClass):
"""Callback class for registering a Python callback from
onRequestPermissionsResult in PythonActivity.
"""
__javainterfaces__ = ['org.kivy.android.PythonActivity$PermissionsCallback']
__javacontext__ = 'app'

def __init__(self, func):
self.func = func
super().__init__()

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


class _RequestPermissionsManager:
"""Internal class for requesting Android permissions via
requestPermissions, including registering callbacks to requestPermissions.

Permissions are requested through the method 'request_permissions' which
accepts a list of permissions and an optional callback.

Any callback will asynchronously receive arguments from
onRequestPermissionsResult on PythonActivity after requestPermissions is
called.

The callback supplied must accept two arguments: 'permissions' and
'grantResults' (as supplied to onPermissionsCallbackResult).

Note that calling request_permission on SDK_INT < 23 will return
immediately (as run-time permissions are not required), and so the callback
will never happen. Therefore, request_permissions should only be called
with a callback if calling check_permission has indicated that it is
necessary.

The attribute '_java_callback' is initially None, but is set when the first
permissions request is made. It is set to an instance of
onRequestPermissionsCallback, which allows the Java callback to be
propagated to the class method 'python_callback'. This is then, in turn,
used to call an application callback if provided to request_permissions.
"""
_java_callback = None
_callbacks = {1: None}
_callback_id = 1
_lock = threading.Lock()
gbm001 marked this conversation as resolved.
Show resolved Hide resolved

@classmethod
def register_callback(cls):
"""Register Java callback for requestPermissions."""
cls._java_callback = _onRequestPermissionsCallback(cls.python_callback)
python_activity = autoclass('org.kivy.android.PythonActivity')
python_activity.addPermissionsCallback(cls._java_callback)

@classmethod
def request_permissions(cls, permissions, callback=None):
"""Requests Android permissions from PythonActivity.
If 'callback' is supplied, the request is made with a new requestCode
and the callback is stored in the _callbacks dict. When a Java callback
with the matching requestCode is received, callback will be called
with arguments of 'permissions' and 'grantResults'.
"""
with cls._lock:
if not cls._java_callback:
cls.register_callback()
python_activity = autoclass('org.kivy.android.PythonActivity')
if not callback:
python_activity.requestPermissions(permissions)
else:
cls._callback_id += 1
python_activity.requestPermissionsWithRequestCode(
permissions, cls._callback_id)
cls._callbacks[cls._callback_id] = callback

@classmethod
def python_callback(cls, requestCode, permissions, grantResults):
"""Calls the relevant callback with arguments of 'permissions'
and 'grantResults'."""
if cls._callbacks.get(requestCode):
cls._callbacks[requestCode](permissions, grantResults)


# Public API methods for requesting permissions

def request_permissions(permissions, callback=None):
_RequestPermissionsManager.request_permissions(permissions, callback)


def request_permission(permission):
request_permissions([permission])
def request_permission(permission, callback=None):
request_permissions([permission], callback)


def check_permission(permission):
Expand Down