Selon ceci: http://developer.Android.com/preview/features/runtime-permissions.html#coding une application peut rechercher des autorisations d'exécution et demander des autorisations si elle n'a pas déjà été accordée. Le dialogue suivant sera alors affiché:
Si l'utilisateur refuse une autorisation importante, une application doit afficher une explication de la raison pour laquelle l'autorisation est nécessaire et de l'impact de cette suppression. Ce dialogue a deux options:
Si l'utilisateur vérifie cependant Never ask again
, la deuxième boîte de dialogue contenant l'explication ne devrait pas être affichée, en particulier si l'utilisateur a déjà déjà refusé une fois auparavant ..____. Maintenant, la question est de savoir comment mon application sait si l'utilisateur a coché Never ask again
? OMI la onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)
ne me donne pas cette information.
Une deuxième question serait: Google envisage-t-il d'incorporer dans la boîte de dialogue des autorisations un message personnalisé expliquant pourquoi l'application a besoin de cette autorisation? De cette façon, il n’y aurait jamais de deuxième dialogue qui permettrait certainement un meilleur résultat.
Developer Preview 2 apporte quelques modifications à la manière dont les autorisations sont demandées par l'application (voir aussi http://developer.Android.com/preview/support.html#preview2-notes ).
Le premier dialogue ressemble maintenant à ceci:
Il n'y a pas de case à cocher "Ne plus montrer à nouveau" (contrairement à l'aperçu du développeur 1). Si l'utilisateur refuse l'autorisation et si l'autorisation est essentielle pour l'application, elle peut également présenter une autre boîte de dialogue pour expliquer la raison pour laquelle l'application demande cette autorisation, par exemple. comme ça:
Si l'utilisateur refuse à nouveau, l'application doit être fermée si elle a absolument besoin de cette autorisation ou continuer à fonctionner avec des fonctionnalités limitées. Si l'utilisateur reconsidère (et sélectionne réessayer), l'autorisation est à nouveau demandée. Cette fois, l'invite ressemble à ceci:
La deuxième fois, la case à cocher "Ne plus demander" est affichée. Si l'utilisateur refuse à nouveau et si la case à cocher est cochée, rien de plus ne devrait se produire . Si la case à cocher est cochée ou non, elle peut être déterminée en utilisant Activity.shouldShowRequestPermissionRationale (String), par exemple comme ça:
if (shouldShowRequestPermissionRationale(Manifest.permission.WRITE_CONTACTS)) {...
C’est ce que dit la documentation Android ( https://developer.Android.com/training/permissions/requesting.html ):
Pour vous aider à trouver les situations dans lesquelles vous devez fournir un supplément explication, le système fournit le Activity.shouldShowRequestPermissionRationale (String). Ce La méthode retourne true si l'application a demandé cette autorisation précédemment et l'utilisateur a refusé la demande. Cela indique que vous devrait probablement expliquer à l'utilisateur pourquoi vous avez besoin de cette permission.
Si l'utilisateur a précédemment refusé la demande d'autorisation et choisi l'option Ne plus demander dans la boîte de dialogue du système de demande d'autorisation, cette méthode retourne false. La méthode retourne également false si le périphérique La politique interdit à l'application d'avoir cette autorisation.
Pour savoir si l'utilisateur a refusé avec "ne plus jamais demander", vous pouvez vérifier à nouveau la méthode shouldShowRequestPermissionRationale dans votre onRequestPermissionsResult lorsque l'utilisateur n'a pas accordé l'autorisation.
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (requestCode == REQUEST_PERMISSION) {
// for each permission check if the user granted/denied them
// you may want to group the rationale in a single dialog,
// this is just an example
for (int i = 0, len = permissions.length; i < len; i++) {
String permission = permissions[i];
if (grantResults[i] == PackageManager.PERMISSION_DENIED) {
// user rejected the permission
boolean showRationale = shouldShowRequestPermissionRationale( permission );
if (! showRationale) {
// user also CHECKED "never ask again"
// you can either enable some fall back,
// disable features of your app
// or open another dialog explaining
// again the permission and directing to
// the app setting
} else if (Manifest.permission.WRITE_CONTACTS.equals(permission)) {
showRationale(permission, R.string.permission_denied_contacts);
// user did NOT check "never ask again"
// this is a good place to explain the user
// why you need the permission and ask if he wants
// to accept it (the rationale)
} else if ( /* possibly check more permissions...*/ ) {
}
}
}
}
}
Vous pouvez ouvrir les paramètres de votre application avec ce code:
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", getPackageName(), null);
intent.setData(uri);
startActivityForResult(intent, REQUEST_PERMISSION_SETTING);
Il n’existe aucun moyen d’envoyer l’utilisateur directement à la page Autorisation.
Vous pouvez vérifier shouldShowRequestPermissionRationale()
dans votre onRequestPermissionsResult()
.
https://youtu.be/C8lUdPVSzDk?t=2m23s
Vérifiez si la permission a été accordée ou non dans onRequestPermissionsResult()
. Si not, cochez shouldShowRequestPermissionRationale()
.
true
, montrez une explication indiquant pourquoi cette autorisation particulière est nécessaire. Ensuite, en fonction du choix de l'utilisateur, requestPermissions()
.false
, affichez un message d'erreur indiquant que l'autorisation n'a pas été accordée et que l'application ne peut pas continuer ou qu'une fonctionnalité particulière est désactivée.Vous trouverez ci-dessous un exemple de code.
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case STORAGE_PERMISSION_REQUEST:
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permission was granted :)
downloadFile();
} else {
// permission was not granted
if (getActivity() == null) {
return;
}
if (ActivityCompat.shouldShowRequestPermissionRationale(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
showStoragePermissionRationale();
} else {
Snackbar snackbar = Snackbar.make(getView(), getResources().getString(R.string.message_no_storage_permission_snackbar), Snackbar.LENGTH_LONG);
snackbar.setAction(getResources().getString(R.string.settings), new View.OnClickListener() {
@Override
public void onClick(View v) {
if (getActivity() == null) {
return;
}
Intent intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", getActivity().getPackageName(), null);
intent.setData(uri);
OrderDetailFragment.this.startActivity(intent);
}
});
snackbar.show();
}
}
break;
}
}
Apparemment, Google Maps fait exactement cela pour obtenir une autorisation de localisation.
Voici une méthode simple et conviviale pour vérifier l’état actuel des autorisations:
@Retention(RetentionPolicy.SOURCE)
@IntDef({GRANTED, DENIED, BLOCKED_OR_NEVER_ASKED })
public @interface PermissionStatus {}
public static final int GRANTED = 0;
public static final int DENIED = 1;
public static final int BLOCKED_OR_NEVER_ASKED = 2;
@PermissionStatus
public static int getPermissionStatus(Activity activity, String androidPermissionName) {
if(ContextCompat.checkSelfPermission(activity, androidPermissionName) != PackageManager.PERMISSION_GRANTED) {
if(!ActivityCompat.shouldShowRequestPermissionRationale(activity, androidPermissionName)){
return BLOCKED_OR_NEVER_ASKED;
}
return DENIED;
}
return GRANTED;
}
Caveat: renvoie BLOCKED_OR_NEVER_ASKED au premier démarrage de l'application, avant que l'utilisateur n'accepte/refuse l'autorisation par l'intermédiaire de l'invite de l'utilisateur (sur les périphériques sdk 23+)
Mettre à jour:
La bibliothèque de support Android semble également avoir une classe très similaire Android.support.v4.content.PermissionChecker
qui contient une checkSelfPermission()
qui retourne:
public static final int PERMISSION_GRANTED = 0;
public static final int PERMISSION_DENIED = -1;
public static final int PERMISSION_DENIED_APP_OP = -2;
Vous pouvez déterminer en vérifiant si l'autorisation justification doit être affichée dans la méthode de rappel onRequestPermissionsResult()
. Et si vous trouvez une autorisation définie sur ne plus demander à nouveau, vous pouvez demander aux utilisateurs d'accorder des autorisations à partir des paramètres.
Ma mise en œuvre complète serait comme ci-dessous. Cela fonctionne à la fois pour les requêtes single ou multiple. Utilisez les éléments suivants ou utilisez directement ma bibliothèque.
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if(permissions.length == 0){
return;
}
boolean allPermissionsGranted = true;
if(grantResults.length>0){
for(int grantResult: grantResults){
if(grantResult != PackageManager.PERMISSION_GRANTED){
allPermissionsGranted = false;
break;
}
}
}
if(!allPermissionsGranted){
boolean somePermissionsForeverDenied = false;
for(String permission: permissions){
if(ActivityCompat.shouldShowRequestPermissionRationale(this, permission)){
//denied
Log.e("denied", permission);
}else{
if(ActivityCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED){
//allowed
Log.e("allowed", permission);
} else{
//set to never ask again
Log.e("set to never ask again", permission);
somePermissionsForeverDenied = true;
}
}
}
if(somePermissionsForeverDenied){
final AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
alertDialogBuilder.setTitle("Permissions Required")
.setMessage("You have forcefully denied some of the required permissions " +
"for this action. Please open settings, go to permissions and allow them.")
.setPositiveButton("Settings", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
Uri.fromParts("package", getPackageName(), null));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
})
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
})
.setCancelable(false)
.create()
.show();
}
} else {
switch (requestCode) {
//act according to the request code used while requesting the permission(s).
}
}
}
Peut être utile pour quelqu'un: -
Ce que j'ai remarqué, c'est que, si nous vérifions l'indicateur shouldShowRequestPermissionRationale () dans la méthode de rappel onRequestPermissionsResult (), il n'affiche que deux états.
Etat 1: -Return true: - Chaque fois que l'utilisateur clique sur Refuser les autorisations (y compris la toute première fois).
Etat 2: -Retourne false: - si l'utilisateur sélectionne «ne demande jamais plus".
Une fois que l'utilisateur a marqué "Ne plus demander", la question ne peut plus être affichée . Mais vous pouvez expliquer à l'utilisateur qu'il a déjà refusé l'autorisation et qu'il doit accorder l'autorisation dans les paramètres. Et référencez-le aux paramètres, avec le code suivant:
@Override
public void onRequestPermissionsResult(int permsRequestCode, String[] permissions, int[] grantResults) {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// now, you have permission go ahead
// TODO: something
} else {
if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,
Manifest.permission.READ_CALL_LOG)) {
// now, user has denied permission (but not permanently!)
} else {
// now, user has denied permission permanently!
Snackbar snackbar = Snackbar.make(findViewById(Android.R.id.content), "You have previously declined this permission.\n" +
"You must approve this permission in \"Permissions\" in the app settings on your device.", Snackbar.LENGTH_LONG).setAction("Settings", new View.OnClickListener() {
@Override
public void onClick(View view) {
startActivity(new Intent(Android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.parse("package:" + BuildConfig.APPLICATION_ID)));
}
});
View snackbarView = snackbar.getView();
TextView textView = (TextView) snackbarView.findViewById(Android.support.design.R.id.snackbar_text);
textView.setMaxLines(5); //Or as much as you need
snackbar.show();
}
}
return;
}
Si vous souhaitez détecter tous les "états" (refusés pour la première fois, refusés, refusés avec "jamais demander à nouveau" ou refusés en permanence), vous pouvez procéder comme suit:
Créer 2 booléens
private boolean beforeClickPermissionRat;
private boolean afterClickPermissionRat;
Définissez le premier avant de demander la permission:
beforeClickPermissionRat = shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE);
Définissez le second dans votre méthode onRequestPermissionsResult:
afterClickPermissionRat = shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE);
Utilisez le "tableau" suivant pour faire tout ce dont vous avez besoin dans onRequestPermissionsResult () (après avoir vérifié que vous n'avez toujours pas l'autorisation):
// before after
// FALSE FALSE = Was denied permanently, still denied permanently --> App Settings
// FALSE TRUE = First time deny, not denied permanently yet --> Nothing
// TRUE FALSE = Just been permanently denied --> Changing my caption to "Go to app settings to edit permissions"
// TRUE TRUE = Wasn't denied permanently, still not denied permanently --> Nothing
J'ai eu le même problème et je l'ai compris. Pour simplifier la vie, j'ai écrit une classe util permettant de gérer les autorisations d'exécution.
public class PermissionUtil {
/*
* Check if version is Marshmallow and above.
* Used in deciding to ask runtime permission
* */
public static boolean shouldAskPermission() {
return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M);
}
private static boolean shouldAskPermission(Context context, String permission){
if (shouldAskPermission()) {
int permissionResult = ActivityCompat.checkSelfPermission(context, permission);
if (permissionResult != PackageManager.PERMISSION_GRANTED) {
return true;
}
}
return false;
}
public static void checkPermission(Context context, String permission, PermissionAskListener listener){
/*
* If permission is not granted
* */
if (shouldAskPermission(context, permission)){
/*
* If permission denied previously
* */
if (((Activity)context).shouldShowRequestPermissionRationale(permission)) {
listener.onPermissionPreviouslyDenied();
} else {
/*
* Permission denied or first time requested
* */
if (PreferencesUtil.isFirstTimeAskingPermission(context, permission)) {
PreferencesUtil.firstTimeAskingPermission(context, permission, false);
listener.onPermissionAsk();
} else {
/*
* Handle the feature without permission or ask user to manually allow permission
* */
listener.onPermissionDisabled();
}
}
} else {
listener.onPermissionGranted();
}
}
/*
* Callback on various cases on checking permission
*
* 1. Below M, runtime permission not needed. In that case onPermissionGranted() would be called.
* If permission is already granted, onPermissionGranted() would be called.
*
* 2. Above M, if the permission is being asked first time onPermissionAsk() would be called.
*
* 3. Above M, if the permission is previously asked but not granted, onPermissionPreviouslyDenied()
* would be called.
*
* 4. Above M, if the permission is disabled by device policy or the user checked "Never ask again"
* check box on previous request permission, onPermissionDisabled() would be called.
* */
public interface PermissionAskListener {
/*
* Callback to ask permission
* */
void onPermissionAsk();
/*
* Callback on permission denied
* */
void onPermissionPreviouslyDenied();
/*
* Callback on permission "Never show again" checked and denied
* */
void onPermissionDisabled();
/*
* Callback on permission granted
* */
void onPermissionGranted();
}
}
Et les méthodes PreferenceUtil sont les suivantes.
public static void firstTimeAskingPermission(Context context, String permission, boolean isFirstTime){
SharedPreferences sharedPreference = context.getSharedPreferences(PREFS_FILE_NAME, MODE_PRIVATE;
sharedPreference.edit().putBoolean(permission, isFirstTime).apply();
}
public static boolean isFirstTimeAskingPermission(Context context, String permission){
return context.getSharedPreferences(PREFS_FILE_NAME, MODE_PRIVATE).getBoolean(permission, true);
}
Maintenant, tout ce dont vous avez besoin est d’utiliser la méthode * checkPermission * avec les arguments appropriés.
Voici un exemple,
PermissionUtil.checkPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE,
new PermissionUtil.PermissionAskListener() {
@Override
public void onPermissionAsk() {
ActivityCompat.requestPermissions(
thisActivity,
new String[]{Manifest.permission.READ_CONTACTS},
REQUEST_EXTERNAL_STORAGE
);
}
@Override
public void onPermissionPreviouslyDenied() {
//show a dialog explaining permission and then request permission
}
@Override
public void onPermissionDisabled() {
Toast.makeText(context, "Permission Disabled.", Toast.LENGTH_SHORT).show();
}
@Override
public void onPermissionGranted() {
readContacts();
}
});
comment mon application sait-elle si l'utilisateur a coché la case "Plus jamais demander"?
Si l'utilisateur a coché ne plus jamais demander , vous recevrez un rappel sur onPermissionDisabled.
Bonne codage :)
Explication complète pour chaque cas d'autorisation
/**
* Case 1: User doesn't have permission
* Case 2: User has permission
*
* Case 3: User has never seen the permission Dialog
* Case 4: User has denied permission once but he din't clicked on "Never Show again" check box
* Case 5: User denied the permission and also clicked on the "Never Show again" check box.
* Case 6: User has allowed the permission
*
*/
public void handlePermission() {
if (ContextCompat.checkSelfPermission(MainActivity.this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
// This is Case 1. Now we need to check further if permission was shown before or not
if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
// This is Case 4.
} else {
// This is Case 3. Request for permission here
}
} else {
// This is Case 2. You have permission now you can do anything related to it
}
}
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// This is Case 2 (Permission is now granted)
} else {
// This is Case 1 again as Permission is not granted by user
//Now further we check if used denied permanently or not
if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
// case 4 User has denied permission but not permanently
} else {
// case 5. Permission denied permanently.
// You can open Permission setting's page from here now.
}
}
}
J'ai rédigé un raccourci pour la demande d'autorisation dans Android M. Ce code gère également la compatibilité avec les versions antérieures d'Android.
Tout le code laid est extrait dans un fragment qui s'attache et se détache de l'activité demandant les autorisations. Vous pouvez utiliser PermissionRequestManager
comme suit:
new PermissionRequestManager()
// We need a AppCompatActivity here, if you are not using support libraries you will have to slightly change
// the PermissionReuqestManager class
.withActivity(this)
// List all permissions you need
.withPermissions(Android.Manifest.permission.CALL_PHONE, Android.Manifest.permission.READ_CALENDAR)
// This Runnable is called whenever the request was successfull
.withSuccessHandler(new Runnable() {
@Override
public void run() {
// Do something with your permissions!
// This is called after the user has granted all
// permissions, we are one a older platform where
// the user does not need to grant permissions
// manually, or all permissions are already granted
}
})
// Optional, called when the user did not grant all permissions
.withFailureHandler(new Runnable() {
@Override
public void run() {
// This is called if the user has rejected one or all of the requested permissions
L.e(this.getClass().getSimpleName(), "Unable to request permission");
}
})
// After calling this, the user is prompted to grant the rights
.request();
Jetez un coup d'œil: https://Gist.github.com/crysxd/385b57d74045a8bd67c4110c34ab74aa
Une fonction utile pour déterminer si une demande de permission arbitraire a été bloquée (en Kotlin):
private fun isPermissionBlockedFromAsking(activity: Activity, permission: String): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return ContextCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED
&& !activity.shouldShowRequestPermissionRationale(permission)
&& PreferenceManager.getDefaultSharedPreferences(activity).getBoolean(permission, false)
}
return false
}
Pour cela, vous devez définir un booléen de préférence partagée avec le nom de votre autorisation souhaitée (par exemple, Android.Manifest.permission.READ_PHONE_STATE
) sur true
lors de votre première demande d'autorisation.
Explication:
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
car une partie du code ne peut être exécutée que sur l'API de niveau 23+.
ContextCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED
pour vérifier que nous n'avons pas encore l'autorisation.
!activity.shouldShowRequestPermissionRationale(permission)
pour vérifier si l'utilisateur a refusé l'application à nouveau. En raison de des bizarreries de cette fonction , la ligne suivante est également requise.
PreferenceManager.getDefaultSharedPreferences(activity).getBoolean(permission, false)
Ceci est utilisé (avec la définition de la valeur sur true lors de la première demande d'autorisation) pour distinguer les états "jamais demandé" et "ne plus demander", car la ligne précédente ne renvoie pas cette information.
La méthode shouldShowRequestPermissionRationale () peut permettre à l'utilisateur de vérifier si l'utilisateur a sélectionné l'option "ne plus jamais demander" et a refusé l'autorisation. un objectif, car je pense que son nom et sa mise en œuvre compliquent davantage la situation.
Comme expliqué dans Demander des autorisations au moment de l'exécution , cette méthode renvoie true si l'option 'ne plus jamais demander' est visible, false sinon; il renvoie donc false dès la première fois qu'une boîte de dialogue est affichée, puis à partir de la deuxième fois, il renvoie true et si l'utilisateur refuse l'autorisation en sélectionnant l'option, il renvoie à nouveau false.
Pour détecter un tel cas, vous pouvez détecter la séquence false-true-false ou (plus simplement) un indicateur qui garde la trace de la première fois que la boîte de dialogue est affichée. Après cela, cette méthode renvoie true ou false, la valeur false vous permettant de détecter le moment où l'option est sélectionnée.
Essayez cette simple bibliothèque de permissions. Il gérera toutes les opérations liées aux autorisations en 3 étapes faciles. Cela m'a fait gagner du temps. Vous pouvez terminer tous les travaux liés aux autorisations en 15 minutes.
Il peut gérer refuser, il peut gérer ne plus jamais demander, il peut appeler les paramètres de l'application pour obtenir une autorisation, il peut donner un message rationnel, il peut donner un message de refus, il peut donner une liste des autorisations acceptées autorisations et etc.
https://github.com/ParkSangGwon/TedPermission
Etape 1: ajoutez votre dépendance
dependencies {
compile 'gun0912.ted:tedpermission:2.1.1'
//check the above link for latest libraries
}
Step2: Demander les autorisations
TedPermission.with(this)
.setPermissionListener(permissionlistener)
.setDeniedMessage("If you reject permission,you can not use this service\n\nPlease turn on permissions at [Setting] > [Permission]")
.setPermissions(Manifest.permission.READ_CONTACTS, Manifest.permission.ACCESS_FINE_LOCATION)
.check();
Étape 3: Gestion de la réponse à l'autorisation
PermissionListener permissionlistener = new PermissionListener() {
@Override
public void onPermissionGranted() {
Toast.makeText(MainActivity.this, "Permission Granted", Toast.LENGTH_SHORT).show();
}
@Override
public void onPermissionDenied(ArrayList<String> deniedPermissions) {
Toast.makeText(MainActivity.this, "Permission Denied\n" + deniedPermissions.toString(), Toast.LENGTH_SHORT).show();
}
};
Vous pouvez utiliser
shouldShowRequestPermissionRationale()
à l'intérieur
onRequestPermissionsResult()
Voir l'exemple ci-dessous:
Vérifiez si l'utilisateur a l'autorisation lorsque l'utilisateur clique sur le bouton:
@Override
public void onClick(View v) {
if (v.getId() == R.id.appCompatBtn_changeProfileCoverPhoto) {
if (Build.VERSION.SDK_INT < 23) { // API < 23 don't need to ask permission
navigateTo(MainActivity.class); // Navigate to activity to change photos
} else {
if (ContextCompat.checkSelfPermission(SettingsActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
// Permission is not granted yet. Ask for permission...
requestWriteExternalPermission();
} else {
// Permission is already granted, good to go :)
navigateTo(MainActivity.class);
}
}
}
}
Lorsque l'utilisateur répondra à la boîte de dialogue d'autorisation, nous irons à onRequestPermissionResult:
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == WRITE_EXTERNAL_PERMISSION_REQUEST_CODE) {
// Case 1. Permission is granted.
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
if (ContextCompat.checkSelfPermission(SettingsActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
== PackageManager.PERMISSION_GRANTED) {
// Before navigating, I still check one more time the permission for good practice.
navigateTo(MainActivity.class);
}
} else { // Case 2. Permission was refused
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
// Case 2.1. shouldShowRequest... returns true because the
// permission was denied before. If it is the first time the app is running we will
// end up in this part of the code. Because he need to deny at least once to get
// to onRequestPermissionsResult.
Snackbar snackbar = Snackbar.make(findViewById(R.id.relLayout_container), R.string.you_must_verify_permissions_to_send_media, Snackbar.LENGTH_LONG);
snackbar.setAction("VERIFY", new View.OnClickListener() {
@Override
public void onClick(View v) {
ActivityCompat.requestPermissions(SettingsActivity.this
, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}
, WRITE_EXTERNAL_PERMISSION_REQUEST_CODE);
}
});
snackbar.show();
} else {
// Case 2.2. Permission was already denied and the user checked "Never ask again".
// Navigate user to settings if he choose to allow this time.
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(R.string.instructions_to_turn_on_storage_permission)
.setPositiveButton(getString(R.string.settings), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Intent settingsIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", getPackageName(), null);
settingsIntent.setData(uri);
startActivityForResult(settingsIntent, 7);
}
})
.setNegativeButton(getString(R.string.not_now), null);
Dialog dialog = builder.create();
dialog.show();
}
}
}
}
S'il vous plaît, ne me lancez pas de pierres pour cette solution.
Cela fonctionne mais est un peu "hacky".
Lorsque vous appelez requestPermissions
, enregistrez l’heure actuelle.
mAskedPermissionTime = System.currentTimeMillis();
Puis dans onRequestPermissionsResult
si le résultat n'est pas obtenu, vérifiez à nouveau l'heure.
if (System.currentTimeMillis() - mAskedPermissionTime < 100)
Puisque l'utilisateur n'a pas pu cliquer aussi rapidement sur le bouton de refus, nous savons qu'il a sélectionné "ne plus jamais demander" car le rappel est instantané.
Utilisez à vos risques et périls.
vous pouvez écouter jolie.
Auditeur
interface PermissionListener {
fun onNeedPermission()
fun onPermissionPreviouslyDenied(numberDenyPermission: Int)
fun onPermissionDisabledPermanently(numberDenyPermission: Int)
fun onPermissionGranted()
}
MainClass pour permission
class PermissionUtil {
private val PREFS_FILENAME = "permission"
private val TAG = "PermissionUtil"
private fun shouldAskPermission(context: Context, permission: String): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val permissionResult = ActivityCompat.checkSelfPermission(context, permission)
if (permissionResult != PackageManager.PERMISSION_GRANTED) {
return true
}
}
return false
}
fun checkPermission(context: Context, permission: String, listener: PermissionListener) {
Log.i(TAG, "CheckPermission for $permission")
if (shouldAskPermission(context, permission)) {
// Load history permission
val sharedPreference = context.getSharedPreferences(PREFS_FILENAME, 0)
val numberShowPermissionDialog = sharedPreference.getInt(permission, 0)
if (numberShowPermissionDialog == 0) {
(context as? Activity)?.let {
if (ActivityCompat.shouldShowRequestPermissionRationale(it, permission)) {
Log.e(TAG, "User has denied permission but not permanently")
listener.onPermissionPreviouslyDenied(numberShowPermissionDialog)
} else {
Log.e(TAG, "Permission denied permanently.")
listener.onPermissionDisabledPermanently(numberShowPermissionDialog)
}
} ?: kotlin.run {
listener.onNeedPermission()
}
} else {
// Is FirstTime
listener.onNeedPermission()
}
// Save history permission
sharedPreference.edit().putInt(permission, numberShowPermissionDialog + 1).apply()
} else {
listener.onPermissionGranted()
}
}
}
Utilisé de cette façon
PermissionUtil().checkPermission(this, Manifest.permission.ACCESS_FINE_LOCATION,
object : PermissionListener {
override fun onNeedPermission() {
log("---------------------->onNeedPermission")
// ActivityCompat.requestPermissions(this@SplashActivity,
// Array(1) { Manifest.permission.ACCESS_FINE_LOCATION },
// 118)
}
override fun onPermissionPreviouslyDenied(numberDenyPermission: Int) {
log("---------------------->onPermissionPreviouslyDenied")
}
override fun onPermissionDisabledPermanently(numberDenyPermission: Int) {
log("---------------------->onPermissionDisabled")
}
override fun onPermissionGranted() {
log("---------------------->onPermissionGranted")
}
})
override onRequestPermissionsResult en activité ou fragmnet
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
if (requestCode == 118) {
if (permissions[0] == Manifest.permission.ACCESS_FINE_LOCATION && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
getLastLocationInMap()
}
}
}
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
switch (requestCode) {
case PERMISSIONS_REQUEST_EXTERNAL_STORAGE: {
if (grantResults.length > 0) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
// Denied
} else {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
// To what you want
} else {
// Bob never checked click
}
}
}
}
}
}
En développant la réponse de mVck ci-dessus, la logique suivante détermine si "Jamais demander à nouveau" a été vérifié pour une demande de permission donnée:
bool bStorage = grantResults[0] == Permission.Granted;
bool bNeverAskForStorage =
!bStorage && (
_bStorageRationaleBefore == true && _bStorageRationaleAfter == false ||
_bStorageRationaleBefore == false && _bStorageRationaleAfter == false
);
qui est extrait d'en bas (pour l'exemple complet voir cette réponse )
private bool _bStorageRationaleBefore;
private bool _bStorageRationaleAfter;
private const int Android_PERMISSION_REQUEST_CODE__SDCARD = 2;
//private const int Android_PERMISSION_REQUEST_CODE__CAMERA = 1;
private const int Android_PERMISSION_REQUEST_CODE__NONE = 0;
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Permission[] grantResults)
{
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode)
{
case Android_PERMISSION_REQUEST_CODE__SDCARD:
_bStorageRationaleAfter = ShouldShowRequestPermissionRationale(Android.Manifest.Permission.WriteExternalStorage);
bool bStorage = grantResults[0] == Permission.Granted;
bool bNeverAskForStorage =
!bStorage && (
_bStorageRationaleBefore == true && _bStorageRationaleAfter == false ||
_bStorageRationaleBefore == false && _bStorageRationaleAfter == false
);
break;
}
}
private List<string> GetRequiredPermissions(out int requestCode)
{
// Android v6 requires explicit permission granting from user at runtime for security reasons
requestCode = Android_PERMISSION_REQUEST_CODE__NONE; // 0
List<string> requiredPermissions = new List<string>();
_bStorageRationaleBefore = ShouldShowRequestPermissionRationale(Android.Manifest.Permission.WriteExternalStorage);
Permission writeExternalStoragePerm = ApplicationContext.CheckSelfPermission(Android.Manifest.Permission.WriteExternalStorage);
//if(extStoragePerm == Permission.Denied)
if (writeExternalStoragePerm != Permission.Granted)
{
requestCode |= Android_PERMISSION_REQUEST_CODE__SDCARD;
requiredPermissions.Add(Android.Manifest.Permission.WriteExternalStorage);
}
return requiredPermissions;
}
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Android v6 requires explicit permission granting from user at runtime for security reasons
int requestCode;
List<string> requiredPermissions = GetRequiredPermissions(out requestCode);
if (requiredPermissions != null && requiredPermissions.Count > 0)
{
if (requestCode >= Android_PERMISSION_REQUEST_CODE__SDCARD)
{
_savedInstanceState = savedInstanceState;
RequestPermissions(requiredPermissions.ToArray(), requestCode);
return;
}
}
}
OnCreate2(savedInstanceState);
}
Au lieu de cela, vous recevrez un rappel sur onRequestPermissionsResult()
sous la forme PERMISSION_DENIED lorsque vous redemanderez une autorisation alors que vous tombez dans une condition fausse de shouldShowRequestPermissionRationale()
De doc Android:
Lorsque le système demande à l'utilisateur d'accorder une autorisation, l'utilisateur a la possibilité de demander au système de ne plus demander cette autorisation. Dans ce cas, chaque fois qu'une application utilise requestPermissions()
pour demander à nouveau cette autorisation, le système refuse immédiatement la demande. Le système appelle votre méthode de rappel onRequestPermissionsResult()
et transmet PERMISSION_DENIED
de la même manière que si l'utilisateur avait explicitement rejeté votre demande. Cela signifie que lorsque vous appelez requestPermissions()
, vous ne pouvez pas supposer qu'une interaction directe avec l'utilisateur a eu lieu.
Pour répondre à la question avec précision, Que se passe-t-il lorsque l'utilisateur appuie sur "Ne plus demander"?
La méthode/fonction remplacée
onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray)
Le tableau grantResult est vide, vous pouvez donc faire quelque chose? Mais pas la meilleure pratique.
Comment gérer "ne jamais demander à nouveau"?
Je travaille avec Fragment, qui nécessitait l'autorisation READ_EXTERNAL_STORAGE.
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
when {
isReadPermissionsGranted() -> {
/**
* Permissions has been Granted
*/
getDirectories()
}
isPermissionDeniedBefore() -> {
/**
* User has denied before, explain why we need the permission and ask again
*/
updateUIForDeniedPermissions()
checkIfPermissionIsGrantedNow()
}
else -> {
/**
* Need to ask For Permissions, First Time
*/
checkIfPermissionIsGrantedNow()
/**
* If user selects, "Dont Ask Again" it will never ask again! so just update the UI for Denied Permissions
*/
updateUIForDeniedPermissions()
}
}
}
Les autres fonctions sont triviales.
// Is Read Write Permissions Granted
fun isReadWritePermissionGranted(context: Context): Boolean {
return (ContextCompat.checkSelfPermission(
context as Activity,
Manifest.permission.READ_EXTERNAL_STORAGE
) == PackageManager.PERMISSION_GRANTED) and
(ContextCompat.checkSelfPermission(
context,
Manifest.permission.WRITE_EXTERNAL_STORAGE
) == PackageManager.PERMISSION_GRANTED)
}
fun isReadPermissionDenied(context: Context) : Boolean {
return ActivityCompat.shouldShowRequestPermissionRationale(
context as Activity,
PermissionsUtils.READ_EXTERNAL_STORAGE_PERMISSIONS)
}
vous pouvez lire le document officiel Android Demander des autorisations d'applications
ou vous pouvez trouver de nombreuses bibliothèques de permission Android populaires sur Github
Je voudrais aussi obtenir l'information si l'utilisateur a sélectionné ou non "ne plus jamais demander". J'ai atteint une "presque solution" avec un drapeau qui a l'air laid, mais avant de vous dire comment, je vais vous parler de ma motivation:
Je voudrais offrir la fonctionnalité de référence de permission initialement. Si l'utilisateur l'utilise sans droits, il obtient le 1 er dialogue par le haut ou les 2 et 3. Lorsque l'utilisateur a choisi "Ne plus jamais demander", je souhaite désactiver la fonctionnalité et l'afficher différemment. - Mon action est déclenchée par une entrée de texte en rotation. J'aimerais également ajouter '(Autorisation révoquée)' au texte de l'étiquette affiché. Cela montre à l'utilisateur: "Il existe une fonctionnalité, mais je ne peux pas l'utiliser en raison de mes paramètres d'autorisation." Cependant, cela ne semble pas être possible, car je ne peux pas vérifier si l'option «Ne plus demander» a été choisie.
Je suis arrivé à une solution avec laquelle je peux vivre en ayant toujours activé ma fonctionnalité avec une vérification active des autorisations. Je montre un message Toast dans onRequestPermissionsResult () en cas de réponse négative, mais uniquement si je n'ai pas affiché ma fenêtre contextuelle de justification personnalisée. Ainsi, si l'utilisateur a choisi de ne plus jamais demander, il reçoit uniquement un message de pain grillé. Si l'utilisateur hésite à choisir de ne plus jamais demander, il obtient uniquement la justification personnalisée et la demande de permission par le système d'exploitation, mais pas de pain grillé, car trois notifications consécutives seraient trop pénibles.
Je dois implémenter une autorisation dynamique pour la caméra. Lorsque 3 cas possibles se produisent: 1. Autoriser, 2. Refusé, 3. Ne plus demander.
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
for (String permission : permissions) {
if (ActivityCompat.shouldShowRequestPermissionRationale(getActivity(), permission)) {
//denied
Log.e("denied", permission);
} else {
if (ActivityCompat.checkSelfPermission(getActivity(), permission) == PackageManager.PERMISSION_GRANTED) {
//allowed
Log.e("allowed", permission);
} else {
//set to never ask again
Log.e("set to never ask again", permission);
//do something here.
}
}
}
if (requestCode != MaterialBarcodeScanner.RC_HANDLE_CAMERA_PERM) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
return;
}
if (grantResults.length != 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
mScannerView.setResultHandler(this);
mScannerView.startCamera(mCameraId);
mScannerView.setFlash(mFlash);
mScannerView.setAutoFocus(mAutoFocus);
return;
} else {
//set to never ask again
Log.e("set to never ask again", permissions[0]);
}
DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
};
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle("Error")
.setMessage(R.string.no_camera_permission)
.setPositiveButton(Android.R.string.ok, listener)
.show();
}
private void insertDummyContactWrapper() {
int hasWriteContactsPermission = checkSelfPermission(Manifest.permission.CAMERA);
if (hasWriteContactsPermission != PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{Manifest.permission.CAMERA},
REQUEST_CODE_ASK_PERMISSIONS);
return;
}
mScannerView.setResultHandler(this);
mScannerView.startCamera(mCameraId);
mScannerView.setFlash(mFlash);
mScannerView.setAutoFocus(mAutoFocus);
}
private int checkSelfPermission(String camera) {
if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
return REQUEST_CODE_ASK_PERMISSIONS;
} else {
return REQUEST_NOT_CODE_ASK_PERMISSIONS;
}
}
Vous pouvez utiliser la méthode if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)
pour déterminer si jamais demander est cochée ou non.
Pour plus de référence: Cochez cette case
Pour vérifier plusieurs autorisations, utilisez:
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)
|| ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
|| ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_FINE_LOCATION)
|| ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.RECORD_AUDIO)) {
showDialogOK("Service Permissions are required for this app",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case DialogInterface.BUTTON_POSITIVE:
checkAndRequestPermissions();
break;
case DialogInterface.BUTTON_NEGATIVE:
// proceed with logic by disabling the related features or quit the app.
finish();
break;
}
}
});
}
//permission is denied (and never ask again is checked)
//shouldShowRequestPermissionRationale will return false
else {
explain("You need to give some mandatory permissions to continue. Do you want to go to app settings?");
// //proceed with logic by disabling the related features or quit the app.
}
méthode explicit ()
private void explain(String msg){
final Android.support.v7.app.AlertDialog.Builder dialog = new Android.support.v7.app.AlertDialog.Builder(this);
dialog.setMessage(msg)
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface paramDialogInterface, int paramInt) {
// permissionsclass.requestPermission(type,code);
startActivity(new Intent(Android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.parse("package:com.exampledemo.parsaniahardik.marshmallowpermission")));
}
})
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface paramDialogInterface, int paramInt) {
finish();
}
});
dialog.show();
}
Le code ci-dessus affichera également une boîte de dialogue, qui redirigera l'utilisateur vers l'écran des paramètres de l'application à partir duquel il pourra donner sa permission s'il avait coché ne plus jamais demander le bouton.