Ranjith Kumar
Ranjith Kumar

Reputation: 278

Android M permission issue with Dialog "Don't ask again"

It's a easier way to grant permission by Scoped Directory Access,but Dialog will show a checkbox named "Don't ask again". If the user selects Don't ask again and denies the request, all future requests for the given directory from your app will be automatically denied, and no request UI will be presented to the user. if user regret or hit that checkbox by mistake,how can app remedy? app can't get permission dialog.

How can we handle this?

Upvotes: 14

Views: 16234

Answers (7)

Joshua Pinter
Joshua Pinter

Reputation: 47551

Open Up the App's Permission Settings.

You cannot show another permissions dialog if the User has checked off "Don't ask again."

However, you can help the user and open up the App's Settings where the User can manually enable the Permissions required.

To do that you'll need to launch an intent, similar to this:

public void openAppSettings() {
    Uri packageUri = Uri.fromParts( "package", getApplicationContext().getPackageName(), null );

    Intent applicationDetailsSettingsIntent = new Intent();

    applicationDetailsSettingsIntent.setAction( Settings.ACTION_APPLICATION_DETAILS_SETTINGS );
    applicationDetailsSettingsIntent.setData( packageUri );
    applicationDetailsSettingsIntent.addFlags( Intent.FLAG_ACTIVITY_NEW_TASK );

    getApplicationContext().startActivity( applicationDetailsSettingsIntent );
}

Now, in order to know when the User has checked off the "Don't ask again" checkbox is another matter and can done with this StackOverflow answer.

Upvotes: 4

Grzesiek Eman
Grzesiek Eman

Reputation: 86

I found a CLEAN AND WORKING SOLUTION for this ( but there is no code example here, - read and understand it why) making sure there is no scenario when taking action that normally ask for permission, doesn't do anything.

The flag shouldShowPermission() - alone, there is no difference between the first time the app ask for given permission and after "dont show again was clicked", it will return false in both cases. It will keep returning true each time until the permission has been set do Dont Ask Again. Meaning starting a second time we ask for permission to infinity or until user clicks on dont ask again.

So to differentiate first time a permission is asked for, and the time when its asked for, after the user has already set Dont Ask Again, you can use a custom flag.

It is a clean and simple workaround to that that will determine if the don't ask again option was set. ( tested and working in 2 productions apps)

The solution:

Add a flag called "rationaleDisplayed" (or whatever you want, that will be indication of permissionRationeDialog being shown to user) - with default value false, store it in prefs. After presenting to user the rationale dialog at least once, set this flag to be true.

Now you have two params, and combination of them when shouldShowRationaleDialog = false, and rationalePermissionDisplayed = true, effectively is the indication of "Dont show again" being set.

Why this works? It works because the shouldShowRationale, will return false when first time asking for permission and the rationaleDisplayedFlag are both false, so the popup will show up correctly. (2 falses) - this is the case of asking for a permission first time.

Then if you deny the first time, sshouldShowRationale will be true, and rationaleDisplayed will be true - (2 trues), this will be the case until the dont set again option is used. - this is the case of asking for permission second time, after the first one was declined.

Finnaly, if you set Dont Ask Again - or on Android api 30 and decline permission 2 times, the flag shouldShowRationale will return false next time it is called.

There you have a case of shouldShowRationale = false, and your own flag rationaleDisplayed = true, which tells you that the Don't Ask Again was set. (otherwise, the shouldShowRationale would still be false). - this is the case when the user declined the permission twice (api 30) or set the Dont Show Again option while denying.

Having that logic case, you can now add a custom permission dialog, with instructions on how to manually enable permissions, and open the app settings with ok button. ( intent to open settings in dialog positive listener).

The flag rationaleDisplayed is basically there to make sure first time app ask for permission it will be shown correctly, but its value allows for determination of the state when user set to dont ask again.

Works like a charm.

Upvotes: 1

Collince Ogada Ogada
Collince Ogada Ogada

Reputation: 43

I created a method to capture all the user actions by use of concatenated if...else if...else and it worked well for me.
First, to determine if both permission was denied and the Don't ask again was also 'ticked', I combined the permission status check and a shouldShowRequestPermissionRationale(Manifest.permission.SEND_SMS).
Then to determine if only the permission was denied without 'ticking' the Don't ask again, I used permission status check. Below snippet:

@RequiresApi(api = Build.VERSION_CODES.M) //this is added for API lower than 23

public void myPermissionRationale(){
        //This checks both permission status and the Don't ask again check box
        if (ContextCompat.checkSelfPermission(this,Manifest.permission.SEND_SMS )
                == PackageManager.PERMISSION_DENIED && !shouldShowRequestPermissionRationale(Manifest.permission.SEND_SMS)) {
            //open app permission settings here for instance 

        }
           //only checks permission status then shows permission request pop up again
           else if (ContextCompat.checkSelfPermission(this,Manifest.permission.SEND_SMS )
                    == PackageManager.PERMISSION_DENIED){
                            // Request the permission
                            ActivityCompat.requestPermissions(this,
                                    new String[]{Manifest.permission.SEND_SMS},
                                    10);

                        }
        }

Upvotes: 0

larsaars
larsaars

Reputation: 2350

public class AccessDenied implements View.OnClickListener{

    public Dialog dialog;
    private LinearLayout contentLayout;
    private Activity context;
    private EditText password;
    private String passwordStr;
    private Runnable doOnAccessPermitted;
    private int wrongColor = Color.RED, defColor = Color.parseColor("#80000000");


    public AccessDenied(Activity con, String pwd) {
        passwordStr = pwd;
        context = con;
        dialog = new Dialog(context, R.style.AnimatedDialog);
        setCancelable(false);
        //init the dialog with content view/animations etc.
        dialog.setContentView(R.layout.access_denied_layout);
        contentLayout = dialog.findViewById(R.id.layoutIconDialogLinearLayout);
        password = dialog.findViewById(R.id.accessdeniedlayoutpassword);
        Button ok = dialog.findViewById(R.id.accessdeniedlayoutok);
        ok.setOnClickListener(this);
        //now the dialog is ready
    }

    public void setActionOnAccess(Runnable doOnAccess) {
        doOnAccessPermitted = doOnAccess;
    }

    public void setCancelable(boolean set) {
        dialog.setCancelable(set);
    }

    public void show() {
        dialog.show();
    }

    public void cancel() {
        dialog.cancel();
    }

    public void setPassword(String pwrd) {
        passwordStr = pwrd;
    }

    public void tryPassword(String tryp) {
        if(passwordStr.equals(tryp)){
            cancel();
            if(doOnAccessPermitted != null)
                doOnAccessPermitted.run();
        }
    }

    @Override
    public void onClick(View view) {
        if(passwordStr.equals(password.getText().toString())) {
            cancel();
            if(doOnAccessPermitted != null)
                doOnAccessPermitted.run();
        }else{
            password.getText().clear();
            Animation anim = AnimationUtils.loadAnimation(context, R.anim.edittext_shake);
            anim.setDuration(200);
            anim.setRepeatCount(5);
            decView().startAnimation(anim);
            decView().setBackgroundColor(wrongColor);
            new android.os.Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    decView().setBackgroundColor(defColor);
                }
            }, 1000);
        }
    }

    private View decView() {
        return password;
    }
}

Upvotes: 1

CommonsWare
CommonsWare

Reputation: 1007322

is any way to change that flag ?

A developer cannot change that flag. Otherwise, there would be no point in having the checkbox, as developers would just ignore it by changing the flag.

However, your question points out a fairly substantial flaw in the scoped directory access: the user has limited ability to change that flag. There does not appear to be a place in Settings to change this state specifically, the way the user can manually grant a rejected runtime permission.

On a Nexus 5X running the 7.1 preview, "Clear Data" will reset this flag, though that has broader effects. On a Google Pixel running 7.1, and on a Nexus 5X running Android 7.0, nothing will reset this flag, even a full uninstall of the app.

I have filed a bug report about this. I am skeptical that the situation will be improved much in the short term — at best, they might fix it so "Clear Data" works reliably.

Upvotes: 8

Lips_coder
Lips_coder

Reputation: 686

We should use shouldShowRequestPermissionRationale.Please go through this:

private void insertDummyContactWrapper() {
        int hasWriteContactsPermission = checkSelfPermission(Manifest.permission.WRITE_CONTACTS);
        if (hasWriteContactsPermission != PackageManager.PERMISSION_GRANTED) {
                if (!shouldShowRequestPermissionRationale(Manifest.permission.WRITE_CONTACTS)) {
                    showMessageOKCancel("You need to allow access to Contacts",
                            new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    requestPermissions(new String[] {Manifest.permission.WRITE_CONTACTS},
                                            REQUEST_CODE_ASK_PERMISSIONS);
                                }
                            });
                    return;
                }
            requestPermissions(new String[] {Manifest.permission.WRITE_CONTACTS},
                    REQUEST_CODE_ASK_PERMISSIONS);
            return;
        }
        insertDummyContact();
    }

Upvotes: 1

Mathias Nunez
Mathias Nunez

Reputation: 56

I think what you need to do is use the method shouldShowRequestPermissionRationale(String) it would return false if user has denied the permission and checked "Don't ask again".

What you should do is show an alert explaining the user why you need the permission or implement a fallback, like disable some feature.

Hope to be helpful.

Upvotes: 2

Related Questions