7

Je suis en train de mettre à jour mon application pour qu'elle soit compatible avec Android 6. Le modèle d'autorisation n'est pas très complexe en théorie, mais maintenant je suis en train de l'implémenter et je me retrouve à écrire le même code laid dans chacune de mes activités nécessitant des permissions.Comment éviter d'écrire le code d'erreur en double pour demander des autorisations?

Pour chaque permission, je dois, il y a une

if (ActivityCompat.checkSelfPermission(this, Manifest.permission.PERMISSION) != 
       PackageManager.PERMISSION_GRANTED) { 
} else { 
} 

puis dans le onRequestPermissionsResult je dois vérifier/filtrer les résultats de chaque demande et traduire quelque chose mon activité comprend.

Je suis en train de mettre à jour ma deuxième activité maintenant, et le code d'autorisation est si semblable au premier que cela ressemble presque à un copier-coller. Les lignes sont longues, le code est similaire, et il semble tout simplement moche.

Je ne veux pas utiliser une solution tierce, j'en ai essayé quelques-unes, mais je préférerais avoir un contrôle total sur le code. Par exemple, certaines bibliothèques ne supportent pas Java 8, que j'utilise dans mon projet.

Que puis-je faire pour éviter un tas de code dupliqué dans toutes mes activités?

Répondre

7

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.

+0

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 –

+0

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

+0

@ 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 * –

1

C'est ce que j'ai utilisé pour gérer les autorisations dans mon application.

classe parent pour permission

public class PermissionManager extends AppCompatActivity implements ActivityCompat.OnRequestPermissionsResultCallback { 
    private static final int REQUEST_CODE = 200; 
    private Activity context; 

    private String[] permissions; 
    private ArrayList<String> grantedPermissions = new ArrayList<>(); 
    private RequestedPermissionResultCallBack callBack; 

    @Override 
    public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) { 
     super.onCreate(savedInstanceState, persistentState); 
    } 

    @Override 
    protected void onStart() { 
     super.onStart(); 
     setContentView(R.layout.actvity_permission); 
     checkForRequiredPermission(getIntent().getStringArrayExtra(getString(R.string.permission))); 
    } 

    @Override 
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { 

     switch (requestCode) { 

      case REQUEST_CODE: 

       checkForGrantedPermissions(permissions, grantResults); 
       break; 
     } 

    } 


    @TargetApi(Build.VERSION_CODES.M) 
    private void checkForRequiredPermission(String[] permissions) { 
     ArrayList<String> requiredPermissionList = new ArrayList<>(); 
     for (String permission : permissions) { 
      if (ActivityCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) { 
       requiredPermissionList.add(permission); 
      } else { 
       grantedPermissions.add(permission); 
      } 
     } 

     if (requiredPermissionList.size() > 0) { 
      (this).requestPermissions(requiredPermissionList.toArray(new String[requiredPermissionList.size()]), REQUEST_CODE); 
     } else { 

      setResult(grantedPermissions); 
     } 

    } 


    public void checkForGrantedPermissions(String[] permissions, int[] grantResults) { 

     for (int i = 0; i < permissions.length; i++) { 
      if (grantResults[i] == PackageManager.PERMISSION_GRANTED) { 
       grantedPermissions.add(permissions[i]); 
      } 

     } 
     setResult(grantedPermissions); 

    } 

    private void setResult(ArrayList<String> grantedPermissions) { 
     Intent intent = new Intent(); 
     intent.putStringArrayListExtra(getString(R.string.granted_permission), grantedPermissions); 
     setResult(Activity.RESULT_OK, intent); 
     this.finish(); 
    } 
} 

Lorsque vous voulez vérifier l'appel d'autorisation cette classe comme celui-ci

private void checkForPermissions() { 
     Intent intent = new Intent(this, PermissionManager.class); 
     intent.putExtra(getString(R.string.permission), permission); 
     startActivityForResult(intent, AppConstants.PERMSION_REQUEST_CODE); 
    } 

Ici la permission est un tableau d'autorisation que vous souhaitez demander pour quelque chose comme ça

private String permission[] = new String[]{Manifest.permission.READ_PHONE_STATE, Manifest.permission.RECEIVE_SMS, Manifest.permission.READ_SMS}; 

et c'est le code pour onActivityResult

case AppConstants.PERMSION_REQUEST_CODE: 
        ArrayList<String> grantedPermissionList = data.getStringArrayListExtra(getString(R.string.granted_permission)); 

        if (grantedPermissionList != null && grantedPermissionList.size() > 0 && grantedPermissionList.contains(permission[0])) { 
         createRequest(); 

        } else { 
         showSettingsDialog(getString(R.string.permission_required)); 
        } 

        break; 
+0

Pouvez-vous formater le code et expliquer comment cela fonctionne? –

+0

@TimCastelijns ajouté le code pour demander la permission –

+0

pouvez-vous formater le code s'il vous plaît? C'est un peu difficile à lire –