java – Android runtime permissions- how to implement-ThrowExceptions

Exception or error:

Android Developer Documentation gives this example of requesting permissions at runtime:

// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(thisActivity,
            Manifest.permission.READ_CONTACTS)
    != PackageManager.PERMISSION_GRANTED) {

    // Should we show an explanation?
    if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
            Manifest.permission.READ_CONTACTS)) {

        // Show an expanation to the user *asynchronously* -- don't block
        // this thread waiting for the user's response! After the user
        // sees the explanation, try again to request the permission.

    } else {

        // No explanation needed, we can request the permission.

        ActivityCompat.requestPermissions(thisActivity,
                new String[]{Manifest.permission.READ_CONTACTS},
                MY_PERMISSIONS_REQUEST_READ_CONTACTS);

        // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
        // app-defined int constant. The callback method gets the
        // result of the request.
    }
}

What is “MY_PERMISSIONS_REQUEST_READ_CONTACTS” in this example? It says it’s an app-defined int constant, but does that mean I should make a Constants.java and declare a public static int? What should the value be?

In other examples I see people use 1 here, or 0 or 0xFFEEDDCC, but I can’t find an explanation of what it is. Can someone explain to me what needs to go here and why? (In my case, I need to make sure the app has permission to access fine location)

The ActivityCompat documentation says “Application specific request code to match with a result reported to onRequestPermissionsResult”? This does not help me.

How to solve:

What is “MY_PERMISSIONS_REQUEST_READ_CONTACTS” in this example?

It is an int, to tie a particular requestPermissions() call to the corresponding onRequestPermissionsResult() callback.

Under the covers, requestPermissions() uses startActivityForResult(); this int serves the same role as it does in startActivityForResult().

does that mean I should make a Constants.java and declare a public static int?

I would just make it a private static final int in the activity. But, you can declare it wherever you want.

What should the value be?

I seem to recall that it needs to be below 0x8000000, but otherwise it can be whatever you want. The value that you use for each requestPermissions() call in an activity should get a distinct int, but the actual numbers do not matter.

If your activity has only one requestPermissions() call, then the int value really does not matter. But many apps will have several requestPermissions() calls in an activity. In that case, the developer may need to know, in onRequestPermissionsResult(), what request this is the result for.

###

Look just a little further down in the documentation under “Handle the permissions request response” and you will see its purpose.

A callback method called onRequestPermissionsResult gets sent back the same code as a parameter so you know which permission was being requested/granted:

@Override
public void onRequestPermissionsResult(int requestCode,
        String permissions[], int[] grantResults) {
    switch (requestCode) {
        case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
            // If request is cancelled, the result arrays are empty.
            if (grantResults.length > 0
                && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                // permission was granted, yay! Do the
                // contacts-related task you need to do.

            } else {

                // permission denied, boo! Disable the
                // functionality that depends on this permission.
            }
            return;
        }

        // other 'case' lines to check for other
        // permissions this app might request
    }
}

Since the constant is used by you only you can give it whatever value you like as a public static final int. Each permission being requested needs its own constant.

###

I went through all answers, but doesn’t satisfied my exact needed answer, so here is an example that I wrote and perfectly works, even user clicks the Don’t ask again checkbox.

  1. Create a method that will be called when you want to ask for runtime permission like readContacts() or you can also have openCamera() as shown below:

    private void readContacts() {
        if (!askContactsPermission()) {
            return;
        } else {
            queryContacts();
        } }
    

Now we need to make askContactsPermission(), you can also name it as askCameraPermission() or whatever permission you are going to ask.

    private boolean askContactsPermission() {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
        return true;
    }
    if (checkSelfPermission(READ_CONTACTS) == PackageManager.PERMISSION_GRANTED) {
        return true;
    }
    if (shouldShowRequestPermissionRationale(READ_CONTACTS)) {
        Snackbar.make(parentLayout, R.string.permission_rationale, Snackbar.LENGTH_INDEFINITE)
                .setAction(android.R.string.ok, new View.OnClickListener() {
                    @Override
                    @TargetApi(Build.VERSION_CODES.M)
                    public void onClick(View v) {
                        requestPermissions(new String[]{READ_CONTACTS}, REQUEST_READ_CONTACTS);
                    }
                }).show();
    } else if (contactPermissionNotGiven) {
        openPermissionSettingDialog();
    } else {
        requestPermissions(new String[]{READ_CONTACTS}, REQUEST_READ_CONTACTS);
        contactPermissionNotGiven = true;

    }
    return false;
}

Before writing this function make sure you have defined the below instance variable as shown:

    private View parentLayout;
    private boolean contactPermissionNotGiven;;


/**
 * Id to identity READ_CONTACTS permission request.
 */
private static final int REQUEST_READ_CONTACTS = 0;

Now final step to override the onRequestPermissionsResult method as shown below:

/**
 * Callback received when a permissions request has been completed.
 */
@RequiresApi(api = Build.VERSION_CODES.M)
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                       @NonNull int[] grantResults) {
    if (requestCode == REQUEST_READ_CONTACTS) {
        if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            queryContacts();
        }
    }
}

Here we are done with the RunTime permissions, the addon is the openPermissionSettingDialog() which simply open the Setting screen if user have permanently disable the permission by clicking Don’t ask again checkbox. below is the method:

    private void openPermissionSettingDialog() {
    String message = getString(R.string.message_permission_disabled);
    AlertDialog alertDialog =
            new AlertDialog.Builder(MainActivity.this, AlertDialog.THEME_DEVICE_DEFAULT_LIGHT)
                    .setMessage(message)
                    .setPositiveButton(getString(android.R.string.ok),
                            new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    Intent intent = new Intent();
                                    intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                                    Uri uri = Uri.fromParts("package", getPackageName(), null);
                                    intent.setData(uri);
                                    startActivity(intent);
                                    dialog.cancel();
                                }
                            }).show();
    alertDialog.setCanceledOnTouchOutside(true);
}

What we missed ?
1. Defining the used strings in strings.xml

<string name="permission_rationale">"Contacts permissions are needed to display Contacts."</string>
    <string name="message_permission_disabled">You have disabled the permissions permanently,
        To enable the permissions please go to Settings -> Permissions and enable the required Permissions,
        pressing OK you will be navigated to Settings screen</string>
  1. Initializing the parentLayout variable inside onCreate method

    parentLayout = findViewById(R.id.content);

  2. Defining the required permission in AndroidManifest.xml

<uses-permission android:name="android.permission.READ_CONTACTS" />
  1. The queryContacts method, based on your need or the runtime permission you can call your method before which the permission was needed. in my case I simply use the loader to fetch the contact as shown below:

    private void queryContacts() {
    getLoaderManager().initLoader(0, null, this);}
    

This works great happy coding 🙂

###

public class SplashActivity extends RuntimePermissionsActivity {


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_splash);

    SplashActivity.super.requestAppPermissions(new
                    String[]{android.Manifest.permission.READ_PHONE_STATE,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE,
                    Manifest.permission.READ_EXTERNAL_STORAGE}, R.string.app_name
            , 20);

}

@Override
public void onPermissionsGranted(int requestCode) {


    try {
        TelephonyManager tele = (TelephonyManager) getApplicationContext()
                .getSystemService(Context.TELEPHONY_SERVICE);

        String imei =tele.getDeviceId()
    } catch (Exception e) {
        e.printStackTrace();
    }
}

public abstract class RuntimePermissionsActivity extends AppCompatActivity {
private SparseIntArray mErrorString;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mErrorString = new SparseIntArray();
}

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    int permissionCheck = PackageManager.PERMISSION_GRANTED;
    for (int permission : grantResults) {
        permissionCheck = permissionCheck + permission;
    }
    if ((grantResults.length > 0) && permissionCheck == PackageManager.PERMISSION_GRANTED) {
        onPermissionsGranted(requestCode);
    } else {

        finish();

    }
}

public void requestAppPermissions(final String[] requestedPermissions,
                                  final int stringId, final int requestCode) {
    mErrorString.put(requestCode, stringId);
    int permissionCheck = PackageManager.PERMISSION_GRANTED;
    boolean shouldShowRequestPermissionRationale = false;
    for (String permission : requestedPermissions) {
        permissionCheck = permissionCheck + ContextCompat.checkSelfPermission(this, permission);
        shouldShowRequestPermissionRationale = shouldShowRequestPermissionRationale || ActivityCompat.shouldShowRequestPermissionRationale(this, permission);
    }
    if (permissionCheck != PackageManager.PERMISSION_GRANTED) {
        if (shouldShowRequestPermissionRationale) {
            ActivityCompat.requestPermissions(RuntimePermissionsActivity.this, requestedPermissions, requestCode);
            /*Snackbar.make(findViewById(android.R.id.content), stringId,
                    Snackbar.LENGTH_INDEFINITE).setAction("GRANT",
                    new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            ActivityCompat.requestPermissions(RuntimePermissionsActivity.this, requestedPermissions, requestCode);
                        }
                    }).show();*/
        } else {
            ActivityCompat.requestPermissions(this, requestedPermissions, requestCode);
        }
    } else {
        onPermissionsGranted(requestCode);
    }
}

public abstract void onPermissionsGranted(int requestCode);

}

Leave a Reply

Your email address will not be published. Required fields are marked *