Ionic - How to request permission again after user rejected once? - ios

Currently, I can request permission for microphone with the following code if the user has not rejected granting permissions:
permission = await this.diagnostic.isMicrophoneAuthorized();
if (!permission) {
permission = await this.diagnostic.requestMicrophoneAuthorization()
.then(async () => {
console.log('this.diagnostic.requestMicrophoneAuthorization succeed.');
return await this.diagnostic.isMicrophoneAuthorized();
})
.catch((err) => {
console.error('this.diagnostic.requestMicrophoneAuthorization failed, error: ', err);
return false;
})
}
But once they reject the request, the requestMicrophoneAuthorization is still triggered, but no dialog pop up and the result always returns false.
Is there a way to request the authorization again with Ionic 3?

To do this, you will need to use the cordova-diagnostic-plugin to check the current microphone authorization status and request authorization again if it has been denied.
import { Diagnostic } from '#ionic-native/diagnostic/ngx';
constructor(private diagnostic: Diagnostic) {}
async requestMicrophoneAuthorization() {
// Check the current microphone authorization status
let authorizationStatus = await this.diagnostic.getMicrophoneAuthorizationStatus();
if (authorizationStatus === this.diagnostic.permissionStatus.DENIED_ALWAYS) {
// If the authorization has been denied always, we need to ask the user to grant
// microphone permission again.
try {
// Request microphone authorization
authorizationStatus = await this.diagnostic.requestMicrophoneAuthorization();
console.log('Microphone authorization status: ', authorizationStatus);
} catch (error) {
console.error('Error requesting microphone authorization: ', error);
}
}
}

Related

expo-notifications - Error encountered while updating the device push token with the server

I'm using Expo's bare-workflow for my react-native project. I'm using expo's push notification service. I keep getting the following error whenever I try to get my expo push token:
[expo-notifications] Error encountered while updating the device push token with the server: {"error":"invalid_token","error_description":"The bearer token is invalid"}
I'm running the app directly on my device so should be able to get notifications.
I'm using basically the same registerForPushNotificationsAsync() that is provided in the documentation.
import Constants from 'expo-constants';
import * as Notifications from 'expo-notifications';
import { Platform } from 'react-native';
export const registerForPushNotificationsAsync = async () => {
try {
if (Constants.isDevice) {
const experienceId = '#{username}/{slug}';
const {
status: existingStatus
} = await Notifications.getPermissionsAsync();
let finalStatus = existingStatus;
if (existingStatus !== 'granted') {
const { status } = await Notifications.requestPermissionsAsync();
finalStatus = status;
}
if (finalStatus !== 'granted') {
alert('Failed to get push token for push notification!');
return;
}
const token = (
await Notifications.getExpoPushTokenAsync({ experienceId })
).data;
console.log(' 🏷 🏷 Token :', token);
return token;
} else {
alert('Must use physical device for Push Notifications');
}
if (Platform.OS === 'android') {
Notifications.setNotificationChannelAsync('default', {
name: 'default',
importance: Notifications.AndroidImportance.MAX,
vibrationPattern: [0, 250, 250, 250],
lightColor: '#FF231F7C'
});
}
return undefined;
} catch (error) {
console.error('Error in registerForPushNotificationsAsync()', error);
return undefined;
}
};
Expo packages in package.json
"expo": "~40.0.0",
"expo-analytics": "^1.0.16",
"expo-app-loading": "^1.0.1",
"expo-font": "~8.4.0",
"expo-image-manipulator": "~8.4.0",
"expo-image-picker": "~9.2.0",
"expo-intent-launcher": "~8.4.0",
"expo-notifications": "~0.8.2",
"expo-splash-screen": "~0.8.0",
"expo-updates": "~0.4.0",
I can't see anything about setting a bearer token, so I'm unsure what it could be after or where to even set it assuming I was able to determine what bearer token it is after.
Does anyone know what might be causing the problem?
So we figured it out.
We were using fetch-token-intercept which was adding our bearer token to calls going to expo, which meant validation was failing.
We modified the function to exclude calls to expo from including our bearer token and now the token is being retrieved successfully.

React Native Ios Push Notification Not Working

I use '#react-native-firebase/messaging' module to send notifications. On Android everything works fine, Following is the error log I get when I try const fcmToken = await firebase.messaging().getToken(); on ios device.
NativeFirebaseError: [messaging/unknown] The operation couldn’t be completed. (com.firebase.iid error 1001.)
I have already implemented "react-native-permissions" to grant notification permission.
My AppDelegate.m contains :
if ([FIRApp defaultApp] == nil) {
[FIRApp configure];
}
Should I add anything else to it?
Any help or suggestion will be very helpful.
Thanks in advance
You need to check and ask messaging permission before get the fcm token in iOS
/**
* Check is notification showing permission enabled if not ask the permission.
*/
async checkFcmPermission() {
firebase
.messaging()
.hasPermission()
.then(enabled => {
if (enabled) {
// User has permissions
this.getFcmToken(); // const fcmToken = await firebase.messaging().getToken();
} else {
// User doesn't have permission
firebase
.messaging()
.requestPermission()
.then(() => {
// User has authorized
this.getFcmToken(); // const fcmToken = await firebase.messaging().getToken();
})
.catch(error => {
// User has rejected permissions
console.log(
'PERMISSION REQUEST :: notification permission rejected',
);
});
}
});
}
There are a lot of solutions to this issue. I post my answer since some StackOverflow users requested.
Here is my code
const sleep = ms => {
return new Promise(resolve => setTimeout(resolve, ms));
};
async function requestPermissionForNotification() {
try {
const permission = await requestNotifications(["alert", "badge", "sound"]);
if (permission.status === "granted") {
setupNotification();
}
} catch (e) {
console.log(e)
}
};
async function setupNotification() {
try {
await sleep(5000)
// TODO no need token for now, Will be used for future releases
const enabled = await firebase.messaging().hasPermission();
await requestNotifications(['alert', 'badge', 'sound']);
await firebase.messaging().registerForRemoteNotifications();
const token = await firebase.messaging().getToken();
firebase.messaging().onMessage(async (remoteMessage) => {
});
} catch (e) {
console.log(e)
}
}
And Also I have added following to Info.plist
<key>UIBackgroundModes</key>
<array>
<string>remote-notification</string>
</array>
To sum up, I use react permissions to grant permission and the sleep method is to wait for firebase to be ready. Finally uninstalled app couple time to get a result.
When It started working, I couldn't dare to touch the code since I spent days on this.

Calling Graphi API from Classic JavaScript through msal.js

I am trying to do silent login through msal.js and then trying to call graph api but always I get 403 error. When I decrypt my access token through jwt.ms I can see that audience is correct but scopes are showing wrong. Hope some can help me.
My code
let config = {
auth: {
clientId: _spPageContextInfo.spfx3rdPartyServicePrincipalId,
authority: `https://login.microsoftonline.com/${_spPageContextInfo.aadTenantId}`,
redirectUri: 'https://xxx.sharepoint.com/sites/xxx-Dev/Pages/myportal.aspx',
validateAuthority: false,
postLogoutRedirectUri: window.origin,
},
cache: {
cacheLocation: 'localStorage',
storeAuthStateInCookie: true
}
}
let myMSALObj = new Msal.UserAgentApplication(config)
let graphConfig = {
graphGroupEndpoint: "https://graph.microsoft.com/v1.0/groups"
}
let request = {
scopes: ["https://graph.microsoft.com/.default"]
}
myMSALObj.handleRedirectCallback(response => { console.log(response) });
//const idTokenScope = { scopes: [_spPageContextInfo.spfx3rdPartyServicePrincipalId] }
const handleError = (error) => {
if (error.errorCode === 'consent_required'
|| error.errorCode === 'interaction_required'
|| error.errorCode === 'login_required') {
//myMSALObj.loginRedirect(idTokenScope);
myMSALObj.loginRedirect(request);
return;
}
throw error;
};
const getToken = () => {
const date = new Date();
const user = myMSALObj.getAccount();
if (!user) {
//myMSALObj.loginRedirect(idTokenScope);
myMSALObj.loginRedirect(request);
return;
}
//myMSALObj.acquireTokenSilent(idTokenScope).then(response => {
myMSALObj.acquireTokenSilent(request).then(response => {
console.log(`${date.toLocaleTimeString()}`, response.accessToken);
callMSGraph(graphConfig.graphGroupEndpoint, response.accessToken, graphAPICallback)
}).catch(handleError);
}
getToken()
function callMSGraph(theUrl, accessToken, callback) {
var xmlHttp = new XMLHttpRequest()
xmlHttp.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200)
callback(JSON.parse(this.responseText))
}
xmlHttp.open("GET", theUrl, true)
xmlHttp.setRequestHeader('Authorization', 'Bearer ' + accessToken)
xmlHttp.send()
}
function graphAPICallback(data) {
document.write(JSON.stringify(data, null, 2))
}
My decoded token
My app permission
There are two kinds of permissions: one is application permission, the othre one is delegated permission. And "https://graph.microsoft.com/.default" is for application permisisons.
With interactively signning from browser, you will be asked to provided with your credentials. In this way, you will get an access token for yourself, which is with delegated permissions. We call this as OAuth 2.0 authorization code flow
However, as you do not set any required delegated permission in your request scope, Azure AD just return you an access token with basic delegated permissions (openid, email an profile).
By the way, if you just want to get an access token with application permissions. You just need to use the OAuth 2.0 client credentials flow to get a token.

Notifications.getExpoPushTokenAsync not working on Stand Alone iOS

I'm trying to my expo/react-native project to send push notifications to my server. It works on standalone Android, but not stand alone iPhone.
The standalone iPhone app never sends the token.
Since the app sends nothing without error, I tried removing:
if (finalStatus !== 'granted') { return; }
This didn't work either.
export async function registerForPushNotificationsAsync(token) {
const { status: existingStatus } = await Permissions.getAsync(
Permissions.NOTIFICATIONS
);
let finalStatus = existingStatus;
// Only ask if permissions have not already been determined, for iOS.
if (existingStatus !== 'granted') {
const { status } = await Permissions.askAsync(Permissions.NOTIFICATIONS);
finalStatus = status;
}
// Stop here if the user did not grant permissions
if (finalStatus !== 'granted') {
return;
}
// Get the push token that uniquely identifies this device
let expoToken = await Notifications.getExpoPushTokenAsync();
// Post new push token to backend for user
return axios({
method: 'POST',
url: `${str.ROOT_URL}/account/push/`,
headers: {
Authorization: `Token ${token}`
},
data: {
"token": expoToken,
"status": finalStatus
}
});
}
I expected the token to get sent to the backend, but nothing is sent on the standalone iOS app.
Please let me know if you know a workaround or had this issue before. Thanks!
I think it's too late to give an answer, but I spent 2 days to resolve it... I hope it helps somebody.
Instead this:
import * as Notifications from "expo-notifications";
Try this:
import { Notifications } from "expo";

Determining if geolocation enabled with react native

Building an app with RN I use the following to receive user's location :
navigator.geolocation.getCurrentPosition(
(position) => {
//do stuff with location
},
(error) => {
//error or locaiton not allowed
},
{enableHighAccuracy: true, timeout: 20000, maximumAge: 1000}
);
Which immediately invokes the inquiry for user to accept geolocation permissions. What I'm trying to achieve now is to have an explicit screen before prompting user to click on a button to accept those permissions. In order to get there, I need some way to check if he has accepted those permissions before or not. Caching it in storage would be a bad solution - it might go out of sync if user changes permissions in settings.
How can I check if user has accepted geolocation permissions without triggering the permission alert?
You can use this library https://github.com/yonahforst/react-native-permissions to check if user has accepted location permission.
Like this:
Permissions.getPermissionStatus('location')
.then(response => {
this.setState({ locationPermission: response })
})
In v2.x.x of react-native-permissions (https://github.com/react-native-community/react-native-permissions)
import { Platform } from "react-native";
import { PERMISSIONS, request } from "react-native-permissions";
import * as Geolocation from "#react-native-community/geolocation";
try {
request(
Platform.select({
android: PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION,
ios: PERMISSIONS.IOS.LOCATION_WHEN_IN_USE
})
).then(res => {
if (res == "granted") {
Geolocation.getCurrentPosition( // do location staffs);
Geolocation.watchPosition( // do watch location staffs );
} else {
// console.log("Location is not enabled");
}
});
} catch (error) {
console.log("location set error:", error);
}
Check this library, this would help you to check location is enabled or not.
https://www.npmjs.com/package/react-native-device-info#isLocationEnabled
Note : Above will not check permission enabled on not. It will just check location truned on or not

Resources