"Normal" permissions such as INTERNET are granted at install time; "dangerous" ones (camera, location, contacts, and since Android 13 POST_NOTIFICATIONS) need the user's answer at run time. check() resolves to a boolean, request() to 'granted', 'denied' or 'never_ask_again', and requestMultiple() asks for several at once. The optional rationale appears only when Android says one is due, after a first refusal:
const result = await PermissionsAndroid.request(PERMISSIONS.POST_NOTIFICATIONS, {
title: 'New arrivals',
message: 'BookNest can tell you when books from your wish list arrive.',
buttonPositive: 'OK',
});
// ...
async function askCamera() { // CAMERA is not declared in BookNest's manifest
console.log('CAMERA ->', await PermissionsAndroid.request(PERMISSIONS.CAMERA));
}The first request showed the system dialog (middle of DrawerLayoutAndroid's figure), answered Don't allow; the second showed the rationale as an Alert, then the dialog, answered Allow. The camera request returned at once, with no dialog:
LOG API 36: POST_NOTIFICATIONS granted before=false -> denied LOG API 36: POST_NOTIFICATIONS granted before=false -> granted LOG CAMERA -> never_ask_again
That last line is a trap: a permission missing from the manifest is refused without asking, and React Native 36,878 reports it exactly as it reports a user who declined twice. If no dialog ever appears, check the manifest (Android Permissions). A real never_ask_again can only be undone in Settings, which Linking.openSettings() opens.