Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get "Never ask Again" option in runtime permission

In my app I have created a class name Untility where I wrore the code for permission of READ_EXTERNAL_STORAGE. But the problem is when I click Deny on the app, I am not finding the alert notification to set it allow again. Initialy I have the option "Never ask Again". Once I clicked on it, the dialog desapperas. and now after running the app if I click on deny, I cannot find Dialog message anymore to make it allow again. How can I modify my code to show this message every time.

My Utility Class is

public class Utility {

public static final int MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE = 123;

@TargetApi(Build.VERSION_CODES.M)
public static boolean checkPermission(final Context context) {
    int currentAPIVersion = Build.VERSION.SDK_INT;
    if (currentAPIVersion >= android.os.Build.VERSION_CODES.M) {
        if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            if (ActivityCompat.shouldShowRequestPermissionRationale((Activity) context, Manifest.permission.READ_EXTERNAL_STORAGE)) {
                Log.v("TAG", "Permission is granted");
            }
            else {
                ActivityCompat.requestPermissions((Activity) context, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE);
            }
            return false;
        } else {
            return true;
        }
    } else {
        return true;
    }
 }

My Another class here I am calling the Utility class is

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    switch (requestCode) {
        case Utility.MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE:
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                if(userChoosenTask.equals("Take Photo"))
                    cameraIntent();
                else if(userChoosenTask.equals("Choose from Library"))
                    galleryIntent();

                    //do something here
            } else {
                //code for deny
                Toast.makeText(DetailMyColleague.this, "Permission Denied", Toast.LENGTH_SHORT).show();
            }
            break;
    }
}
private void selectImage() {
    final CharSequence[] items = { "Take Photo", "Choose from Library",
            "Cancel" };

    AlertDialog.Builder builder = new AlertDialog.Builder(DetailMyColleague.this);
    builder.setTitle("Add Photo!");
    builder.setItems(items, new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int item) {
            boolean result=Utility.checkPermission(DetailMyColleague.this);

            if (items[item].equals("Take Photo")) {
                userChoosenTask ="Take Photo";
                if(result)
                    cameraIntent();

            } else if (items[item].equals("Choose from Library")) {
                userChoosenTask ="Choose from Library";
                if(result)
                    galleryIntent();

            } else if (items[item].equals("Cancel")) {
                dialog.dismiss();
            }
        }
    });
    builder.show();
}
private void galleryIntent()
{
    Intent intent = new Intent();
    intent.setType("image/*");
    intent.setAction(Intent.ACTION_GET_CONTENT);//
    startActivityForResult(Intent.createChooser(intent, "Select File"),SELECT_FILE);
}
private void cameraIntent()
{
    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    startActivityForResult(intent, REQUEST_CAMERA);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (resultCode == Activity.RESULT_OK) {
        if (requestCode == SELECT_FILE)
            onSelectFromGalleryResult(data);
        else if (requestCode == REQUEST_CAMERA)
            onCaptureImageResult(data);
    }
}

private void onCaptureImageResult(Intent data) {
    Bitmap thumbnail = (Bitmap) data.getExtras().get("data");
    ByteArrayOutputStream bytes = new ByteArrayOutputStream();
    thumbnail.compress(Bitmap.CompressFormat.JPEG, 90, bytes);

    File destination = new File(Environment.getExternalStorageDirectory(),
            System.currentTimeMillis() + ".jpg");

    FileOutputStream fo;
    try {
        destination.createNewFile();
        fo = new FileOutputStream(destination);
        fo.write(bytes.toByteArray());
        fo.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }

    profilePic.setImageBitmap(thumbnail);
}
@SuppressWarnings("deprecation")
private void onSelectFromGalleryResult(Intent data) {

    Bitmap bm=null;
    if (data != null) {
        try {
            bm = MediaStore.Images.Media.getBitmap(getApplicationContext().getContentResolver(), data.getData());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    profilePic.setImageBitmap(bm);
}

2 Answers

You cannot show the same Request message once they say "don't ask again", but you can check for the permission, and if denied, show a custom dialog that directs them to the settings page with an intent:

startActivityForResult(new Intent(android.provider.Settings.ACTION_SETTINGS), 0);

If you permission check fails:

if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
    // show a dialog
    new AlertDialog.Builder(this).setMessage("You need to enable permissions to use this feature").setPositiveButton("Go to settings", new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            // navigate to settings
            startActivityForResult(new Intent(android.provider.Settings.ACTION_SETTINGS), 0);
        }
    }).setNegativeButton("Go back", new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            // leave?
            MyActivity.this.onBackPressed();
        }
    }).show();
}
like image 160
anomeric Avatar answered Nov 17 '25 08:11

anomeric


'Don't show again' state can be figured out when permission is already 'Denied' and method shouldShowRequestPermissionRationale() returns false

This method can be used as possibility to show additional info to user, when system dialogs can't be shown (user selects "Accept" of "Don't show again") by system

Deny             >> shouldShowRequestPermissionRationale(permission) -> true
Don't ask again  >> shouldShowRequestPermissionRationale(permission) -> false
Accept           >> shouldShowRequestPermissionRationale(permission) -> false

In case if you can't provide any action while all permissions you requested are Denied and at least one of them is denied by 'Don't ask again' checkbox - the best approach is to navigate user to Application Settings in System.

Here is a solution for "Accept"/"Deny"/"Don't ask again" workflow. Also here is an example for Jetpack Activity Result API. (requesting multiple permissions and navigation to another activity with coming back)

Comments for code:

  • Requesting permission (with Activity Result API) also contains check for 'Granted'-status of permission inside the system, you receive only true/false result for every permission you requested
  • actionPermXXX are the place where you can provide your actions with system just after permission was granted (for example start Location observing and\or fetching images from External Storage etc.)
  • 'Don't show again' state of permission can be reset by manually changing this permission in Applications'Settings (Settings > Apps > 'Your_app' > Permissions)

PS. Code is not perfect, but good for understanding

Fragment

class Fragment_ActiityResultAPI_RequestMultiplePermissions : Fragment(){

    val actionPermLocation = {
        tvPerm1.text = "GRANTED";
        tvPerm1.setTextColor(Color.GREEN)
    }
    val actionPermReadExt = {
        tvPerm2.text = "GRANTED";
        tvPerm2.setTextColor(Color.GREEN)
    }
    val permissionsAll = mutableMapOf(
        Manifest.permission.ACCESS_FINE_LOCATION  to actionPermLocation,
        Manifest.permission.READ_EXTERNAL_STORAGE to actionPermReadExt,
    )
    private val arcRequestPermissions = registerForActivityResult(RequestMultiplePermissions()){ perms ->
        perms.entries.forEach {
            if(it.value){
                permissionsAll[it.key]?.invoke()
            }
        }
    }
    private val arcNavigateToSettings = registerForActivityResult(StartSettingsActivityContract()) {
        arcRequestPermissions.launch(permissionsAll.keys.toTypedArray())
    }
    override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
        return inflater.inflate(R.layout.fragment_layout, container, false)
    }
    override fun onViewCreated(layoutView: View, savedInstanceState: Bundle?) {
        super.onViewCreated(layoutView, savedInstanceState)
        // CHECK PERMISSIONS AT FRAGMENT START
        arcRequestPermissions.launch(permissionsAll.keys.toTypedArray())
        btn.setOnClickListener {
            // CHECK PERMISSION AT BUTTON CLICK
            processPermission()
        }
    }
    private fun processPermission() {
        var atLeastOnePermDenied         = false
        var atLeastOnePermAsDontAskAgain = false
        permissionsAll.keys.toTypedArray().forEach {
            atLeastOnePermDenied         = atLeastOnePermDenied         || checkPermDenied(it)
            atLeastOnePermAsDontAskAgain = atLeastOnePermAsDontAskAgain || checkPermDontAskAgain(it)
        }
        if(atLeastOnePermAsDontAskAgain){
            showAlertNavigateToAppSettings()
            return
        }
        if(atLeastOnePermDenied){
            arcRequestPermissions.launch(permissionsAll.keys.toTypedArray())
            return
        }
        Utils.toast(requireContext(), ">>>  Execute your target action!!  <<<")
    }
    private fun checkPermDenied(perm: String): Boolean {
        return (ActivityCompat.checkSelfPermission(requireContext(), perm)
                ==
                PackageManager.PERMISSION_DENIED)
    }
    private fun checkPermDontAskAgain(perm: String): Boolean {
        return checkPermDenied(perm) && !shouldShowRequestPermissionRationale(perm)
    }
    private fun showAlertNavigateToAppSettings() {
        val builder = AlertDialog.Builder(requireContext())
        builder.setMessage("You have to grant permissions for action")
        builder.setPositiveButton("Go to Settings") { dialog, which -> // Do nothing but close the dialog
            arcNavigateToSettings.launch(1)
        }
        builder.setNegativeButton("Cancel") { dialog, which -> // Do nothing
            dialog.dismiss()
        }
        val alert = builder.create()
        alert.show()
    }
    class StartSettingsActivityContract : ActivityResultContract<Int, String?>() {
        override fun createIntent(context: Context, input: Int): Intent {
            return Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
                val uri = Uri.fromParts("package", context.packageName, null)
                this.data = uri
            }
        }
        override fun parseResult(resultCode: Int, intent: Intent?): String? {
            return ""
        }
    }
}

Layout

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="@android:color/white">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <TextView
                android:layout_width="150dp"
                android:layout_height="wrap_content"
                android:text="LOCATION: "
                android:textColor="@android:color/darker_gray"/>

            <TextView
                android:id="@+id/tvPerm1"
                android:layout_width="100dp"
                android:layout_height="wrap_content"
                android:text="Denied"
                android:textColor="@android:color/darker_gray"/>

        </TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <TextView
                android:layout_width="150dp"
                android:layout_height="wrap_content"
                android:text="READ_STORAGE: "
                android:textColor="@android:color/darker_gray"/>

            <TextView
                android:id="@+id/tvPerm2"
                android:layout_width="100dp"
                android:layout_height="wrap_content"
                android:text="Denied"
                android:textColor="@android:color/darker_gray"/>

        </TableRow>

    </LinearLayout>

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <Button
            android:id="@+id/btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="50dp"
            android:layout_gravity="center_horizontal"
            android:text="Launch a Camera"/>
    </FrameLayout>

</LinearLayout>

build.gradle

implementation 'androidx.activity:activity-ktx:1.2.1'
implementation 'androidx.fragment:fragment:1.3.0-alpha05'
like image 41
chatlanin Avatar answered Nov 17 '25 08:11

chatlanin



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!