3
votes

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

4 Answers

2
votes

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

'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'
0
votes

How can I modify my code to show thi message every time.

You can't. If the user checked "Don't ask again", then the only way the user can grant permission is via the Settings app. Once the user checks "don't ask again" and denies the permission, you cannot display the permission dialog for any permission in that permission group.

0
votes

The "Don't ask again" checkbox is visible only after more than one deny per permission code. If you check "Don't ask again" and click on Deny then permission will be automatically denied. If you want to choose again, go to the "App info" page of your application and clear data storage of your app. Now, if you go back in your app and request permission again. Then, the default permission dialog is showing. You can see documentation here.