Android M - verifique a permissão de tempo de execução - como determinar se o usuário marcou "Nunca perguntar novamente"?

307

De acordo com isso: http://developer.android.com/preview/features/runtime-permissions.html#coding um aplicativo pode verificar permissões de tempo de execução e solicitar permissões, se ainda não tiver sido concedido. A seguinte caixa de diálogo será exibida:

insira a descrição da imagem aqui

Caso o usuário recuse uma permissão importante, o aplicativo deve exibir uma explicação de por que a permissão é necessária e qual o impacto da recusa. Essa caixa de diálogo possui duas opções:

  1. tente novamente (a permissão é solicitada novamente)
  2. negar (o aplicativo funcionará sem essa permissão).

Se o usuário verificar Never ask again, no entanto, a segunda caixa de diálogo com a explicação não deverá ser mostrada, especialmente se o usuário já recusou uma vez. Agora, a pergunta é: como meu aplicativo sabe se o usuário verificou o Never ask again? OMI onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)não me dá essa informação.

Uma segunda pergunta seria: o Google planeja incorporar uma mensagem personalizada na caixa de diálogo de permissão que explicaria por que o aplicativo precisa da permissão? Dessa forma, nunca haveria um segundo diálogo que certamente contribuísse para uma melhor experiência de usuário.

Emanuel Moecklin
fonte
9
"o Google tem planos para incorporar uma mensagem personalizada na caixa de diálogo de permissão que explicaria por que o aplicativo precisa da permissão?" - na apresentação do Google I | O sobre o sistema de permissão M, lembro-me de alguém perguntado nas perguntas e respostas, e a resposta foi que eles estão pensando nisso.
CommonsWare
1
Não testei sozinho, mas a documentação diz sobre Activity.shouldShowRequestPermissionRationale (String): esse método retornará true se o aplicativo solicitou essa permissão anteriormente e o usuário negou a solicitação. Isso indica que você provavelmente deve explicar ao usuário por que precisa da permissão. Se o usuário recusou a solicitação de permissão no passado e escolheu a opção Não perguntar novamente na caixa de diálogo do sistema de solicitação de permissão, esse método retornará false. O método também retornará false se a política do dispositivo proibir que o aplicativo tenha essa permissão.
Fraid 17/07/2015
1
@Fraid: parece que eles adicionaram isso com a visualização nº 2 do Android M: developer.android.com/preview/support.html#preview2-notes e provavelmente é o que eu estava procurando. Não posso testá-lo agora, mas o farei na próxima semana. Se ele faz o que eu espero que você faça, você pode publicá-lo como resposta e obter alguma reputação. Enquanto isso, isso pode ajudar outras pessoas: youtube.com/watch?v=f17qe9vZ8RM
Emanuel Moecklin -
exemplo de Permissão perigosas e permissões especiais: github.com/henrychuangtw/AndroidRuntimePermission
HenryChuang
1
O @Alex é mais difícil para os desenvolvedores, com certeza, mas do ponto de vista do usuário, é possível conceder ou negar permissões específicas. O principal problema que vejo é que a granularidade de permissões é muito inconsistente e você acaba solicitando uma permissão que pode ter quase nada a ver com o que você está tentando fazer no seu aplicativo (por exemplo, permissão de contatos quando eu quero me conectar a Google Drive porque precisa de uma lista das contas de dispositivo para fins de autenticação e a permissão da conta faz parte do grupo de permissões de contato).
Emanuel Moecklin

Respostas:

341

A prévia do desenvolvedor 2 traz algumas alterações na forma como as permissões são solicitadas pelo aplicativo (consulte também http://developer.android.com/preview/support.html#preview2-notes ).

A primeira caixa de diálogo agora fica assim:

insira a descrição da imagem aqui

Não existe a caixa de seleção "Nunca mostrar novamente" (diferente da visualização 1 do desenvolvedor). Se o usuário negar a permissão e se a permissão for essencial para o aplicativo, ele poderá apresentar outra caixa de diálogo para explicar o motivo pelo qual o aplicativo solicita essa permissão, por exemplo:

insira a descrição da imagem aqui

Se o usuário recusar novamente, o aplicativo deverá desligar se precisar absolutamente dessa permissão ou continuar executando com funcionalidade limitada. Se o usuário reconsiderar (e selecionar tentar novamente), a permissão será solicitada novamente. Desta vez, o prompt é assim:

insira a descrição da imagem aqui

Na segunda vez, a caixa de seleção "Nunca perguntar novamente" é exibida. Se o usuário negar novamente e a caixa de seleção estiver marcada, nada mais deve acontecer. Se a caixa de seleção está marcada ou não, pode ser determinada usando Activity.shouldShowRequestPermissionRationale (String), por exemplo:

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

É o que diz a documentação do Android ( https://developer.android.com/training/permissions/requesting.html ):

Para ajudar a encontrar as situações em que você precisa fornecer explicações adicionais, o sistema fornece o método Activity.shouldShowRequestPermissionRationale (String). Este método retorna true se o aplicativo solicitou essa permissão anteriormente e o usuário negou a solicitação. Isso indica que você provavelmente deve explicar ao usuário por que precisa da permissão.

Se o usuário recusou a solicitação de permissão no passado e escolheu a opção Não perguntar novamente na caixa de diálogo do sistema de solicitação de permissão, esse método retornará false. O método também retornará false se a política do dispositivo proibir que o aplicativo tenha essa permissão.

Para saber se o usuário negou "nunca perguntar novamente", verifique novamente o método shouldShowRequestPermissionRationale no seu onRequestPermissionsResult quando o usuário não concedeu a permissão.

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

Você pode abrir a configuração do seu aplicativo com 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);

Não há como enviar o usuário diretamente para a página Autorização.

Emanuel Moecklin
fonte
30
Eu verifiquei o valor de retorno do método shouldShowRequestPermissionRationale () como false para verificar se o usuário selecionou "Nunca perguntar novamente". Mas também estou obtendo seu valor falso pela primeira vez quando solicito permissão. Portanto, não sou capaz de diferenciar se o usuário selecionou a caixa de seleção "Nunca perguntar novamente" ou não. Por favor sugira??
Sagar Trehan
32
De acordo com meu entendimento, o método shouldShowRationalePermissionRationale () retorna false em três casos: 1. Se chamarmos esse método pela primeira vez antes de pedir permissão. 2. Se o usuário selecionar "Não perguntar novamente" e negar permissão. 3. Se a política de dispositivo proíbe a aplicação de ter a permissão
Sagar Trehan
24
Tudo bem ... mas nós, desenvolvedores, realmente precisamos saber se o usuário disse "nunca pergunte novamente" ou não. Eu tenho um bom botão para acessar um recurso. A primeira vez que o usuário clica: deve perguntar a justificativa? não, peça permissão. O usuário nega. Usuário clique novamente no botão: lógica? Sim! Mostre justificativa, o usuário diz Ok, depois negue e nunca pergunte novamente (ok, ele é um idiota, mas os usuários geralmente são). Mais tarde usuário pressionar novamente o botão, lógica? não, peça permissão, nada acontece para o usuário. Eu realmente preciso de uma maneira de dizer ao usuário: ei cara, se você quiser esse recurso, vá para a configuração do aplicativo e dê a permissão.
Daniele Segato
4
Ótimo @EmanuelMoecklin, isso é melhor que a documentação do Google agora: D
Daniele Segato
4
onRequestPermissionsResult não será chamado, a menos que você solicite a permissão. Como não existe a caixa de seleção "Nunca perguntar novamente" na primeira vez em que a permissão é solicitada, shouldShowRequestPermissionRationale retornará True (permissão solicitada, mas sem perguntar novamente). Conseqüentemente, a justificativa é sempre mostrada na primeira vez em que o usuário rejeita a permissão, mas depois disso somente se a caixa de seleção não estiver marcada.
Emanuel Moecklin
95

Você pode verificar o shouldShowRequestPermissionRationale()seu onRequestPermissionsResult().

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

Verifique se a permissão foi concedida ou não onRequestPermissionsResult(). Caso contrário , verifique shouldShowRequestPermissionRationale().

  1. Se esse método retornar true, mostre uma explicação de por que essa permissão específica é necessária. Então, dependendo da escolha do usuário novamente requestPermissions().
  2. Se retornar false, mostrará uma mensagem de erro de que a permissão não foi concedida e o aplicativo não pode prosseguir ou um recurso específico está desativado.

Abaixo está o código de exemplo.

@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, o Google Maps faz exatamente isso para obter permissão de localização.

Abhinav Chauhan
fonte
Obrigado pela foto e pelo link do Youtube. Corresponde mais ou menos à minha própria resposta. Deve-se observar que a pergunta foi feita quando apenas a visualização 1 do desenvolvedor estava disponível, que não possuía o método shouldShowRequestPermissionRationale.
Emanuel Moecklin
Eu sou novo no Android e quero mais sobre este onRequestPermissionsResult () método. mas estou recebendo o erro de que ele deve implementar um método supertipo. você pode dizer como usar isso
Andrain
39

Aqui está um método fácil e agradável para verificar o status atual da permissão:

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

Advertência: retorna BLOCKED_OR_NEVER_ASKED a primeira inicialização do aplicativo, antes que o usuário aceite / negue a permissão por meio do prompt do usuário (nos dispositivos sdk 23+)

Atualizar:

A biblioteca de suporte do Android agora também parece ter uma classe muito semelhante android.support.v4.content.PermissionCheckerque contém uma checkSelfPermission()que retorna:

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
fonte
1
Para o primeiro lançamento, estou armazenando um booleano em preferências compartilhadas.
Saeid Farivar
5
Isso sempre retorna BLOCKED_OR_NEVER_ASKEDse a permissão ainda não foi solicitada.
Saket
6
sim, essa é a razão pela qual se chama "BLOCKED_OR_NEVER_ASKED", também vê a última frase
Patrick Favre
3
android.content.pmjá define PERMISSION_GRANTED = 0e PERMISSION_DENIED = -1. Talvez conjunto BLOCKED_OR_NEVER_ASKED = PERMISSION_DENIED - 1ou algo assim?
samis 25/07
Veja a resposta do mVck abaixo para lidar com a ressalva.
28517 samis
28

Depois que o usuário marcar "Não perguntar novamente", a pergunta não poderá ser exibida novamente. Mas pode ser explicado ao usuário que ele negou a permissão anteriormente e deve conceder permissão nas configurações. E referencie-o às configurações, com o seguinte 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;
}
רותם ריכטר
fonte
na migração para androidX você pode substituir android.support.design.R com com.google.android.material.R
Ridha Rezzag
26

Pode ser útil para alguém: -

O que eu notei é que, se verificarmos o sinalizador shouldShowRequestPermissionRationale () no método de retorno de chamada onRequestPermissionsResult (), ele mostrará apenas dois estados .

Estado 1: -Retorno verdadeiro: - Sempre que o usuário clicar em Negar permissões (incluindo a primeira vez).

Estado 2: -Retorna false: - se o usuário selecionar "nunca pede novamente".

Link do exemplo de trabalho detalhado

Nicks
fonte
2
Esta é a maneira correta de detectar se o usuário selecionou a opção nunca perguntar novamente.
Muhammad Babar
Ah, a chave aqui é que você lida com isso no onRequestPermissionsResult, não quando realmente solicita a permissão.
Joshua Pinter
26

Você pode determiná- lo verificando se a lógica da permissão deve ser mostrada dentro do onRequestPermissionsResult()método de retorno de chamada. E se você encontrar alguma permissão definida para nunca perguntar novamente , poderá solicitar que os usuários concedam permissões a partir das configurações.

Minha implementação completa seria como abaixo. Ele funciona para solicitações de permissões únicas ou múltiplas . Use o seguinte ou use diretamente minha 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
fonte
hii @nabin Minha exigência é quando clico no botão de download (que faz o download do arquivo pdf) para que o tempo tenha que verificar se a permissão de gravação é permitida ou negada, como usar esse código! você pode me guiar por favor
Rucha Bhatt Joshi
olá @RuchaBhatt Dê uma olhada na minha biblioteca. github.com/nabinbhandari/Android-Permissions
Nabin Bhandari
15

Se você deseja detectar todos os "estados" (primeira vez negado, recém-negado, apenas negado com "Nunca perguntar novamente" ou negado permanentemente), faça o seguinte:

Crie 2 booleanos

private boolean beforeClickPermissionRat;
private boolean afterClickPermissionRat;

Defina o primeiro antes de pedir permissão:

beforeClickPermissionRat = shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE);

Defina o segundo dentro do seu método onRequestPermissionsResult:

afterClickPermissionRat = shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE);

Use a seguinte "tabela" para fazer o que você precisar em onRequestPermissionsResult () (depois de verificar se você ainda não tem permissão):

// 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
fonte
Não há sentido em verificar o shouldShowRequestPermissionRationale antes de chamar requestPermissions, a menos que você queira mostrar a justificativa antes de solicitar a permissão. Mostrar a justificativa somente depois que o usuário negou a permissão parece ser como a maioria dos aplicativos lida com isso atualmente.
Emanuel Moecklin
2
@EmanuelMoecklin, tanto quanto sei, é a única maneira de verificar se já foi negado (verificando-o antes e depois, conforme explicado na minha tabela de verdade) ou se é uma primeira vez negada (no meu caso, redireciono o usuário para as configurações do aplicativo, se ele for negado permanentemente)
mVck 30/01
1
// TRUE FALSEtambém ocorre quando o usuário permite uma permissão após negá-la anteriormente.
28517 samis
11

Eu tive o mesmo problema e descobri. Para tornar a vida muito mais simples, escrevi uma classe util para lidar com permissões de tempo de execução.

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

E o PreferenceUtil métodos são os seguintes.

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

Agora, tudo que você precisa é usar o método * checkPermission * com argumentos adequados.

Aqui está um exemplo,

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

como meu aplicativo sabe se o usuário verificou a opção "Nunca perguntar novamente"?

Se o usuário marcar Nunca perguntar novamente , você receberá retorno de chamada em onPermissionDisabled .

Feliz codificação :)

muthuraj
fonte
shouldShowRequestPermissionRationale Tenho erro aqui, você pode me ajudar.
Rucha Bhatt Joshi
eu não consigo encontrar este método shouldShowRequestPermissionRationale pode ser i não conseguiu obter contexto .. mas é i fina encontrada outra solução alternativa .. Obrigado por ajuda :)
Rucha Bhatt Joshi
1
Foi mal. shouldShowRequestPermissionRationale está disponível através de Activity, não no contexto. Atualizei minha resposta lançando o contexto para Activity antes de chamar esse método. Confira :)
muthuraj
1
Essa é a única maneira de contornar o primeiro valor falso retornado shouldShowRequestPermissionRationale, salvando de preferência a solicitação enviada ao usuário. Eu tive a mesma idéia e encontrei sua resposta. Bom trabalho
MatPag
4

Explicação completa para todos os casos de permissão

/**
 *    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
fonte
4

Uma função útil para determinar se uma permissão arbitrária foi impedida de solicitar (no 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
}

O uso disso requer a configuração de uma preferência compartilhada booleana com o nome da sua permissão desejada (por exemplo android.Manifest.permission.READ_PHONE_STATE)true quando você solicitar uma permissão pela primeira vez.


Explicação:

Build.VERSION.SDK_INT >= Build.VERSION_CODES.M como parte do código pode ser executada apenas no nível 23+ da API.

ContextCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED para verificar se ainda não temos permissão.

!activity.shouldShowRequestPermissionRationale(permission)para verificar se o usuário negou o pedido novamente. Devido a peculiaridades dessa função , a seguinte linha também é necessária.

PreferenceManager.getDefaultSharedPreferences(activity).getBoolean(permission, false) isso é usado (juntamente com a definição do valor como true na primeira solicitação de permissão) para distinguir entre os estados "Nunca perguntei" e "Nunca pergunte novamente", pois a linha anterior não retorna essas informações.

JakeSteam
fonte
4

O método shouldShowRequestPermissionRationale () pode ser usado para verificar se o usuário selecionou a opção 'nunca perguntou novamente' e negou a permissão. Existem muitos exemplos de código, então eu prefiro explicar como usá-lo para esse fim, porque acho que seu nome e sua implementação tornam isso mais complicado do que realmente é.

Conforme explicado em Solicitando permissões no tempo de execução , esse método retorna true se a opção 'nunca perguntar novamente' estiver visível, caso contrário, false; portanto, retorna false na primeira vez em que uma caixa de diálogo é exibida; depois, na segunda vez, retorna true; somente se o usuário negar a permissão para selecionar a opção, nesse ponto, ele retornará false novamente.

Para detectar esse caso, você pode detectar a sequência falso-verdadeiro-falso, ou (mais simples), pode ter um sinalizador que monitora a hora inicial em que a caixa de diálogo é exibida. Depois disso, esse método retorna verdadeiro ou falso, onde o falso permitirá que você detecte quando a opção está selecionada.

Alessio
fonte
3

Por favor, não jogue pedras em mim por esta solução.

Isso funciona, mas é um pouco "hacky".

Ao ligar requestPermissions, registre a hora atual.

        mAskedPermissionTime = System.currentTimeMillis();

Então em onRequestPermissionsResult

se o resultado não for concedido, verifique a hora novamente.

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

Como o usuário não pode clicar tão rapidamente no botão negar, sabemos que ele selecionou "nunca perguntar novamente" porque o retorno de chamada é instantâneo.

Use por sua conta e risco.

Antzi
fonte
e se virmos a caixa de diálogo solicitada por 5 minutos e depois negar?
Saksham
Então, qual é a utilidade disso, se não puder cumprir o requisito básico. Um código pode ser um hack, conforme aceito, se cumprir claramente todos os requisitos em todos os casos, não.
Saksham 13/0518
Sim, isso é ruim. Os testadores automáticos como esse podem conseguir clicar mais rápido que isso: developer.android.com/training/testing/crawler
stackzebra
2

Eu escrevi um atalho para solicitação de permissão no Android M. Esse código também lida com a compatibilidade com versões anteriores de versões mais antigas do Android.

Todo o código feio é extraído para um Fragmento que se anexa e se desanexa da Atividade solicitando as permissões. Você pode usar PermissionRequestManagero seguinte:

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

Dê uma olhada: https://gist.github.com/crysxd/385b57d74045a8bd67c4110c34ab74aa

crysxd
fonte
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
fonte
2

Experimente esta biblioteca de permissões simples. Ele irá lidar com todas as operações relacionadas à permissão em 3 etapas fáceis. Isso economizou meu tempo. Você pode concluir todo o trabalho relacionado a permissões em 15 minutos .

Ele pode lidar com Negar, Ele pode lidar com Nunca pedir novamente, Pode chamar configurações do aplicativo para obter permissão, Pode dar uma mensagem do Rational, Pode dar uma mensagem de Negação, Pode fornecer uma lista de permissões aceitas, Pode fornecer uma lista de negadas permissões e etc.

https://github.com/ParkSangGwon/TedPermission

Etapa 1: adicione sua dependência

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

Etapa 2: pedir permissões

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

Etapa 3: manipular a resposta da permissão

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
fonte
Ótimo. Ele salvou a minha vez
Vigneswaran A
Bom, fácil de usar
Uray Febri
2

você pode ouvir bonita.

Ouvinte

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

MainClass para permissão

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 dessa maneira

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

                })

substituir onRequestPermissionsResult na atividade ou fragmnet

override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
 if (requestCode == 118) {
        if (permissions[0] == Manifest.permission.ACCESS_FINE_LOCATION && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            getLastLocationInMap()
        }
        }
    }
Rasoul Miri
fonte
1

Em vez disso, você receberá retorno de chamada onRequestPermissionsResult()como PERMISSION_DENIED quando solicitar novamente a permissão enquanto estiver em falsa condição deshouldShowRequestPermissionRationale()

No documento do Android:

Quando o sistema solicita que o usuário conceda uma permissão, o usuário tem a opção de informar ao sistema para não solicitar essa permissão novamente. Nesse caso, sempre que um aplicativo usar essa requestPermissions()solicitação novamente, o sistema negará imediatamente a solicitação. O sistema chama seu onRequestPermissionsResult()método de retorno de chamada e passa PERMISSION_DENIED, da mesma forma que faria se o usuário tivesse rejeitado sua solicitação explicitamente novamente. Isso significa que, quando você liga requestPermissions(), não pode assumir que alguma interação direta com o usuário ocorreu.

Farhan
fonte
1

Você pode usar o if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)método para detectar se nunca perguntar está marcado ou não.

Para mais referência: Marque esta

Para verificar várias permissões, 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 explan ()

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

O código acima também mostrará uma caixa de diálogo, que redirecionará o usuário para a tela de configurações do aplicativo, de onde ele poderá dar permissão se tivesse marcado o botão nunca perguntar novamente.

user6435056
fonte
1

Você pode usar

shouldShowRequestPermissionRationale()

dentro

onRequestPermissionsResult()

Veja o exemplo abaixo:

Verifique se ele tem permissão quando o usuário clica no botão:

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

Quando o usuário responder à caixa de diálogo de permissão, iremos para 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();
            }
        }
    }

}
Logo Santos
fonte
0

Eu também gostaria de obter as informações se o usuário selecionou ou não "nunca perguntar novamente". Consegui uma "quase solução" com uma bandeira de aparência feia, mas antes de contar como, falarei sobre minha motivação:

Gostaria de oferecer a funcionalidade de referência de permissão inicialmente. Se o usuário o usa e não possui direitos, ele obtém a 1ª caixa de diálogo de cima ou a 2ª e a 3ª. Quando o usuário escolher 'Nunca perguntar novamente', gostaria de desativar a funcionalidade e exibi-la de maneira diferente. - Minha ação é acionada por uma entrada de texto giratório, eu também gostaria de adicionar '(Permissão revogada)' ao texto do rótulo exibido. Isso mostra ao usuário: 'Existe funcionalidade, mas não posso usá-la devido às minhas configurações de permissão.' No entanto, isso não parece ser possível, pois não posso verificar se 'Nunca perguntar novamente' foi escolhido.

Cheguei a uma solução em que posso viver, tendo minha funcionalidade sempre ativada com uma verificação de permissão ativa. Estou mostrando uma mensagem Toast em onRequestPermissionsResult () no caso de uma resposta negativa, mas somente se eu não tiver mostrado meu pop-up racional personalizado. Portanto, se o usuário escolher 'Nunca perguntar novamente', ele receberá apenas uma mensagem. Se o usuário estiver relutante em escolher 'nunca perguntar novamente', ele obtém apenas o raciocínio personalizado e o pop-up de solicitação de permissão pelo sistema operacional, mas não brindam, pois três notificações seguidas seriam muito trabalhosas.

ChristianKoelle
fonte
0

Eu tenho que implementar permissão dinâmica para a câmera. Onde ocorrem três casos possíveis: 1. Permitir, 2. Negado, 3. Não pergunte novamente.

 @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
fonte
0

Expandindo a resposta do mVck acima, a lógica a seguir determina se "Nunca perguntar novamente" foi verificado para uma determinada solicitação de permissão:

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

que é extraído de baixo (para o exemplo completo, veja esta resposta )

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 é
fonte
0

Para responder à pergunta com precisão, o que acontece quando o usuário pressiona "Nunca perguntar novamente"?

O método / função substituído

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

O array grantResult parece estar vazio, então você pode fazer algo lá, talvez? Mas não é a melhor prática.

Como lidar com "Never Ask Again"?

Estou trabalhando com o Fragment, que exigiu a permissão 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()

            }
        }
    }

As outras funções são triviais.

// 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
fonte