Je ne voulais utiliser aucune des bibliothèques disponibles pour les raisons expliquées dans la question, donc j'ai développé quelque chose moi-même.
Toutes mes activités nécessitant une ou plusieurs autorisations héritent d'un PermissionActivity
qui gère toutes les tâches liées aux autorisations.
Comment ça marche est votre activité appelle
if (checkHasPermission(Manifest.permission.ACCESS_FINE_LOCATION)) { }
de la classe parente. Si l'autorisation est déjà accordée, le code peut continuer. Si ce n'est pas le cas, la classe parent demandera l'autorisation et enverra les résultats à la classe enfant en utilisant une méthode abstraite et/ou une ou plusieurs méthodes remplaçables.
La classe parente
Cette classe peut être laissée inchangée, à l'exception des blocs de commutation dans messageForRationale()
et requestCodeForPermission()
. Mettez à jour ceux pour les autorisations dont votre application a besoin.
/**
* An activity that can be extended to simplify handling permissions.
* <p>
* Deriving classes will not have to write boilerplate code and code duplication between activities
* that share this functionality is avoided.
*/
public abstract class PermissionActivity extends AppCompatActivity {
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
// If multiple permissions were requested in one call, check if they were all granted.
if (requestCode == RequestCode.PERMISSION_MULTIPLE) {
boolean allPermissionsGranted = true;
for (int grantResult : grantResults) {
if (grantResult != PackageManager.PERMISSION_GRANTED) {
allPermissionsGranted = false;
}
}
if (allPermissionsGranted) {
onAllPermissionsGranted(permissions);
return;
}
}
// Else, check each one if it was granted/denied/blocked.
for (int i = 0; i < permissions.length; i++) {
if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
// User granted permission.
onPermissionGranted(permissions[i]);
} else {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, permissions[i])) {
// User denied permission.
onPermissionDenied(permissions[i]);
} else {
// User denied permission and checked 'never ask again'.
onPermissionBlocked(permissions[i]);
}
}
}
}
/**
* Checks if the app has the given permission(s).
* <p>
* If not, it will request them.
* <p>
* The method is called `checkHasPermission` to avoid the linter showing a warning in the
* child class when it's delegating permission checks to its parent class. See
* http://stackoverflow.com/questions/36031218/check-android-permissions-in-a
* -method/36193309#36193309 for details.
*/
public boolean checkHasPermission(int requestCode, String... permissions) {
if (!(permissions.length > 0)) {
throw new IllegalArgumentException("must request at least one permission");
}
if (requestCode == RequestCode.PERMISSION_MULTIPLE) {
List<String> permissions_ = new ArrayList<>();
for (String permission : permissions) {
if (ActivityCompat.checkSelfPermission(this, permission) !=
PackageManager.PERMISSION_GRANTED) {
permissions_.add(permission);
}
}
if (!permissions_.isEmpty()) {
requestPermissions(this, permissions_.toArray(new String[permissions_.size()]), requestCode);
return false;
} else {
return true;
}
} else {
if (ActivityCompat.checkSelfPermission(this, permissions[0]) !=
PackageManager.PERMISSION_GRANTED) {
requestPermissions(this, permissions, requestCode);
return false;
} else {
return true;
}
}
}
/**
* Requests the given permissions.
*/
private void requestPermissions(Activity activity, String permissions[], int resultCode) {
showRequestPermissionsDialog(activity, permissions, resultCode);
}
/**
* Called when a rationale (explanation why a permission is needed) should be shown to the user.
* <p>
* If the user clicks the positive button, the permission is requested again, otherwise the
* dialog is dismissed.
*/
public void showRationaleDialog(Activity activity, String permission, String message,
int resultCode) {
new AlertDialog.Builder(activity)
.setMessage(message)
.setPositiveButton("ok", (dialog, which) ->
showRequestPermissionDialog(activity, permission, resultCode))
.setNegativeButton("not now", (dialog, which) -> { /* Do nothing */ })
.show();
}
/**
* Requests a single permission.
*/
private void showRequestPermissionDialog(Activity activity, String permission, int resultCode) {
ActivityCompat.requestPermissions(activity, new String[]{permission}, resultCode);
}
/**
* Requests multiple permissions in one call.
*/
private void showRequestPermissionsDialog(Activity activity, String[] permissions,
int resultCode) {
ActivityCompat.requestPermissions(activity, permissions, resultCode);
}
/**
* Returns a message to be shown to the user that explains why a specific permission is
* required.
*/
public String messageForRationale(String permission) {
String s;
switch (permission) {
case Manifest.permission.READ_PHONE_STATE:
s = "access this device's state";
break;
case Manifest.permission.ACCESS_FINE_LOCATION:
s = "access the location of this device";
break;
case Manifest.permission.SEND_SMS:
s = "send text messages";
break;
default:
throw new IllegalArgumentException("Permission not handled: " + permission);
}
return String.format("MyApp needs permission to %s.", s);
}
/**
* Get the RequestCode for the given permission.
*/
public int requestCodeForPermission(String permission) {
int code;
switch (permission) {
case Manifest.permission.READ_PHONE_STATE:
code = RequestCode.PERMISSION_READ_PHONE_STATE;
break;
case Manifest.permission.ACCESS_FINE_LOCATION:
code = RequestCode.PERMISSION_FINE_LOCATION;
break;
case Manifest.permission.SEND_SMS:
code = RequestCode.PERMISSION_SEND_SMS;
break;
// TODO: add required permissions for your app
default:
throw new IllegalArgumentException("Permission not handled: " + permission);
}
return code;
}
/**
* Called if all requested permissions were granted in the same dialog.
* E.g. FINE_LOCATION and SEND_SMS were requested, and both were granted.
* <p>
* Child class can override this method if it wants to know when this happens.
* <p>
* Linter can show an unjust "call requires permission" warning in child class if a method that
* requires permission(s) is called. Silence it with `@SuppressWarnings("MissingPermission")`.
*/
protected void onAllPermissionsGranted(String[] permissions) {
}
/**
* Called for all permissions that were granted in the same dialog, in case not all were
* granted. E.g. if FINE_LOCATION, COARSE_LOCATION and SEND_SMS were requested and FINE_LOCATION
* was not granted but COARSE_LOCATION and SEND_SMS were, it will be called for COARSE_LOCATION
* and SEND_SMS.
* <p>
* Child class can override this method if it wants to know when this happens.
* <p>
* Linter can show an unjust "call requires permission" warning in child class if a method that
* requires permission(s) is called. Silence it with `@SuppressWarnings("MissingPermission")`.
*/
protected void onPermissionGranted(String permission) {
}
/**
* Called for all permissions that were denied in the same dialog, handled one by one.
* <p>
* Child class should not override this general behavior.
*/
protected void onPermissionDenied(String permission) {
String message = messageForRationale(permission);
showRationaleDialog(this, permission, message, requestCodeForPermission(permission));
}
/**
* Called for all permissions that were blocked in the same dialog, handled one by one.
* <p>
* Blocked means a user denied a permission with the 'never ask again' checkbox checked.
* <p>
* Child class must override and decide what to do when a permission is blocked.
*/
protected abstract void onPermissionBlocked(String permission);
}
La notation ->
sont lambda expressions.
RequestCode est une interface utilisée uniquement pour abstraire de chiffres:
public interface RequestCode {
int PERMISSION_READ_PHONE_STATE = 0;
int PERMISSION_FINE_LOCATION = 1;
int PERMISSION_SEND_SMS = 2;
int PERMISSION_MULTIPLE = 3;
}
vous pouvez modifier comme vous le souhaitez. N'utilisez pas de nombres supérieurs à 256. Si vous le faites, une exception sera émise indiquant
Vous ne pouvez utiliser que les 8 bits inférieurs pour le code de requête.
Dans les activités où l'autorisation est nécessaire, vous pouvez l'utiliser comme ceci (juste un exemple).Assurez-vous d'avoir l'activité extend PermissionActivity
private void callThisSomewhere() {
if (checkHasPermission(RequestCode.PERMISSION_READ_PHONE_STATE,
Manifest.permission.READ_PHONE_STATE)) {
tryDoStuffWithPhoneState();
}
}
@RequiresPermission(Manifest.permission.READ_PHONE_STATE)
private void doStuffWithPhoneState() {
// Do stuff.
}
@Override
public void onPermissionGranted(String permission) {
tryDoStuffWithPhoneState();
}
@Override
public void onPermissionBlocked(String permission) {
// Disable parts of app that require this permission.
}
Si vous demandez plusieurs autorisations en une seule fois, vous devez utiliser RequestCode.PERMISSION_MULTIPLE
. Sinon, seule la première autorisation sera demandée.
Les autorisations qui ne figurent pas dans AndroidManifest.xml seront automatiquement bloquées sans afficher la boîte de dialogue de l'utilisateur. Veillez donc à ajouter également les autorisations que vous devez demander dans le manifeste.
Limites
Cette solution est compatible avec des fragments ou des services.
solution très agréable. La modularisation d'Android n'est pas quelque chose dont on discute assez souvent. C'est presque comme si on oublie que c'est OOP –
Cela a été extrêmement utile. Merci beaucoup! Dans le cas où l'utilisateur bloque complètement la permission, quelle est votre solution de repli préférée? Désactivez-vous cette fonctionnalité et ne la touchez plus jamais, ou avez-vous un moyen de diriger l'utilisateur vers les paramètres pour accorder la permission? – AdamMc331
@ McAdam331 Oui, je désactiver la fonctionnalité, comme indiqué ici https://developer.android.com/training/permissions/requesting.html sous * Gérer la réponse à la demande d'autorisations * –