Android M: verifique el permiso de tiempo de ejecución: ¿cómo determinar si el usuario marcó "Nunca preguntar de nuevo"?

307

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

ingrese la descripción de la imagen aquí

En caso de que el usuario rechace un permiso importante, una aplicación debería 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 nuevamente (se solicita permiso nuevamente)
  2. negar (la aplicación funcionará sin ese permiso).

Never ask againSin embargo, si el usuario verifica , el segundo diálogo con la explicación no debería mostrarse, especialmente si el usuario ya lo rechazó una vez. Ahora la pregunta es: ¿cómo sabe mi aplicación si el usuario ha verificado el Never ask again? La OMI onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)no me da esa información.

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

Emanuel Moecklin
fuente
99
"¿Google tiene planes de incorporar un mensaje personalizado en el cuadro de diálogo de permisos que explique por qué la aplicación necesita el permiso?" - En la presentación de Google I | O sobre el sistema de permisos M, me parece recordar que alguien preguntó en las preguntas y respuestas, y la respuesta fue que lo están pensando.
CommonsWare
1
No lo probé yo mismo, pero la documentación dice sobre Activity.shouldShowRequestPermissionRationale (String): este método devuelve verdadero si la aplicación ha solicitado este permiso anteriormente y el usuario rechazó la solicitud. Eso indica que probablemente debería explicarle al usuario por qué necesita el permiso. Si el usuario rechazó la solicitud de permiso en el pasado y eligió la opción No volver a preguntar en el cuadro de diálogo del sistema de solicitud de permiso, este método devuelve falso. El método también devuelve falso si la política del dispositivo prohíbe que la aplicación tenga ese permiso.
Fraid
1
@Miedo: parece que agregaron esto con la vista previa # 2 de Android M: developer.android.com/preview/support.html#preview2-notes y probablemente sea lo que estaba buscando. No puedo probarlo ahora, pero lo haré la próxima semana. Si hace lo que espero, puede publicarlo como respuesta y obtener algo de reputación. Mientras tanto, esto podría ayudar a otros: youtube.com/watch?v=f17qe9vZ8RM
Emanuel Moecklin
ejemplo de permisos peligrosos y permisos especiales: github.com/henrychuangtw/AndroidRuntimePermission
HenryChuang
1
@Alex es más difícil para los desarrolladores, eso es seguro, pero desde la perspectiva del usuario, es posible otorgar o denegar permisos específicos. El principal problema que veo es que la granularidad de los permisos es muy inconsistente y terminas pidiendo un permiso que podría no tener casi nada que ver con lo que intentas hacer en tu aplicación (por ejemplo, permiso de contacto cuando quiero conectarme a Google Drive porque necesita una lista de las cuentas del dispositivo para fines de autenticación y el permiso de la cuenta es parte del grupo de permisos de contacto).
Emanuel Moecklin

Respuestas:

341

Developer Preview 2 trae algunos cambios en la forma en que la aplicación solicita los permisos (consulte también http://developer.android.com/preview/support.html#preview2-notes ).

El primer diálogo ahora se ve así:

ingrese la descripción de la imagen aquí

No hay una casilla de verificación "Nunca volver a mostrar" (a diferencia de la vista previa del desarrollador 1). Si el usuario niega 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 cual la aplicación solicita ese permiso, por ejemplo:

ingrese la descripción de la imagen aquí

Si el usuario rechaza nuevamente, la aplicación debería cerrarse si necesita absolutamente ese permiso o seguir ejecutándose con una funcionalidad limitada. Si el usuario reconsidera (y selecciona volver a intentarlo), se solicita nuevamente el permiso. Esta vez el aviso se ve así:

ingrese la descripción de la imagen aquí

La segunda vez se muestra la casilla de verificación "Nunca preguntar de nuevo". Si el usuario niega nuevamente y la casilla de verificación está marcada, no debería suceder nada más. Se puede determinar si la casilla de verificación está marcada o no mediante Activity.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 una explicación adicional, el sistema proporciona el método Activity.shouldShowRequestPermissionRationale (String). Este método devuelve verdadero si la aplicación ha solicitado este permiso anteriormente y el usuario rechazó la solicitud. Eso indica que probablemente debería explicarle al usuario por qué necesita el permiso.

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

Para saber si el usuario negó con "nunca preguntar de nuevo", puede verificar nuevamente el método shouldShowRequestPermissionRationale en su onRequestPermissionsResult cuando el usuario no otorgó 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.

Emanuel Moecklin
fuente
30
Verifiqué el valor de retorno del método shouldShowRequestPermissionRationale () como falso para verificar si el usuario seleccionó "Nunca preguntar de nuevo". Pero también obtengo su valor como falso por primera vez cuando solicito permiso. Por lo tanto, no puedo diferenciar si el usuario seleccionó o no la casilla de verificación "Nunca preguntar de nuevo". ¿¿Por favor recomiende??
Sagar Trehan
32
Según tengo entendido, el método shouldShowRationalePermissionRationale () devuelve falso en tres casos: 1. Si llamamos a este método por primera vez antes de pedir permiso. 2. Si el usuario selecciona "No volver a preguntar" y niega el permiso. 3. Si la política del dispositivo prohíbe que la aplicación tenga ese permiso
Sagar Trehan
24
Todo bien ... pero nosotros, los desarrolladores, realmente necesitamos saber si el usuario dijo "nunca preguntar de nuevo" o no. Tengo un buen botón para acceder a una función. La primera vez que el usuario hace clic: ¿debe preguntar la razón? no, pide permiso. El usuario lo niega. El usuario vuelve a hacer clic en el botón: ¿justificación? ¡Sí! Mostrar justificación, el usuario dice Ok, luego negar y nunca volver a preguntar (ok, él es un idiota, pero los usuarios a menudo lo son). Más tarde, el usuario presiona nuevamente el botón, ¿justificación? no, pide permiso, no pasa nada para el usuario. Realmente necesito una manera, allí, para decirle al usuario: hola hombre, si quieres esta función ahora ve a la configuración de la aplicación y da el permiso.
Daniele Segato
44
Genial @EmanuelMoecklin, esto es mejor que la documentación de Google ahora: D
Daniele Segato
44
onRequestPermissionsResult no se llamará a menos que solicite el permiso. Como no hay una casilla de verificación "Nunca volver a preguntar" la primera vez que se solicita el permiso, shouldShowRequestPermissionRationale devolverá True (permiso solicitado pero sin volver a preguntar). En consecuencia, la justificación siempre se muestra la primera vez que el usuario rechaza el permiso, pero después de eso solo si la casilla de verificación no está marcada.
Emanuel Moecklin
95

Puedes revisar shouldShowRequestPermissionRationale()tu onRequestPermissionsResult().

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

Compruebe si se otorgó el permiso o no onRequestPermissionsResult(). Si no, entonces verifique shouldShowRequestPermissionRationale().

  1. Si este método regresa true, muestre una explicación de por qué se necesita este permiso en particular. Luego, dependiendo de la elección del usuario nuevamente requestPermissions().
  2. Si regresa false, muestre un mensaje de error que indique que no se otorgó el permiso y que la aplicación no puede continuar o que una función en particular está deshabilitada.

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

@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 obtener permiso de ubicación.

Abhinav Chauhan
fuente
Gracias por la foto y el enlace de Youtube. Coincide más o menos con mi propia respuesta. Cabe señalar que la pregunta se hizo cuando solo estaba disponible la vista previa del desarrollador 1 que no tenía el método shouldShowRequestPermissionRationale.
Emanuel Moecklin
Soy nuevo en Android y quiero anular este método onRequestPermissionsResult (). pero recibo el error de que debe implementar un método de supertipo. ¿Puedes decir cómo usar esto?
Andrain
39

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 haya aceptado / denegado el permiso a través de la solicitud del usuario (en dispositivos SDK 23+)

Actualizar:

La biblioteca de soporte de Android ahora también parece tener una clase muy similar android.support.v4.content.PermissionCheckerque contiene una 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;
Patrick Favre
fuente
1
Para el primer lanzamiento, estoy almacenando un booleano en preferencias compartidas.
Saeid Farivar
55
Esto siempre se devuelve BLOCKED_OR_NEVER_ASKEDsi el permiso aún no se ha solicitado.
Saket
66
sí, esa es la razón por la que se llama "BLOCKED_OR_NEVER_ASKED", también vea la última oración
Patrick Favre
3
android.content.pmya define PERMISSION_GRANTED = 0y PERMISSION_DENIED = -1. Tal vez conjunto BLOCKED_OR_NEVER_ASKED = PERMISSION_DENIED - 1o algo?
samis
Vea la respuesta de mVck a continuación para manejar la advertencia.
samis
28

Una vez que el usuario ha marcado "No volver a preguntar", la pregunta no se puede volver a mostrar. Pero puede explicarse al usuario que previamente ha denegado el permiso y debe otorgarlo en la configuración. Y refiéralo a la configuración, 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;
}
רותם ריכטר
fuente
en la migración a androidX puede reemplazar android.support.design.R con com.google.android.material.R
Ridha Rezzag
26

Puede ser útil para alguien: -

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

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

Estado 2: - Devuelve falso: - si el usuario selecciona "nunca pregunta de nuevo".

Enlace de ejemplo de trabajo detallado

Mellas
fuente
2
Esta es la forma correcta de detectar si el usuario ha seleccionado la opción no volver a preguntar nunca más.
Muhammad Babar
Ah, la clave aquí es que manejas esto en el onRequestPermissionsResult, no cuando realmente solicitas el permiso.
Joshua Pinter
26

Puede determinarlo verificando si la justificación del permiso se mostrará dentro del onRequestPermissionsResult()método de devolución de llamada. Y si encuentra algún permiso configurado para no volver a preguntar nunca más , puede solicitar a los usuarios que otorguen permisos desde la configuración.

Mi implementación completa sería como a continuación. Funciona para solicitudes de permisos simples o múltiples . Use lo siguiente o use 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).
        }
    }
}
Nabin Bhandari
fuente
hii @nabin mi requisito es cuando hago clic en el botón de descarga (que descarga el archivo pdf) que el tiempo tiene que verificar si el permiso de escritura está permitido o denegado, ¿cómo usar este código? ¿me puede guiar a PLZ
Rucha Bhatt Joshi
hola @RuchaBhatt Echa un vistazo a mi biblioteca. github.com/nabinbhandari/Android-Permissions
Nabin Bhandari
15

Si desea detectar todos los "estados" (primera vez denegado, solo denegado, solo denegado con "Nunca preguntar de nuevo" o denegado permanentemente), puede hacer lo siguiente:

Crea 2 booleanos

private boolean beforeClickPermissionRat;
private boolean afterClickPermissionRat;

Establezca 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 verificar que todavía no tiene el 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
mVck
fuente
No tiene sentido verificar el shouldShowRequestPermissionRationale antes de llamar a requestPermissions a menos que desee mostrar la justificación antes de solicitar el permiso. Sin embargo, mostrar la justificación solo después de que el usuario haya denegado el permiso parece ser la forma en que la mayoría de las aplicaciones lo manejan hoy en día.
Emanuel Moecklin
2
@EmanuelMoecklin, por lo que sé, es la única forma de verificar si ya ha sido denegado (verificándolo antes y después, como se explica en mi tabla de verdad) o si es la primera vez que lo niego (en mi caso, redirijo al usuario a los ajustes de la aplicación si es negado de forma permanente)
mVck
1
// TRUE FALSETambién ocurre cuando el usuario permite un permiso después de denegarlo previamente.
samis
11

Tuve el mismo problema y lo resolví. Para simplificar la vida, 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 preguntar de nuevo"?

Si el usuario marcó No preguntar nunca más , recibirá una devolución de llamada en onPermissionDisabled .

Feliz codificación :)

muthuraj
fuente
shouldShowRequestPermissionRationale Recibí un error aquí, ¿pueden ayudarme?
Rucha Bhatt Joshi
no puedo encontrar este método shouldShowRequestPermissionRationale puede ser que no logré obtener el contexto ... pero está bien, encontré otra solución alternativa ... Gracias por la ayuda :)
Rucha Bhatt Joshi
1
Culpa mía. shouldShowRequestPermissionRationale está disponible a través de Activity, no context. Actualicé mi respuesta enviando el contexto a Activity antes de llamar a ese método. Compruébalo :)
muthuraj
1
Esta es la única forma de evitar el primer valor falso devuelto shouldShowRequestPermissionRationale, guardando de preferencia la solicitud enviada al usuario. Tuve la misma idea y encontré tu respuesta. Buen trabajo hombre
MatPag
4

Explicación completa de 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.
        }

    }
}
saksham
fuente
4

Una función útil para determinar si se ha bloqueado la solicitud de un permiso arbitrario (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) truecuando solicite un permiso por primera vez.


Explicación:

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

ContextCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED para verificar que aún no tenemos el permiso.

!activity.shouldShowRequestPermissionRationale(permission)para verificar si el usuario ha denegado la aplicación preguntando nuevamente. Debido a las peculiaridades de esta función , también se requiere la siguiente línea.

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

JakeSteam
fuente
4

El método shouldShowRequestPermissionRationale () se puede usar para verificar si el usuario seleccionó la opción 'nunca más se preguntó' y denegó el permiso. Hay muchos ejemplos de código, por lo 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 Solicitud de permisos en tiempo de ejecución , ese método devuelve verdadero si la opción 'nunca preguntar de nuevo' es visible, de lo contrario, falso; Por lo tanto, devuelve falso la primera vez que se muestra un cuadro de diálogo, luego, a partir de la segunda vez, devuelve verdadero, y solo si el usuario niega el permiso para seleccionar la opción, en ese punto vuelve a ser falso nuevamente.

Para detectar este caso, puede detectar la secuencia falso-verdadero-falso, o (más simple) puede tener un indicador que haga 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 le permitirá detectar cuándo se selecciona la opción.

Alessio
fuente
3

Por favor no me arrojes piedras por esta solución.

Esto funciona pero es un poco "hacky".

Cuando llame requestPermissions, registre la hora actual.

        mAskedPermissionTime = System.currentTimeMillis();

Entonces en onRequestPermissionsResult

Si no se otorga el resultado, verifique la hora nuevamente.

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

Como 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.

Antzi
fuente
¿Qué pasa si vemos ese diálogo solicitado durante 5 minutos y luego lo negamos?
saksham
Entonces, ¿de qué sirve esto si no puede cumplir el requisito básico? Un código puede ser un truco si se acepta si cumple claramente todos los requisitos en todos los demás casos.
saksham
Si, esto es malo. Los probadores automáticos como este podrían hacer clic más rápido que eso: developer.android.com/training/testing/crawler
stackzebra
2

Escribí una taquigrafía para solicitar 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 adjunta y se separa a la Actividad que solicita los permisos. Puede usar PermissionRequestManagerlo siguiente:

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

crysxd
fuente
2
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
                    }
                }
            }
        }
    }
}
Vinod Ranga
fuente
2

Pruebe esta sencilla biblioteca de permisos. Manejará todas las operaciones relacionadas con el permiso en 3 sencillos pasos. Me salvó el tiempo. Puede finalizar 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 denegados permisos y etc.

https://github.com/ParkSangGwon/TedPermission

Paso 1: agrega tu dependencia

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

Paso 2: pide 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();
    }
};
Vignes
fuente
Excelente. Me ahorró tiempo
Vigneswaran A
Agradable, fácil de usar
Uray Febri
2

Puedes escuchar bonita.

Oyente

interface PermissionListener {
    fun onNeedPermission()
    fun onPermissionPreviouslyDenied(numberDenyPermission: Int)
    fun onPermissionDisabledPermanently(numberDenyPermission: Int)
    fun onPermissionGranted()
}

MainClass para permiso

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

    }
}

Usado de esta manera

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

                })

anular onRequestPermissionsResult en actividad o 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()
        }
        }
    }
Rasoul Miri
fuente
1

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

Desde el documento de Android:

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

Farhan
fuente
1

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

Para más referencia: marque esto

Para verificar los permisos múltiples, 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 de explicar ()

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á un cuadro de diálogo, que redirigirá al usuario a la pantalla de configuración de la aplicación desde donde puede otorgar permiso si ha marcado el botón Nunca preguntar de nuevo.

usuario6435056
fuente
1

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

}
Pronto Santos
fuente
0

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

Me gustaría ofrecer la funcionalidad de referencia de permisos inicialmente. Si el usuario lo usa y no tiene derechos, obtiene el primer cuadro de diálogo de arriba o el segundo y el tercero. Cuando el usuario ha elegido 'Nunca preguntar de nuevo', me gustaría deshabilitar la funcionalidad y mostrarla de manera diferente. - Mi acción se desencadena por una entrada de texto giratoria, también me gustaría agregar '(Permiso revocado)' al texto de la etiqueta que se muestra. Esto le muestra al usuario: 'Hay funcionalidad pero no puedo usarla, debido a mi configuración de permisos'. Sin embargo, esto no parece posible, ya que no puedo comprobar si se ha elegido o no "Nunca preguntar de nuevo".

Llegué a una solución con la que puedo vivir al tener mi funcionalidad siempre habilitada con una verificación de permiso activa. 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. Entonces, si el usuario ha elegido 'Nunca preguntar de nuevo', 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 por parte del sistema operativo, pero no brinda, ya que tres notificaciones seguidas serían demasiado dolorosas.

ChristianKoelle
fuente
0

Tengo que implementar un permiso dinámico para la cámara. Donde ocurren 3 casos posibles: 1. Permitir, 2. Denegado, 3. No volver 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;
    }
}
hitesh141
fuente
0

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 ver el ejemplo completo, vea 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);
}
Sam es
fuente
0

Para responder la pregunta con precisión, ¿qué sucede cuando el usuario presiona "Nunca preguntar de nuevo"?

El método / función anulada

onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray)

La matriz grantResult resulta estar vacía, ¿entonces puede hacer algo allí? Pero no es la mejor práctica.

¿Cómo manejar "Nunca preguntar de nuevo"?

Estoy trabajando con Fragment, que requería el permiso 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()

            }
        }
    }

Las otras funciones son 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)
}
devDeejay
fuente