Android M-check runtime permission - ¿cómo determinar si el usuario marcó "Nunca preguntar de nuevo"?


De acuerdo con esto: http://developer.android.com/preview/features/runtime-permissions.html#coding una aplicación puede comprobar los permisos de tiempo de ejecución y solicitar permisos si aún no se ha concedido. El siguiente diálogo se mostrará entonces:

introduzca la descripción de la imagen aquí

En caso de que el usuario rechace un permiso importante, imo una aplicación debe mostrar una explicación de por qué se necesita el permiso y qué impacto tiene la disminución. Ese diálogo tiene dos opciones:

  1. vuelva a intentarlo de nuevo (se solicita permiso de nuevo)
  2. deny (app funcionará sin ese permiso).

Sin embargo, si el usuario marca Never ask again, el segundo diálogo con la explicación no debe mostrarse, especialmente si el usuario ya lo rechazó una vez antes. Ahora la pregunta es: ¿cómo sabe mi aplicación si el usuario ha comprobado el Never ask again? IMO el onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) no me da esa información.

Una segunda pregunta sería: ¿tiene Google planes para incorporar un mensaje personalizado en el diálogo de permiso que explicaría por qué la aplicación necesita el permiso? De esa manera nunca habría un segundo diálogo que sin duda haría para una mejor ux.

Author: JJD, 2015-06-09

21 answers

Developer Preview 2 trae algunos cambios en cómo se solicitan los permisos por la aplicación (ver también http://developer.android.com/preview/support.html#preview2-notes).

El primer diálogo ahora se ve así:

introduzca la descripción de la imagen aquí

No hay una casilla de verificación "Nunca mostrar de nuevo" (a diferencia de developer preview 1). Si el usuario deniega el permiso y si el permiso es esencial para la aplicación, podría presentar otro cuadro de diálogo para explicar la razón por la que la aplicación lo solicita permiso, por ejemplo, así:

introduzca la descripción de la imagen aquí

Si el usuario rechaza de nuevo la aplicación debe cerrarse si necesita absolutamente ese permiso o seguir funcionando con funcionalidad limitada. Si el usuario reconsidera (y selecciona re-try), el permiso se solicita de nuevo. Esta vez el indicador se ve así:

introduzca la descripción de la imagen aquí

La segunda vez se muestra la casilla "Nunca preguntar de nuevo". Si el usuario deniega de nuevo y la casilla de verificación está marcada nada más debería suceder. Si la casilla de verificación está marcada o no se puede determinar mediante Actividad.shouldShowRequestPermissionRationale (String), por ejemplo así:

if (shouldShowRequestPermissionRationale(Manifest.permission.WRITE_CONTACTS)) {...

Eso es lo que dice la documentación de Android ( https://developer.android.com/training/permissions/requesting.html):

Para ayudar a encontrar las situaciones en las que necesita proporcionar extra explicación, el sistema proporciona la Actividad.Método shouldShowRequestPermissionRationale (String). Este devuelve el método true si la aplicación ha solicitado este permiso previamente y el usuario denegó la solicitud. Eso indica que usted probablemente debería explicar al usuario por qué necesita el permiso.

Si el usuario rechazó la solicitud de permiso en el pasado y eligió la opción No preguntar de nuevo en el cuadro de diálogo del sistema de solicitud de permisos, este método devuelve false. El método también devuelve false si el dispositivo la política prohíbe que la aplicación tenga ese permiso.

Para saber si el usuario denegado con "nunca preguntar de nuevo" puede comprobar de nuevo el método shouldShowRequestPermissionRationale en su onRequestPermissionsResult cuando el usuario no concedió el permiso.

@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...*/ ) {
                }
            }
        }
    }
}

Puede abrir la configuración de su aplicación con este código:

Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", getPackageName(), null);
intent.setData(uri);
startActivityForResult(intent, REQUEST_PERMISSION_SETTING);

No hay forma de enviar al usuario directamente a la página de Autorización.

 287
Author: Emanuel Moecklin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-23 21:48:47

Puede marcar shouldShowRequestPermissionRationale() en su onRequestPermissionsResult().

shouldShowRequestPermissionRationale https://youtu.be/C8lUdPVSzDk?t=2m23s

Compruebe si el permiso fue concedido o no en onRequestPermissionsResult(). Si no entonces marque shouldShowRequestPermissionRationale().

  1. Si este método devuelve true entonces muestra una explicación de por qué se necesita este permiso en particular. Luego, dependiendo de la elección del usuario nuevamente requestPermissions().
  2. Si devuelve false, muestra un mensaje de error que indica que no se concedió el permiso y que la aplicación no puede siga adelante o se deshabilitará una función en particular.

A continuación se muestra el código de ejemplo.

@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;
    }
}

Aparentemente, Google Maps hace exactamente esto para el permiso de ubicación.

 79
Author: Abhinav Chauhan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-11-04 05:41:05

Aquí hay un método agradable y fácil para verificar el estado actual de los permisos:

    @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;
    }

Advertencia: devuelve BLOCKED_OR_NEVER_ASKED el primer inicio de la aplicación, antes de que el usuario aceptara / denegara el permiso a través del mensaje de usuario (en sdk 23+ dispositivos)

Actualización:

La biblioteca de soporte de Android ahora también parece tener una clase muy similar android.support.v4.content.PermissionChecker que contiene un checkSelfPermission() que devuelve:

public static final int PERMISSION_GRANTED = 0;
public static final int PERMISSION_DENIED = -1;
public static final int PERMISSION_DENIED_APP_OP = -2;
 33
Author: for3st,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-02 15:07:36

Puede ser útil para alguien:--

Lo que he notado es que si comprobamos el indicador shouldShowRequestPermissionRationale() en el método de devolución de llamada onRequestPermissionsResult (), muestra solo dos estados.

Estado 1:-Devuelve verdadero: Any Cada vez que el usuario hace clic en Denegar permisos (incluida la primera vez).

Estado 2:-Devuelve false :- si el usuario selecciona "nunca vuelve a preguntar".

Enlace del ejemplo de trabajo detallado

 19
Author: Nicks,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:18:30

Usted puede determinar comprobando si permission rationale debe mostrarse dentro del método de devolución de llamada onRequestPermissionsResult(). Y si encuentra algún permiso establecido en nunca vuelva a preguntar, puede solicitar a los usuarios que otorguen permisos desde la configuración.

Mi implementación completa sería como a continuación. Funciona tanto para soloo múltiples solicitudes de permisos. Utilice lo siguiente o utilice directamente mi biblioteca.

@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).
        }
    }
}
 15
Author: Nabin Bhandari,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-12 12:22:01

Si desea detectar todos los "estados" (denegado por primera vez, acaba de ser denegado, acaba de ser denegado con "Nunca vuelva a preguntar" o permanentemente denegado) puede hacer lo siguiente:

Crear 2 booleanos

private boolean beforeClickPermissionRat;
private boolean afterClickPermissionRat;

Establece el primero antes de pedir permiso:

beforeClickPermissionRat = shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE);

Establezca el segundo dentro de su método onRequestPermissionsResult:

afterClickPermissionRat = shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE);

Use la siguiente "tabla" para hacer lo que necesite en onRequestPermissionsResult () (después de comprobar que todavía no tiene permiso):

// 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
 9
Author: mVck,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-23 16:34:36

Tuve el mismo problema y lo descubrí. Para hacer la vida mucho más simple, escribí una clase util para manejar los permisos de tiempo de ejecución.

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();
    }
}

Y los métodos PreferenceUtil son los siguientes.

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);
}

Ahora, todo lo que necesita es usar el método * checkPermission* con los argumentos adecuados.

Aquí hay un ejemplo,

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();
                        }
                    });

¿Cómo sabe mi aplicación si el usuario ha marcado "Nunca volver a preguntar"?

Si el usuario marca Nunca pregunte de nuevo , obtendrá una devolución de llamada en onPermissionDisabled.

Feliz codificación:)

 7
Author: muthuraj,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-16 13:51:45

Una vez que el usuario ha marcado "No volver a preguntar", la pregunta no se puede mostrar de nuevo. Pero se puede explicar al usuario que previamente ha negado el permiso y debe otorgar permiso en la configuración. Y remítelo a los ajustes, con el siguiente código:

@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;
}
 5
Author: רותם ריכטר,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-25 09:54:11

Explicación completa para cada caso de permiso

/**
 *    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.
        }

    }
}
 3
Author: saksham,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-27 08:17:05

Escribí una taquigrafía para la solicitud de permiso en Android M. Este código también maneja la compatibilidad con versiones anteriores de Android.

Todo el código feo se extrae en un Fragmento que se une y se separa a la Actividad que solicita los permisos.Puedes usar PermissionRequestManager de la siguiente manera:

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();

Echa un vistazo: https://gist.github.com/crysxd/385b57d74045a8bd67c4110c34ab74aa

 2
Author: crysxd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-24 16:47:12

El método shouldShowRequestPermissionRationale() puede ser user para comprobar si el usuario seleccionó la opción 'nunca preguntó de nuevo' y denegó el permiso. Hay muchos ejemplos de código, así que preferiría explicar cómo usarlo para tal propósito, porque creo que su nombre y su implementación hacen que esto sea más complicado de lo que realmente es.

Como se explica en Requesting Permissions at Run Time , ese método devuelve true si la opción 'never ask again' es visible, false de lo contrario; por lo tanto, devuelve false la primera vez que se muestra un diálogo, luego, a partir de la segunda vez, devuelve true, y solo si el usuario niega el permiso al seleccionar la opción, en ese momento devuelve false nuevamente.

Para detectar tal caso, puede detectar la secuencia false-true-false, o (más simple) puede tener una bandera que realiza un seguimiento de la hora inicial en que se muestra el diálogo. Después de eso, ese método devuelve verdadero o falso, donde el falso permitirá detectar cuando la opción está seleccionada.

 2
Author: Alessio,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-07 07:16:01

Pruebe esta sencilla biblioteca de permisos. Se encargará de todas las operaciones relacionadas con el permiso en 3 sencillos pasos. Me ahorró tiempo. Puede terminar todo el trabajo relacionado con los permisos en 15 minutos.

Puede manejar Denegar, Puede manejar Nunca preguntar de nuevo, Puede llamar a la configuración de la aplicación para obtener permiso, Puede dar un mensaje Racional, Puede dar un mensaje de Denegación, Puede dar una lista de permisos aceptados, Puede dar una lista de permisos denegados y sucesivamente.

Https://github.com/ParkSangGwon/TedPermission

Paso 1: añade tu dependencia

dependencies {
     compile 'gun0912.ted:tedpermission:2.1.1'
     //check the above link for latest libraries
}

Paso 2: Pedir permisos

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();

Paso 3: Manejar la respuesta de permiso

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();
    }
};
 2
Author: Vignes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-23 07:41:28

Por favor, no me tires piedras por esta solución.

Esto funciona, pero es un poco "hacky".

Cuando llame a requestPermissions, registre la hora actual.

        mAskedPermissionTime = System.currentTimeMillis();

Luego en onRequestPermissionsResult

Si el resultado no se concede, compruebe la hora de nuevo.

 if (System.currentTimeMillis() - mAskedPermissionTime < 100)

Dado que el usuario no puede hacer clic tan rápido en el botón denegar, sabemos que seleccionó "nunca preguntar de nuevo" porque la devolución de llamada es instantánea.

Úselo bajo su propio riesgo.

 1
Author: Antzi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-14 04:46:38

Puede usar

shouldShowRequestPermissionRationale()

Dentro

onRequestPermissionsResult()

Vea el siguiente ejemplo:

Compruebe si tiene permiso cuando el usuario hace clic en el botón:

@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);
            }
        } 
    }
}

Cuando el usuario responda al cuadro de diálogo de permisos, iremos a 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();
            }
        }
    }

}
 1
Author: Soon Santos,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-28 12:44:11

Una función útil para determinar si un permiso arbitrario ha sido bloqueado para solicitar (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
}

El uso de esto requiere establecer un booleano de preferencia compartida con el nombre de su permiso deseado (por ejemplo, android.Manifest.permission.READ_PHONE_STATE) a true cuando solicite un permiso por primera vez.


Explicación:

Build.VERSION.SDK_INT >= Build.VERSION_CODES.M como parte del código solo se puede ejecutar en el nivel de API 23+.

ContextCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED para comprobar que no tenemos ya el permiso.

!activity.shouldShowRequestPermissionRationale(permission) para comprobar si el usuario ha negado la aplicación pidiendo de nuevo. Debido a peculiaridades de esta función, la siguiente línea también es necesaria.

PreferenceManager.getDefaultSharedPreferences(activity).getBoolean(permission, false) esto se usa (junto con establecer el valor a true en la primera solicitud de permiso) para distinguir entre los estados "Nunca preguntó" y "Nunca preguntar de nuevo", ya que la línea anterior no devuelve esta información.

 1
Author: JakeSteam,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-05 11:20:11

También me gustaría obtener la información si el usuario ha seleccionado o no "nunca preguntar de nuevo". He logrado una 'casi solución' con una bandera de aspecto feo, pero antes de decirles cómo, les contaré acerca de mi motivación:

Me gustaría ofrecer la funcionalidad de referencia de permisos inicialmente. Si el usuario lo usa y no tiene derechos, él / ella obtiene el 1to diálogo de arriba o ambos el 2do y 3ro. Cuando el usuario ha elegido 'Nunca preguntar de nuevo' Me gustaría desactivar la funcionalidad y para mostrar de manera diferente. - Mi acción se desencadena por una entrada de texto spinner, también me gustaría agregar '(Permiso revocado) ' al texto de la etiqueta que se muestra. Esto muestra al usuario: 'Hay funcionalidad pero no puedo usarla, debido a mi configuración de permisos."Sin embargo, esto no parece ser posible, ya que no puedo comprobar si se ha elegido o no "Nunca volver a preguntar".

Llegué a una solución con la que puedo vivir al tener mi funcionalidad siempre habilitada con un activo verificación de permisos. Estoy mostrando un mensaje Toast en onRequestPermissionsResult () en caso de una respuesta negativa, pero solo si no he mostrado mi ventana emergente de justificación personalizada. Por lo tanto, si el usuario ha elegido 'Nunca volver a preguntar', solo recibe un mensaje de brindis. Si el usuario es reacio a elegir 'nunca preguntar de nuevo', solo obtiene la justificación personalizada y la ventana emergente de solicitud de permiso del sistema operativo, pero no tostadas, ya que tres notificaciones seguidas serían demasiado dolor.

 0
Author: ChristianKoelle,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-06 23:00:18

En su lugar, recibirá una devolución de llamada en onRequestPermissionsResult() como PERMISSION_DENIED cuando solicite permiso nuevamente mientras cae en condición falsa de shouldShowRequestPermissionRationale()

Desde Android doc:

Cuando el sistema le pide al usuario que conceda un permiso, el usuario tiene la opción de decirle al sistema que no solicite ese permiso de nuevo. En ese caso, cada vez que una aplicación utiliza requestPermissions() para solicitar ese permiso de nuevo, el sistema deniega inmediatamente la solicitud. El sistema llama a su método de devolución de llamada onRequestPermissionsResult() y pasa PERMISSION_DENIED, de la misma manera que lo haría si el usuario hubiera rechazado explícitamente su solicitud de nuevo. Esto significa que cuando llama a requestPermissions(), no puede asumir que se ha producido ninguna interacción directa con el usuario.

 0
Author: Farhan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-26 10:54:18

Tengo que implementar el permiso dinámico para la cámara. Donde ocurren 3 casos posibles: 1. Permitir, 2. Denegado, 3. No vuelvas a preguntar.

 @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;
    }
}
 0
Author: hitesh141,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-04 12:25:09

Puede usar el método if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA) para detectar si nunca preguntar está marcado o no.

Para más referencia : Marque esto

Para comprobar si hay varios permisos use:

  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étodo Explain ()

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();
    }

El código anterior también mostrará el diálogo, que redirigirá al usuario a la pantalla de configuración de la aplicación desde donde puede dar permiso si ha marcado el botón nunca preguntar de nuevo.

 0
Author: user6435056,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-15 06:38:02

Ampliando la respuesta anterior de mVck, la siguiente lógica determina si "Nunca preguntar de nuevo" se ha verificado para una solicitud de permiso dada:

bool bStorage = grantResults[0] == Permission.Granted;
bool bNeverAskForStorage =
    !bStorage && (
        _bStorageRationaleBefore == true  && _bStorageRationaleAfter == false ||
        _bStorageRationaleBefore == false && _bStorageRationaleAfter == false
    );

Que se extrae de abajo (para el ejemplo completo ver esta respuesta)

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);
}
 0
Author: samis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-26 12:50:44
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
                    }
                }
            }
        }
    }
}
 0
Author: Vinod Ranga,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-15 21:05:33