perms);
}
private static final String TAG = "EasyPermissions";
/**
* Check if the calling context has a set of permissions.
*
* @param context the calling context.
* @param perms one ore more permissions, such as {@link Manifest.permission#CAMERA}.
* @return true if all permissions are already granted, false if at least one permission is not
* yet granted.
* @see Manifest.permission
*/
public static boolean hasPermissions(Context context, @NonNull String... perms) {
// Always return true for SDK < M, let the system deal with the permissions
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
Log.w(TAG, "hasPermissions: API version < M, returning true by default");
// DANGER ZONE!!! Changing this will break the library.
return true;
}
// Null context may be passed if we have detected Low API (less than M) so getting
// to this point with a null context should not be possible.
if (context == null) {
throw new IllegalArgumentException("Can't check permissions for null context");
}
for (String perm : perms) {
if (ContextCompat.checkSelfPermission(context, perm)
!= PackageManager.PERMISSION_GRANTED) {
return false;
}
}
return true;
}
/**
* Request permissions from an Activity with standard OK/Cancel buttons.
*
* @see #requestPermissions(Activity, String, int, int, int, String...)
*/
public static void requestPermissions(
@NonNull Activity host, @NonNull String rationale,
int requestCode, @NonNull String... perms) {
requestPermissions(host, rationale, android.R.string.ok, android.R.string.cancel,
requestCode, perms);
}
/**
* Request permissions from a Support Fragment with standard OK/Cancel buttons.
*
* @see #requestPermissions(Activity, String, int, int, int, String...)
*/
public static void requestPermissions(
@NonNull Fragment host, @NonNull String rationale,
int requestCode, @NonNull String... perms) {
requestPermissions(host, rationale, android.R.string.ok, android.R.string.cancel,
requestCode, perms);
}
/**
* Request permissions from a standard Fragment with standard OK/Cancel buttons.
*
* @see #requestPermissions(Activity, String, int, int, int, String...)
*/
public static void requestPermissions(
@NonNull android.app.Fragment host, @NonNull String rationale,
int requestCode, @NonNull String... perms) {
requestPermissions(host, rationale, android.R.string.ok, android.R.string.cancel,
requestCode, perms);
}
/**
* Request a set of permissions, showing rationale if the system requests it.
*
* @param host requesting context.
* @param rationale a message explaining why the application needs this set of permissions,
* will be displayed if the user rejects the request the first time.
* @param positiveButton custom text for positive button
* @param negativeButton custom text for negative button
* @param requestCode request code to track this request, must be < 256.
* @param perms a set of permissions to be requested.
* @see Manifest.permission
*/
public static void requestPermissions(
@NonNull Activity host, @NonNull String rationale,
@StringRes int positiveButton, @StringRes int negativeButton,
int requestCode, @NonNull String... perms) {
requestPermissions(PermissionHelper.newInstance(host), rationale,
positiveButton, negativeButton,
requestCode, perms);
}
/**
* Request permissions from a Support Fragment.
*
* @see #requestPermissions(Activity, String, int, int, int, String...)
*/
public static void requestPermissions(
@NonNull Fragment host, @NonNull String rationale,
@StringRes int positiveButton, @StringRes int negativeButton,
int requestCode, @NonNull String... perms) {
requestPermissions(PermissionHelper.newInstance(host), rationale,
positiveButton, negativeButton,
requestCode, perms);
}
/**
* @see #requestPermissions(Activity, String, int, int, int, String...)
*/
public static void requestPermissions(
@NonNull android.app.Fragment host, @NonNull String rationale,
@StringRes int positiveButton, @StringRes int negativeButton,
int requestCode, @NonNull String... perms) {
requestPermissions(PermissionHelper.newInstance(host), rationale,
positiveButton, negativeButton,
requestCode, perms);
}
private static void requestPermissions(
@NonNull PermissionHelper helper, @NonNull String rationale,
@StringRes int positiveButton, @StringRes int negativeButton,
int requestCode, @NonNull String... perms) {
// Check for permissions before dispatching the request
if (hasPermissions(helper.getContext(), perms)) {
notifyAlreadyHasPermissions(helper.getHost(), requestCode, perms);
return;
}
// Request permissions
helper.requestPermissions(rationale, positiveButton,
negativeButton, requestCode, perms);
}
/**
* Handle the result of a permission request, should be called from the calling {@link
* Activity}'s {@link ActivityCompat.OnRequestPermissionsResultCallback#onRequestPermissionsResult(int,
* String[], int[])} method.
*
* If any permissions were granted or denied, the {@code object} will receive the appropriate
* callbacks through {@link PermissionCallbacks} and methods annotated with {@link
* AfterPermissionGranted} will be run if appropriate.
*
* @param requestCode requestCode argument to permission result callback.
* @param permissions permissions argument to permission result callback.
* @param grantResults grantResults argument to permission result callback.
* @param receivers an array of objects that have a method annotated with {@link
* AfterPermissionGranted} or implement {@link PermissionCallbacks}.
*/
public static void onRequestPermissionsResult(int requestCode,
@NonNull String[] permissions,
@NonNull int[] grantResults,
@NonNull Object... receivers) {
// Make a collection of granted and denied permissions from the request.
List granted = new ArrayList<>();
List denied = new ArrayList<>();
for (int i = 0; i < permissions.length; i++) {
String perm = permissions[i];
if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
granted.add(perm);
} else {
denied.add(perm);
}
}
// iterate through all receivers
for (Object object : receivers) {
// Report granted permissions, if any.
if (!granted.isEmpty()) {
if (object instanceof PermissionCallbacks) {
((PermissionCallbacks) object).onPermissionsGranted(requestCode, granted);
}
}
// Report denied permissions, if any.
if (!denied.isEmpty()) {
if (object instanceof PermissionCallbacks) {
((PermissionCallbacks) object).onPermissionsDenied(requestCode, denied);
}
}
// If 100% successful, call annotated methods
if (!granted.isEmpty() && denied.isEmpty()) {
runAnnotatedMethods(object, requestCode);
}
}
}
/**
* Check if at least one permission in the list of denied permissions has been permanently
* denied (user clicked "Never ask again").
*
* @param host context requesting permissions.
* @param deniedPermissions list of denied permissions, usually from {@link
* PermissionCallbacks#onPermissionsDenied(int, List)}
* @return {@code true} if at least one permission in the list was permanently denied.
*/
public static boolean somePermissionPermanentlyDenied(@NonNull Activity host,
@NonNull List deniedPermissions) {
return PermissionHelper.newInstance(host)
.somePermissionPermanentlyDenied(deniedPermissions);
}
/**
* @see #somePermissionPermanentlyDenied(Activity, List)
*/
public static boolean somePermissionPermanentlyDenied(@NonNull Fragment host,
@NonNull List deniedPermissions) {
return PermissionHelper.newInstance(host)
.somePermissionPermanentlyDenied(deniedPermissions);
}
/**
* @see #somePermissionPermanentlyDenied(Activity, List).
*/
public static boolean somePermissionPermanentlyDenied(@NonNull android.app.Fragment host,
@NonNull List deniedPermissions) {
return PermissionHelper.newInstance(host)
.somePermissionPermanentlyDenied(deniedPermissions);
}
/**
* Check if a permission has been permanently denied (user clicked "Never ask again").
*
* @param host context requesting permissions.
* @param deniedPermission denied permission.
* @return {@code true} if the permissions has been permanently denied.
*/
public static boolean permissionPermanentlyDenied(@NonNull Activity host,
@NonNull String deniedPermission) {
return PermissionHelper.newInstance(host).permissionPermanentlyDenied(deniedPermission);
}
/**
* @see #permissionPermanentlyDenied(Activity, String)
*/
public static boolean permissionPermanentlyDenied(@NonNull Fragment host,
@NonNull String deniedPermission) {
return PermissionHelper.newInstance(host).permissionPermanentlyDenied(deniedPermission);
}
/**
* @see #permissionPermanentlyDenied(Activity, String).
*/
public static boolean permissionPermanentlyDenied(@NonNull android.app.Fragment host,
@NonNull String deniedPermission) {
return PermissionHelper.newInstance(host).permissionPermanentlyDenied(deniedPermission);
}
/**
* See if some denied permission has been permanently denied.
*
* @param host requesting context.
* @param perms array of permissions.
* @return true if the user has previously denied any of the {@code perms} and we should show a
* rationale, false otherwise.
*/
public static boolean somePermissionDenied(@NonNull Activity host,
@NonNull String... perms) {
return PermissionHelper.newInstance(host).somePermissionDenied(perms);
}
/**
* @see #somePermissionDenied(Activity, String...)
*/
public static boolean somePermissionDenied(@NonNull Fragment host,
@NonNull String... perms) {
return PermissionHelper.newInstance(host).somePermissionDenied(perms);
}
/**
* @see #somePermissionDenied(Activity, String...)
*/
public static boolean somePermissionDenied(@NonNull android.app.Fragment host,
@NonNull String... perms) {
return PermissionHelper.newInstance(host).somePermissionDenied(perms);
}
/**
* Run permission callbacks on an object that requested permissions but already has them by
* simulating {@link PackageManager#PERMISSION_GRANTED}.
*
* @param object the object requesting permissions.
* @param requestCode the permission request code.
* @param perms a list of permissions requested.
*/
private static void notifyAlreadyHasPermissions(@NonNull Object object,
int requestCode,
@NonNull String[] perms) {
int[] grantResults = new int[perms.length];
for (int i = 0; i < perms.length; i++) {
grantResults[i] = PackageManager.PERMISSION_GRANTED;
}
onRequestPermissionsResult(requestCode, perms, grantResults, object);
}
/**
* Find all methods annotated with {@link AfterPermissionGranted} on a given object with the
* correct requestCode argument.
*
* @param object the object with annotated methods.
* @param requestCode the requestCode passed to the annotation.
*/
private static void runAnnotatedMethods(@NonNull Object object, int requestCode) {
Class clazz = object.getClass();
if (isUsingAndroidAnnotations(object)) {
clazz = clazz.getSuperclass();
}
while (clazz != null) {
for (Method method : clazz.getDeclaredMethods()) {
AfterPermissionGranted ann = method.getAnnotation(AfterPermissionGranted.class);
if (ann != null) {
// Check for annotated methods with matching request code.
if (ann.value() == requestCode) {
// Method must be void so that we can invoke it
if (method.getParameterTypes().length > 0) {
throw new RuntimeException(
"Cannot execute method " + method.getName() + " because it is non-void method and/or has input parameters.");
}
try {
// Make method accessible if private
if (!method.isAccessible()) {
method.setAccessible(true);
}
method.invoke(object);
} catch (IllegalAccessException e) {
Log.e(TAG, "runDefaultMethod:IllegalAccessException", e);
} catch (InvocationTargetException e) {
Log.e(TAG, "runDefaultMethod:InvocationTargetException", e);
}
}
}
}
clazz = clazz.getSuperclass();
}
}
/**
* Determine if the project is using the AndroidAnnotations library.
*/
private static boolean isUsingAndroidAnnotations(@NonNull Object object) {
if (!object.getClass().getSimpleName().endsWith("_")) {
return false;
}
try {
Class clazz = Class.forName("org.androidannotations.api.view.HasViews");
return clazz.isInstance(object);
} catch (ClassNotFoundException e) {
return false;
}
}
}