Im trying to send push notification to an android device using the expo push notification tool, (It is working for iOS) but it doesn't work, I have added a firebase project with a separate android app, the google-service.json, and ran the expo push:android:upload --api-key . Is there something missing ?
Im using a managed workflow
SDK 39
This is the code which I have used and it works perfectly for iOS
getting the expo push notification code
async function registerForPushNotificationsAsync() {
let token;
if (Constants.isDevice) {
const { status: existingStatus } = await Permissions.getAsync(Permissions.NOTIFICATIONS);
let finalStatus = existingStatus;
if (existingStatus !== 'granted') {
const { status } = await Permissions.askAsync(Permissions.NOTIFICATIONS);
finalStatus = status;
}
if (finalStatus !== 'granted') {
return;
}
token = (await Notifications.getExpoPushTokenAsync()).data;
let uid;
firebase.auth().signInAnonymously()
.then(() => {
uid = firebase.auth().currentUser.uid;
})
.then(() => {
db.collection('users').doc(uid).get()
.then(function(doc) {
if (doc.exists) {
db.collection('users').doc(uid).update({
expoPushToken: token,
'last use date': new Date().toISOString().slice(0,10),
'region': Localization.region
})
} else {
db.collection('users').doc(uid).set({
expoPushToken: token,
'last use date': new Date().toISOString().slice(0,10),
'region': Localization.region
})
}
})
})
.catch(() => console.log('ERROR'))
} 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 token;
}
app.json
"android": {
"useNextNotificationsApi": true,
"googleServicesFile": "./google-services.json",
"adaptiveIcon": {
"foregroundImage": "./assets/adaptive-icon.png",
"backgroundColor": "#FFFFFF"
},
"package": "com.NAME.NAME",
"versionCode": 3
},
Related
react native pdf file download after i have not open not for android
working for ios iPhone 13 device IOS 15.5
android Pixels 3a android 11v
i think i have added everything needed in android manifest,
It works for ios devices but not for android. after i download the file i can't open it i.e. there is a download error
my code:
import { Alert, PermissionsAndroid, Platform } from 'react-native';
import RNFetchBlob from 'rn-fetch-blob';
interface IDownloadOptions {
src: string;
fileName: string;
token: string | null;
setLoading?: (loading: boolean) => void;
}
// commint
class DownloadManager {
public download = async (options: IDownloadOptions): Promise<any> => {
const { token, src, fileName, setLoading } = options;
const { dirs } = RNFetchBlob.fs;
const dirToSave = Platform.OS === 'ios' ? dirs.DocumentDir : dirs.DownloadDir;
console.log('dirToSave', dirToSave);
if (Platform.OS === 'android') {
const permission = await PermissionsAndroid.request('android.permission.WRITE_EXTERNAL_STORAGE');
if (permission !== 'granted') {
console.log('not granted');
setLoading && setLoading(false);
return;
}
}
const ios = RNFetchBlob.ios;
const android = RNFetchBlob.android;
const configfb = {
fileCache: true,
useDownloadManager: true,
notification: true,
mediaScannable: true,
title: fileName,
path: `${dirToSave}/${fileName}`,
};
console.log('config', configfb);
return new Promise((resolve, reject) => {
requestAnimationFrame(() => {
RNFetchBlob.config({
fileCache: configfb.fileCache,
// #ts-ignore
title: configfb.title,
path: configfb.path,
// #ts-ignore
appendExt: 'pdf',
addAndroidDownloads: {
...configfb,
},
})
.fetch('GET', src, {
Authorization: `Bearer ${token}`,
})
.then(res => {
const status = res.info().status;
console.log('status: ' + status);
console.log('res: ' + res);
if (status === 200) {
if (Platform.OS === 'ios') {
ios.openDocument(res.data);
}
if (Platform.OS === 'android') {
android.actionViewIntent(res.path(), 'pdf');
}
}
if (status === 500) {
Alert.alert('Server error 500');
}
setLoading && setLoading(false);
resolve(res);
})
.catch(error => {
console.log('error', error);
setLoading && setLoading(false);
reject(error);
});
});
});
};
}
Problem started when i realize that Permissions.askAsync not working as expected.
I find Permissions.askAsync not working as expected and it`s cool solution for ios, but i need it for android! So, i add some extra code for this:
Alert.alert(
'No Notification Permission',
'please go to settings and enable notifications permissions manually',
[
{ text: 'cancel', onPress: () => console.log('cancel') },
{
text: 'Allow',
onPress: async () => {
if (Platform.OS === 'android') {
await IntentLauncher.startActivityAsync(
IntentLauncher.ACTION_APP_NOTIFICATION_SETTINGS,
{
data: `package:${Application.applicationId}`,
}
);
}
if (Platform.OS === 'ios') {
Linking.openURL('app-settings:');
}
},
},
],
{ cancelable: false },
);
UPD. construction below works great, but i want to access directly to the APP_NOTIFICATION_SETTINGS.
onPress={() => {
IntentLauncher.startActivityAsync(
IntentLauncher.ACTION_APPLICATION_DETAILS_SETTINGS,
{
data: `package:${Application.applicationId}`,
}
);
}}
Related issue in expo forums https://forums.expo.io/t/opening-device-settings-on-android-using-linking/2059/14
I try to access to the APP_NOTIFICATION_SETTINGS but for some reason i'm getting error like "The app wasn't found in the list of installed apps". Tried it on published project and on standalone (apk) and got the same result. Anyone knows what is the problem?
I realize this is a bit late, but the suggested answer didn't work for me. This is what worked on my device using Android Version 29:
const pkg = Constants.manifest.releaseChannel
? Constants.manifest.android.package
: 'host.exp.exponent';
IntentLauncher.startActivityAsync(
IntentLauncher.ACTION_APP_NOTIFICATION_SETTINGS,
{
extra: { 'android.provider.extra.APP_PACKAGE': pkg }
},
);
TL;DR: The key change here being android.provider.extra.APP_PACKAGE as the extra key name.
Solution:
const pkg = Constants.manifest.releaseChannel
? Constants.manifest.android.package // When published, considered as using standalone build
: 'host.exp.exponent'; // In expo client mode
onPress: () => {
if (Platform.OS === 'android') {
if (Platform.Version >= 26) {
IntentLauncher.startActivityAsync(
IntentLauncher.ACTION_APP_NOTIFICATION_SETTINGS,
{
data: `package:${pkg}`,
},
);
} else {
IntentLauncher.startActivityAsync(
IntentLauncher.ACTION_APPLICATION_DETAILS_SETTINGS,
{
data: `package:${pkg}`,
},
);
}
}
if (Platform.OS === 'ios') {
Linking.openURL('app-settings:');
}
},
Solution description: https://forums.expo.io/t/api-to-open-app-settings/5290/18
"expo": "^39.0.0" / Library - expo-notifications.
Since moving to the new API, I have been facing various problems. I fixed problems with the icon, with receiving a notification in apk, with handlers, and now I could not find an answer to this question.
I have 3 phones Samsung Galaxy S9+ with Android 10, Xiaomi Redmi 4x with Android 7.12 N2G47H MIUI 10 Global 9.6.27 and Xiaomi Redmi Note 4 Global Android 7.0 nrd90m MIUI global 11.0.2.
Samsung Galaxy S9+. With my current config i receive notification
excellent as it should be:
Vibration +
Sound +
Popup when notification comes +
All this come from the box, i do not need to ask permissions or something like that.
Xiaomi Redmi 4x and Xiaomi Redmi Note 4.
With my current config i have problems:
Vibration - do not work both in both published and apk(standalone) versions
Sound +
Popup when notification comes +
AND THE MAIN PROBLEM: All this DO NOT come from the box, I need to manually give permission for notifications to pop up, sound, etc.
My app.json :
{
"expo": {
"name": "qwert",
"slug": "qwert",
"version": "1.1.2",
"orientation": "portrait",
"icon": "./src/assets/images/logo1024.png",
"scheme": "myapp",
"userInterfaceStyle": "automatic",
"privacy": "unlisted",
"splash": {
"image": "./src/assets/images/splashScreen.png",
"resizeMode": "contain",
"backgroundColor": "#f1f0f0"
},
"android": {
"package": "com.qwert.app",
"googleServicesFile": "./google-services.json",
"versionCode": 1,
"useNextNotificationsApi": true
},
"notification": {
"icon": "./src/assets/images/VIAicon96.png",
"color": "#8edcd5",
"androidMode": "collapse"
},
"updates": {
"fallbackToCacheTimeout": 0
},
"assetBundlePatterns": [
"**/*"
],
"ios": {
"supportsTablet": true
},
"web": {
"favicon": "./assets/images/favicon.png"
},
"description": ""
}
}
My permissions/configs:
import Constants from 'expo-constants';
import * as Notifications from 'expo-notifications';
import * as Permissions from 'expo-permissions';
import { Platform } from 'react-native';
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowAlert: true,
shouldPlaySound: true,
shouldSetBadge: true,
}),
});
export const registerForPushNotificationsAsync = async () => {
let token;
if (Constants.isDevice) {
const { status: existingStatus } = await Permissions.getAsync(
Permissions.NOTIFICATIONS,
);
let finalStatus = existingStatus;
if (existingStatus !== 'granted') {
const { status } = await Permissions.askAsync(Permissions.NOTIFICATIONS);
finalStatus = status;
}
if (finalStatus !== 'granted') {
console.log('Failed to get push token for push notification!');
return;
}
token = (await Notifications.getExpoPushTokenAsync()).data;
console.log(token);
} else {
console.log('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',
});
}
// eslint-disable-next-line consistent-return
return token;
};
Also, place where use all this:
useEffect(() => {
const handleNotification = async () => {
await getPermitsAndIntegrateIntoApp(store); // works great
};
const setPersistDataToStore = async () => {
const EXPO_PUSH_TOKEN = await registerForPushNotificationsAsync();
const qwertyyy = await getConfig(Constants.deviceId, EXPO_PUSH_TOKEN);
store.setQwerrt(config.destinations);
};
setPersistDataToStore();
Notifications.addNotificationReceivedListener(handleNotification);
return () => Notifications.removeAllNotificationListeners();
}, [store]);
On all 3 phones I have a logic for handling notifications ( send request, updates store, and updates screen on my app). It works correct.
How to automatically configure the permission to show push notifications? How to enable vibration on Xiaomi?
Finally i found an answers.
https://github.com/expo/expo/issues/8556 tells us that
And that
Permissions.askAsync not working as expected tells us how to let user set permissions to notifications.
And i still do not know why Xiaomi do not vibrate......
I have Xiaomi Redmi 6A, and with default configuration from examples in Expo documentation I received only vibration on notification. I have to manually turn on pop up (floating) and sound for default channel. Only after that notifications began to work as expected.
I have a react native app and I'm trying to implement push notification with the react-native-push-notification package and firebase cloud messaging. It turns out that when I get a notification, the app just stops working. The RemotePushController function seems to work, as soon as the App starts I get the console log with the token. I've tried several things but nothing works.
[]
[
I was able to solve the problem by changing the versions of the googlePlayServicesVersion and firebaseMessagingVersion at android/build.gradle as shown in the image below. I set both to "+".
For Android, no need to set anything in AndroidManifest.xml.
And in the top level build.gradle having the following configuration should be sufficient. Notice the "+" against the versions.
buildscript {
ext {
buildToolsVersion = "28.0.3"
minSdkVersion = 16
compileSdkVersion = 28
targetSdkVersion = 28
googlePlayServicesVersion = "16.+"
androidMapsUtilsVersion = "0.5+"
firebaseVersion = "+"
firebaseMessagingVersion = "+"
}
Here's a hook that I have created for FCM.
import React, { useEffect } from 'react'
import { Alert } from 'react-native'
import messaging from '#react-native-firebase/messaging'
import { useNavigation } from '#react-navigation/native'
import { useAsyncStorage } from '#react-native-community/async-storage'
const useFirebaseCloudMessaging = () => {
const navigation = useNavigation()
const { getItem: getFcmToken, setItem: saveFcmToken } = useAsyncStorage('fcmToken')
const [fcmToken, setFcmToken] = React.useState(null)
const [initialRoute, setInitialRoute] = React.useState('Home')
const getToken = async () => {
const token = await getFcmToken()
if (!token) {
// Get the device token
messaging()
.getToken()
.then(token => {
setFcmToken(token)
saveFcmToken(token)
})
}
}
const requestUserPermission = async () => {
const authStatus = await messaging().requestPermission()
const enabled =
authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
authStatus === messaging.AuthorizationStatus.PROVISIONAL
if (enabled) {
console.log('Authorization status:', authStatus)
}
}
useEffect(() => {
// If using other push notification providers (ie Amazon SNS, etc)
// you may need to get the APNs token instead for iOS:
// if(Platform.OS == 'ios') { messaging().getAPNSToken().then(token => { return saveTokenToDatabase(token); }); }
// Listen to whether the token changes
return messaging().onTokenRefresh(token => {
saveFcmToken(token)
})
}, [])
useEffect(() => {
const unsubscribe = messaging().onMessage(async remoteMessage => {
Alert.alert('A new FCM message arrived!', JSON.stringify(remoteMessage))
})
return unsubscribe
}, [])
useEffect(() => {
// Assume a message-notification contains a "type" property in the data payload of the screen to open
messaging().onNotificationOpenedApp(remoteMessage => {
console.log(
'Notification caused app to open from background state:',
remoteMessage.notification
)
navigation.navigate(remoteMessage.data.type)
})
// Check whether an initial notification is available
messaging()
.getInitialNotification()
.then(remoteMessage => {
if (remoteMessage) {
console.log(
'Notification caused app to open from quit state:',
remoteMessage.notification
)
setInitialRoute(remoteMessage.data.type) // e.g. "Settings"
}
})
}, [])
return {
fcmToken,
getToken,
requestUserPermission
}
}
export default useFirebaseCloudMessaging
Use the hook in a top level App component
import React, { useEffect } from 'react'
import Root from './App'
import useFirebaseCloudMessaging from './FirebaseCloudMessaging'
const App = () => {
const {
getToken,
requestUserPermission
} = useFirebaseCloudMessaging()
useEffect(() => {
requestUserPermission()
getToken()
}, [])
return <Root />
}
export default App
I have integrated facebook login and it is working fine on all versions above kitkat.
In kitkat there is a strange behavior when tapped on Facebook login button
it takes the app to background while loading.
Code I am using for login :
const doFacebookLogin = async dispatch => {
const { type, token } = await LoginManager.logInWithReadPermissions(["public_profile", "email", "pages_show_list"])
.then(
(result) => {
if (result.isCancelled) {
return dispatch({ type: FACEBOOK_LOGIN_CANCEL });
} else {
AccessToken.getCurrentAccessToken()
.then(async (data) => {
await setValue('fb_token', data.accessToken);
let facebookApiData = await initUser(data.accessToken);
await setValue('facebook_data', JSON.stringify(facebookApiData));
dispatch({ type: FACEBOOK_LOGIN_PROCRESSING});
// let response = await api.post('api/account/AddUser',JSON.parse('{"BrowserName":"MobileApp","BrowserVersion":"46", "Email":"'+ facebookApiData.email +'","FacebookID":"'+facebookApiData.id+'","FirstName":"'+facebookApiData.first_name+'","MiddleName":"'+facebookApiData.middle_name+'", "FullName": "'+facebookApiData.name+'", "Gender":"'+facebookApiData.gender+'","LastName":"'+facebookApiData.last_name+'", "ProfileLink":"'+facebookApiData.link+'","image":"'+facebookApiData.picture.data.url+'","Location":null}}'));
// await setValue('LoginId', response.LoginId);
return dispatch({ type: FACEBOOK_LOGIN_SUCCESS, payload: data.accessToken });
});
}
},
(error) => {
Alert.alert('Sign in error', error);
},
);
if (type === "cancel") {
return dispatch({ type: FACEBOOK_LOGIN_FAIL });
}
await setValue("fb_token", token);
return dispatch({ type: FACEBOOK_LOGIN_SUCCESS, payload: token });
};
Screenshot is attached
Dependencies I am using
"dependencies": {
"react": "16.4.0",
"react-native": "0.55.4",
"react-native-fbsdk": "^0.7"
},