Permissões do Android M: Confuso sobre o uso da função shouldShowRequestPermissionRationale ()

148

Eu estava examinando o documento oficial sobre o novo modelo de Permissões no Android M. Ele fala sobre a shouldShowRequestPermissionRationale()função que retorna truese o aplicativo solicitou essa permissão anteriormente e o usuário negou a solicitação. Se o usuário recusou a solicitação de permissão no passado e escolheu a opção Não perguntar novamente, esse método retornará false.

Mas como podemos diferenciar os dois casos a seguir?

Caso 1 : o aplicativo não tem permissão e o usuário não foi solicitado antes. Nesse caso, shouldShowRequestPermissionRationale () retornará false, porque é a primeira vez que solicitamos ao usuário.

Caso 2 : o usuário negou a permissão e selecionou "Não perguntar novamente"; nesse caso, também deveriaShowRequestPermissionRationale () retornará false.

Gostaria de enviar o usuário para a página de configurações do aplicativo no caso 2. Como faço para diferenciar esses dois casos?

akshayt23
fonte
1
A resposta aceita é boa. Como alternativa, você também pode usar um pref compartilhado para saber se o aplicativo solicitou a permissão antes. Apenas lançando isso lá fora, caso seja mais aplicável à situação de outra pessoa.
Rockin4Life33
4
Também existe o caso 3: o usuário foi solicitado e concedeu / negou a permissão, mas usou as configurações de permissão para voltar a "perguntar sempre". O teste shows shouldShowRequestPermissionRationale()retorna falso neste caso, o que prejudicará qualquer código que contenha um sinalizador "já perguntei antes".
Logan Pickup
Aqui está uma amostra do Google que mostra as práticas recomendadas no permissionsAndroid. github.com/android/permissions-samples
itabdullah 22/01

Respostas:

172

Após M Preview 1, se a caixa de diálogo for exibida pela primeira vez , não haverá a caixa de seleção Nunca perguntar novamente .

Se o usuário negar a solicitação de permissão, haverá uma caixa de seleção Nunca perguntar novamente na caixa de diálogo de permissão na segunda vez que a permissão for solicitada.

Portanto, a lógica deve ser assim:

  1. Solicitar permissão:

    if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(context, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CODE);
    } else {
        //Do the stuff that requires permission...
    }
    
  2. Verifique se a permissão foi negada ou concedida onRequestPermissionsResult.

    Se a permissão foi negada anteriormente, desta vez, haverá uma caixa de seleção Nunca perguntar novamente na caixa de diálogo de permissão.

    Ligue shouldShowRequestPermissionRationalepara verificar se o usuário verificou Nunca perguntar novamente . shouldShowRequestPermissionRationaleO método retornará false apenas se o usuário selecionado Nunca perguntar novamente ou a política do dispositivo proibir o aplicativo de ter essa permissão:

    if (grantResults.length > 0){
        if(grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            //Do the stuff that requires permission...
        }else if (grantResults[0] == PackageManager.PERMISSION_DENIED){
            // Should we show an explanation?
            if (ActivityCompat.shouldShowRequestPermissionRationale(context, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                //Show permission explanation dialog...
            }else{
                //Never ask again selected, or device policy prohibits the app from having that permission.
                //So, disable that feature, or fall back to another situation...
            }
        }
    }
    

Portanto, você não precisará rastrear se um usuário marcou Nunca perguntar novamente ou não.

CanC
fonte
48
Um ponto de esclarecimento, shouldShowRequestPermissionRationale (), também retornará false se o usuário nunca tiver sido solicitado a pedir permissão (ou seja, a primeira vez que o aplicativo for executado). Você não entraria nesse caso se seguir a lógica do exemplo fornecido. Mas a redação, abaixo de 2, é um pouco enganadora.
Ben
1
@ Canc muito explicação agradável. Obrigado :)
AndoAiron
14
Não tenho certeza, isso parece falho. Como devemos saber se é a primeira vez que o usuário é solicitado? Eu tenho que rastrear se o usuário foi perguntado, e se ele fez, então eu tenho que reverter a lógica. Não faz nenhum sentido para mim.
22817 Daniel F
4
Eu acho que é importante notar que onde você está passando contextno ActivityCompat.shouldShowRequestPermissionRationale(...)parâmetro é realmente do tipo Activity. Pode não afetar todos, mas, no meu caso, afeta.
aProperFox 5/09
7
Essa lógica do Android é tão estúpida! Obriga-me a ligar shouldpara o retorno de chamada e salvar seu contra-valor no NVM apenas para saber se preciso solicitar a solicitação novamente na próxima vez que o aplicativo for aberto! ... uau (facepalm) ... foi muito difícil fazer apenas uma chamada retornando uma enumeração de status?
Shockwaver
22

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 os métodos PreferenceUtil 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();
                        }
                    });

Caso 1: o aplicativo não tem permissão e o usuário não foi solicitado antes. Nesse caso, shouldShowRequestPermissionRationale () retornará false, porque é a primeira vez que solicitamos ao usuário.

Caso 2: O usuário negou a permissão e selecionou "Não perguntar novamente"; nesse caso, também deveriaShowRequestPermissionRationale () retornará false.

Gostaria de enviar o usuário para a página de configurações do aplicativo no caso 2. Como faço para diferenciar esses dois casos?

Você receberá retorno de chamada em onPermissionAsk no caso 1 e em onPermissionDisabled no caso 2.

Feliz codificação :)

muthuraj
fonte
Excelente explicação mano. Seguiu exatamente o mesmo procedimento. :)
Sumit Jha
O que devo preencher para esta atividade? public void onPermissionAsk() { ActivityCompat.requestPermissions( thisActivity, ... .
Mardymar
@Mardymar thisActivityé nada além de YourActivity.this.
precisa saber é
1
como lidar com várias permissões e como integrar esse código no fragmento.
Taimur
Que tipo de contextvocê está usando? shouldShowRequestPermissionRationale(permission)não existe em android.content.Context. está em ActivityCompat
Hilikus
8

ATUALIZAR

Acredito que a resposta do CanC abaixo seja a correta que deve ser seguida. A única maneira de ter certeza é verificar isso no retorno de chamada onRequestPermissionResult usando shouldShowPermissionRationale.

==

Minha resposta original:

A única maneira que eu encontrei é acompanhar por conta própria se é a primeira vez ou não (por exemplo, usando preferências compartilhadas). Se não for a primeira vez, use shouldShowRequestPermissionRationale()para diferenciar.

Veja também: Android M - verifique a permissão de tempo de execução - como determinar se o usuário verificou "Nunca perguntar novamente"?

Alex Florescu
fonte
1
Sim, até eu concordo que o método do CanC é o que deve ser seguido. Vou marcar como a resposta aceita.
precisa saber é o seguinte
6

Pelo que entendi, shouldShowRequestPermissionRationale () executa vários casos de uso ocultos e notifica o aplicativo se deve ou não mostrar uma explicação sobre as permissões solicitadas.

A idéia por trás das permissões de tempo de execução é que, na maioria das vezes, o usuário dirá Sim à solicitação de permissão. Dessa forma, o usuário precisará fazer apenas um clique. Obviamente, a solicitação deve ser usada no contexto correto - ou seja, solicitando a permissão da câmera quando o botão "Câmera" é pressionado.

Se o usuário negar a solicitação, mas depois de algum tempo aparecer e pressionar o botão "Câmera" novamente, shouldShowRequestPermissionRationale () retornará true, para que o aplicativo possa mostrar uma explicação significativa por que a permissão é solicitada e por que o aplicativo não funcionar corretamente sem ele. Normalmente, você mostraria nessa janela de diálogo um botão para negar novamente / decidir mais tarde e um botão para conceder as permissões. O botão conceder permissões na caixa de diálogo de justificativa deve iniciar a solicitação de permissão novamente. Dessa vez, o usuário também terá uma caixa de seleção "Nunca mostrar novamente". Se ele decidir selecioná-lo e negar a permissão novamente, notificará o sistema Android de que o usuário e o aplicativo não estão na mesma página. Essa ação teria duas consequências - shouldShowRequestPermissionRationale () sempre retornará false,

Mas também há outro cenário possível em que onRequestPermissionsResult pode ser usado. Por exemplo, alguns dispositivos podem ter uma política de dispositivo que desativa a câmera (trabalhando para CIA, DARPA, etc). Nesses dispositivos, onRequestPermissionsResult sempre retornará false, e o método requestPermissions () negará silenciosamente a solicitação.

Foi o que reuni ouvindo o podcast com Ben Poiesz - gerente de produto na estrutura do Android.
http://androidbackstage.blogspot.jp/2015/08/episode-33-permission-mission.html

Shumoapp
fonte
6

Basta postar outra opção, se alguém quiser. Você pode usar o EasyPermissions, fornecido pelo próprio Google, para, como dito, "Simplificar as permissões do sistema Android M".

Então você não precisa lidar shouldShowRequestPermissionRationalediretamente.

Wei WANG
fonte
Por que não ver este projeto prevoiusly :)
Vlad
O problema com EasyPermissions permanece quase o mesmo. Solicitar permissionPermanentlyDeniedinternamente apenas chamadas shouldShowPermissionsRationalee retornos trueno caso em que o usuário nunca foi solicitado a conceder permissões.
hgoebl
4

Se alguém estiver interessado em uma solução Kotlin, refatorei a resposta do @muthuraj para estar no Kotlin. Também modernizou um pouco o bloqueio de conclusão em vez dos ouvintes.

PermissionUtil

object PermissionUtil {
    private val PREFS_FILE_NAME = "preference"

    fun firstTimeAskingPermission(context: Context, permission: String, isFirstTime: Boolean) {
        val sharedPreference = context.getSharedPreferences(PREFS_FILE_NAME, MODE_PRIVATE)
        sharedPreference.preferences.edit().putBoolean(permission,
                isFirstTime).apply()
    }

    fun isFirstTimeAskingPermission(context: Context, permission: String): Boolean {
        val sharedPreference = context.getSharedPreferences(PREFS_FILE_NAME, MODE_PRIVATE)
        return sharedPreference.preferences.getBoolean(permission,
                true)
    }
}

PermissionHandler

enum class CheckPermissionResult {
    PermissionAsk,
    PermissionPreviouslyDenied,
    PermissionDisabled,
    PermissionGranted
}

typealias PermissionCheckCompletion = (CheckPermissionResult) -> Unit


object PermissionHandler {

    private fun shouldAskPermission(context: Context, permission: String): Boolean {
        return ContextCompat.checkSelfPermission(context,
                permission) != PackageManager.PERMISSION_GRANTED
    }

    fun checkPermission(context: Context, permission: String, completion: PermissionCheckCompletion) {
        // If permission is not granted
        if (shouldAskPermission(context, permission)) {
            //If permission denied previously
            if ((context as Activity).shouldShowRequestPermissionRationale(permission)) {
                completion(CheckPermissionResult.PermissionPreviouslyDenied)
            } else {
                // Permission denied or first time requested
                if (PermissionUtil.isFirstTimeAskingPermission(context,
                                permission)) {
                    PermissionUtil.firstTimeAskingPermission(context,
                            permission,
                            false)
                    completion(CheckPermissionResult.PermissionAsk)
                } else {
                    // Handle the feature without permission or ask user to manually allow permission
                    completion(CheckPermissionResult.PermissionDisabled)
                }
            }
        } else {
            completion(CheckPermissionResult.PermissionGranted)
        }
    }
}

Implementação

PermissionHandler.checkPermission(activity,
                    Manifest.permission.CAMERA) { result ->
                when (result) {
                    CheckPermissionResult.PermissionGranted -> {
                        // openCamera()
                    }
                    CheckPermissionResult.PermissionDisabled -> {
                        // displayAlert(noPermissionAlert)
                    }
                    CheckPermissionResult.PermissionAsk -> {
                        // requestCameraPermissions()
                    }
                    CheckPermissionResult.PermissionPreviouslyDenied -> {
                        // displayAlert(permissionRequestAlert)
                    }
                }
            }
bmjohns
fonte
3

Verifique esta implementação. está funcionando muito bem para mim. basicamente, você verifica as permissões no método checkPermissions () passando uma lista de permissões. Você verifica o resultado da solicitação de permissão em onRequestPermissionsResult (). A implementação permite abordar os dois casos em que o usuário seleciona "nunca perguntar novamente" ou não. Nesta implementação, caso se selecione "nunca perguntar novamente", a caixa de diálogo tem uma opção para levá-lo à atividade de configurações do aplicativo.

Todo esse código está dentro do meu fragmento. Eu estava pensando que seria melhor criar uma classe especializada para fazer isso, como um PermissionManager, mas não tenho certeza.

/**
     * responsible for checking if permissions are granted. In case permissions are not granted, the user will be requested and the method returns false. In case we have all permissions, the method return true.
     * The response of the request for the permissions is going to be handled in the onRequestPermissionsResult() method
     * @param permissions list of permissions to be checked if are granted onRequestPermissionsResult().
     * @param requestCode request code to identify this request in
     * @return true case we already have all permissions. false in case we had to prompt the user for it.
     */
    private boolean checkPermissions(List<String> permissions, int requestCode) {
        List<String> permissionsNotGranted = new ArrayList<>();
        for (String permission : permissions) {
            if (ContextCompat.checkSelfPermission(getActivity(), permission) != PackageManager.PERMISSION_GRANTED)
                permissionsNotGranted.add(permission);
        }

        //If there is any permission we don't have (it's going to be in permissionsNotGranted List) , we need to request.
        if (!permissionsNotGranted.isEmpty()) {
            requestPermissions(permissionsNotGranted.toArray(new String[permissionsNotGranted.size()]), requestCode);
            return false;
        }
        return true;
    }

    /**
     * called after permissions are requested to the user. This is called always, either
     * has granted or not the permissions.
     * @param requestCode  int code used to identify the request made. Was passed as parameter in the
     *                     requestPermissions() call.
     * @param permissions  Array containing the permissions asked to the user.
     * @param grantResults Array containing the results of the permissions requested to the user.
     */
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        switch (requestCode) {
            case YOUR_REQUEST_CODE: {
                boolean anyPermissionDenied = false;
                boolean neverAskAgainSelected = false;
                // Check if any permission asked has been denied
                for (int i = 0; i < grantResults.length; i++) {
                    if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
                        anyPermissionDenied = true;
                        //check if user select "never ask again" when denying any permission
                        if (!shouldShowRequestPermissionRationale(permissions[i])) {
                            neverAskAgainSelected = true;
                        }
                    }
                }
                if (!anyPermissionDenied) {
                    // All Permissions asked were granted! Yey!
                    // DO YOUR STUFF
                } else {
                    // the user has just denied one or all of the permissions
                    // use this message to explain why he needs to grant these permissions in order to proceed
                    String message = "";
                    DialogInterface.OnClickListener listener = null;
                    if (neverAskAgainSelected) {
                        //This message is displayed after the user has checked never ask again checkbox.
                        message = getString(R.string.permission_denied_never_ask_again_dialog_message);
                        listener = new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                //this will be executed if User clicks OK button. This is gonna take the user to the App Settings
                                startAppSettingsConfigActivity();
                            }
                        };
                    } else {
                        //This message is displayed while the user hasn't checked never ask again checkbox.
                        message = getString(R.string.permission_denied_dialog_message);
                    }
                    new AlertDialog.Builder(getActivity(), R.style.AlertDialogTheme)
                            .setMessage(message)
                            .setPositiveButton(getString(R.string.label_Ok), listener)
                            .setNegativeButton(getString(R.string.label_cancel), null)
                            .create()
                            .show();
                }
            }
            break;
            default:
                super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }

    /**
     * start the App Settings Activity so that the user can change
     * settings related to the application such as permissions.
     */
    private void startAppSettingsConfigActivity() {
        final Intent i = new Intent();
        i.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
        i.addCategory(Intent.CATEGORY_DEFAULT);
        i.setData(Uri.parse("package:" + getActivity().getPackageName()));
        i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        i.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
        i.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
        getActivity().startActivity(i);
    }
Thiago Saraiva
fonte
2

Pode ser útil para alguém: -

O que 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 para exemplo de trabalho detalhado .

Nicks
fonte
6
retorna falso pela primeira vez. não é verdade
Jom
Sim, foi o que eu mencionei. Se você marcar o sinalizador no método de retorno de chamada onRequestPermissionsResult (), ele terá apenas dois estados, especificamente nesse retorno de chamada.
Nicks
2
Infelizmente, shouldShowRequestPermissionRationale sempre retorna false - independentemente de o usuário ter negado a permissão ou não.
IgorGanapolsky
1

Podemos fazer assim?

@Retention(RetentionPolicy.SOURCE)
@IntDef({GRANTED, DENIED, NEVER})
public @interface PermissionStatus {
}

public static final int GRANTED = 0;
public static final int DENIED = 1;
public static final int NEVER = 2;

@PermissionStatus
public static int getPermissionStatus(Activity activity, String permission) {
    if (ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)) {
        return DENIED;
    } else {
        if (ActivityCompat.checkSelfPermission(activity, permission) == PackageManager.PERMISSION_GRANTED) {
            return GRANTED;
        } else {
            return NEVER;
        }
    }
}
Dr. aNdRO
fonte
Infelizmente, esse código não faz distinção entre uma situação em que a permissão nunca foi solicitada antes e onde "nunca solicitar novamente" foi verificada.
Ben
você deve usar a combinação desta + a classe auxiliar de permissão para verificar se a permissão é concedida ou não.
Dr. aNdRO
0

shouldShowRequestPermissionRationale para permissão ESPECIAL sempre retorne VERDADEIRO SOMENTE depois que o usuário negou sem caixa de seleção

Estamos interessados ​​no valor FALSE

Portanto, há 3 casos perdidos com valor falso :

1. não havia essa ação anteriormente e agora o usuário decide concordar ou negar.

Basta definir uma preferência ASKED_PERMISSION_*que não existe agora e seria verdadeira no onRequestPermissionsResultinício, em qualquer caso de concordar ou negar

Portanto, embora essa preferência não exista, não há motivo para verificarshouldShowRequestPermissionRationale

2. o usuário clicou em concordar.

Simplesmente faça:

checkCallingOrSelfPermission(permission) == PackageManager.PERMISSION_GRANTED

O qual retornará verdadeiro e não há motivo para verificarshouldShowRequestPermissionRationale

3. usuário clicou em negar com a caixa de seleção (segunda ou mais vezes solicitada)

É A HORA DE TRABALHAR, com o shouldShowRequestPermissionRationalequal retornará FALSE

(existe preferência e não temos permissão)

Vlad
fonte
0

Esse código solicita que o usuário solicite permissão durante o tempo de execução, se o usuário permitir, execute o método de resultado, se o usuário negar, pergunte novamente com descrição com o usuário negue (peça novamente com instruções), mas se o usuário optar por nunca solicitar novamente. ele lida com nunca perguntar novamente, exibir a opção de configurações abertas com instruções.

public String storagePermissions = Manifest.permission.READ_EXTERNAL_STORAGE;   
private static final int REQUEST_ACCESS =101;  

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
      if(checkSelfPermission(storagePermissions)== PackageManager.PERMISSION_GRANTED){
          result();    // result  is your block of code 
      }else {
          requestPermissions(new String[]{storagePermissions},REQUEST_ACCESS);
      }

    }
    else{
        result();    //so if user is lower than api verison M, no permission is requested
    } 

}

 private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener) {
    new AlertDialog.Builder(MainActivity.this)
            .setMessage(message)
            .setTitle("Hi User..")
            .setPositiveButton("Ok", okListener)
            .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {        //idea calling showMessage funtion again
                    Snackbar mySnackbar = Snackbar.make( findViewById(R.id.coordinatorlayout),"You Press Cancel.. ", Snackbar.LENGTH_INDEFINITE);
                    mySnackbar.setAction("Exit", new cancelButton());
                    mySnackbar.show();

                }
            })
            .create()
            .show();
}


private void result(){
          //your code
}

    @RequiresApi(api = Build.VERSION_CODES.M)
public class NeverAskAgain implements View.OnClickListener{
    @Override
    public void onClick(View view)
    {
        goToSettings();
    }
}
@RequiresApi(api = Build.VERSION_CODES.M)
private void goToSettings() {
    Intent myAppSettings = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.parse("package:" + getPackageName()));
    finish();
    myAppSettings.addCategory(Intent.CATEGORY_DEFAULT);
    myAppSettings.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    startActivityForResult(myAppSettings, REQUEST_APP_SETTINGS);
}
public class cancelButton implements View.OnClickListener{
    @Override
    public void onClick(View view){
        Toast.makeText(MainActivity.this,"To use this app , you must grant storage permission",Toast.LENGTH_SHORT);
        finish();
    }
    }


 @Override
@RequiresApi(api = Build.VERSION_CODES.M)
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode,permissions,grantResults);

    switch(requestCode) {
        case REQUEST_ACCESS:
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    // permission is granted
                    result();
                    break;
                }
                else if (!shouldShowRequestPermissionRationale(permissions[0])){
                    showMessageOKCancel("You choose Never Ask Again,option",
                new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        Snackbar mySnackbar = Snackbar.make(findViewById(R.id.coordinatorlayout), "Permission=>Storage=>On", Snackbar.LENGTH_INDEFINITE);
                        mySnackbar.setAction("Settings", new NeverAskAgain());
                        mySnackbar.show();
                    }
                     });
                    break;
                }
                else {
                    showMessageOKCancel("You Denid permission Request..",
                    new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            requestPermissions(new String[]{storagePermissions}, REQUEST_ACCESS);
                        }
                    });
                    break;
                }
        }
}
Abhishek Garg
fonte