Step 4.5: SDK 23+ Permissions Handling

Android Marshmallow and Beyond

For those of you using Marshmallow or Nougat, you may have noticed that if you try to read one of your contacts or send an SMS, your app crashes. This is because starting with 6.0.0, Android now requires apps to ask permission to do a certain action at run-time (at the time of the action) as opposed to install-time. This provides more transparency as to what the app is doing and prevents shady apps from doing anything impermissible in the background.

So, in order to be able to use SMS or read from contacts, we have to implement some more code to ask users for permission.

More about Requesting Permission at Run-Time →

Permissions

First, to request for permissions, we should give each of the requests a request code to signify which request we are processing. Add the following to the instance variables of SupFragment.java.

private static final int READ_CONTACTS = 1;
private static final int SEND_SMS = 2;

Next, we need to make a method to prompt the user for permission when we request access to each of these action. Create a new method getPermissionFromUser() in SupFragment.java.

/**
 * Prompts user for permission to perform a particular task.
 */
 
public void getPermissionFromUser(int request) {
    String permission = null;
    if (request == READ_CONTACTS) {
        permission = Manifest.permission.READ_CONTACTS;
    } else if (request == SEND_SMS) {
        permission = Manifest.permission.SEND_SMS;
    }
    if (permission != null &&
            ContextCompat.checkSelfPermission(getActivity().getApplicationContext(), permission)
                    != PackageManager.PERMISSION_GRANTED) {
        requestPermissions(new String[]{permission}, request);
    }
}

And then next add function calls to the top of the onClickListeners for mSelectFriend and mSendButton.

mSelectFriend.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        getPermissionFromUser(READ_CONTACTS);
        startActivityForResult(pickContact, REQUEST_CONTACT);
    }
});

mSendButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        getPermissionFromUser(SEND_SMS);
        if (mFriend.getPhoneNumber() != null) {
            sendSup();
        } else {
            sendCustomizedSupWithoutPhoneNumber();
        }
    }
});

Finally, we want to override the onRequestPermissionsResult() method in order to display a custom message to let the user know whether or not permission has been granted. We will do this via a toast. Add this override to SupFragment.java.

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    if (requestCode == READ_CONTACTS || requestCode == SEND_SMS) {
        String permission = null;
        if (requestCode == READ_CONTACTS) {
            permission = getString(R.string.read_contacts);
        } else if (requestCode == SEND_SMS) {
            permission = getString(R.string.send_sms);
        }
        if (permission != null) {
            String result = null;
            if (grantResults.length == 1 &&
                    grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                result = " permission granted";
            } else {
                result = " permission denied";
            }
            Toast.makeText(getActivity().getApplicationContext(),
                    permission + result, Toast.LENGTH_SHORT).show();
        }
    } else {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }
}

And that should be all there is to it! Re-compile your app and then attempt selecting a contact and sending an SMS again. It should prompt you for permission and you can either grant or deny permission to do either action.