How can I detect screen unlock with React Native? - react-native

Does anyone know a way in which I can detect when the user has opened their phone? To my understanding android.intent.USER_PRESENT is broadcast when the device is unlocked (e.g. correct password entered), however, I do not know how to detect the broadcast in with React native. Does anyone have a solution?

Look into AppState API in FB(Face Book)
It has 3 states on FB and 5 total. I only see 3 in FB but the other 2 may or may not be suppprted.
Active - The app is running in the foreground
background - The app is running in the background. The user is either in another app or on the home screen.
inactive - This is a state that occurs when transitioning between foreground & background, and during periods of inactivity such as entering the Multitasking view or in the event of an incoming call
Check out apples Docs for more on these.
You're going to have to test what state is hit when you the phone is in the lockscreen. I can't tell you what state to use because I have never tested the api out.
as you can see from the code below the test is done in a conditional statement
if (this.state.appState.match(/inactive|background/) && nextAppState === 'active')
I'm taking facebooks example here but attach the change event listiner in componentDidMount and remove in ComponentWillUnmount and the code will run accourding to the state.
import React, {Component} from 'react'
import {AppState, Text} from 'react-native'
class AppStateExample extends Component {
state = {
appState: AppState.currentState
}
componentDidMount() {
AppState.addEventListener('change', this._handleAppStateChange);
}
componentWillUnmount() {
AppState.removeEventListener('change', this._handleAppStateChange);
}
_handleAppStateChange = (nextAppState) => {
if (this.state.appState.match(/inactive|background/) && nextAppState === 'active') {
console.log('App has come to the foreground!')
}
this.setState({appState: nextAppState});
}
render() {
return (
<Text>Current state is: {this.state.appState}</Text>
);
}
}

Related

React native UI is not getting rendered after callback from native event emitter. Even callback having state change

I want to navigate the user to another screen in react native project after native app widget click in android. I was able to catch event using native event emitter in my MainView.js and there i changed state of one of my component and it got changed but UI is not getting rendered after this state change. It is showing blank screen and there is not error on the console. Thanks in advance for any help!!
export default class MainView extends React.Component {
constructor(props) {
super(props);
this.state = {text: 'Hi, This is main screen for app widget!!!'};
}
componentDidMount() {
const eventEmitter = new NativeEventEmitter();
this.listener = eventEmitter.addListener('MyCustomEvent', (event) => {
console.log('MyCustomEvent -->', event);
console.log('MyCustomEvent ArticleId -->', event.ArticleId);
if (event.ArticleId === data.articleId) {
console.log('data ArticleId true', data.articleId);
//navigation.push('Article Details', data);
this.setState({
text: data.articleDes,
});
// setText(data.articleDes);
console.log('text -->', this.state.text);
} else {
// setText('No such article found.');
console.log('text -->', this.state.text);
}
});
}
componentWillUnmount() {
this.eventListener.remove(); //Removes the listener
}
render() {
return (
<View style={{flex: 1}}>
<Text>{this.state.text}</Text>
<Button
title="click"
onPress={() => this.props.navigation.push('Article Details', data)}
/>
</View>
);
}
}
CustomActivity source code which is launched from appwidget click. From this activity's oncreate, I'm emitting events to react-native main view.
int articleId = 0;
if (getIntent() != null) {
articleId = getIntent().getIntExtra("articleId", 0);
Log.e("articleid", "" + articleId);
}
// Put data to map
WritableMap payload = Arguments.createMap();
payload.putInt("ArticleId", articleId);
// Emitting event from java code
ReactContext context = getReactNativeHost().getReactInstanceManager().getCurrentReactContext();
if ( context != null && context.hasActiveCatalystInstance()) {
Log.e("react context", "not null");
(getReactNativeHost().getReactInstanceManager().getCurrentReactContext())
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit("MyCustomEvent", payload);
}
That is not how to use NativeEventEmitter. You need to initialise the NativeEventEmitter with the native module you are emitting events from:
import { NativeEventEmitter, NativeModules } from 'react-native';
const { myNativeModule } = NativeModules;
componentDidMount() {
...
const eventEmitter = new NativeEventEmitter(myNativeModule);
this.eventListener = eventEmitter.addListener('myEvent', (event) => {
console.log(event.eventProperty) // "someValue"
});
...
}
componentWillUnmount() {
this.eventListener.remove(); //Removes the listener
}
Read more about NativeModules here: https://reactnative.dev/docs/native-modules-android
This sound familiar with an issue I am experiencing on IOS. The code is similar, but I cannot guarantee that the underlying structure in Android works in the same way. Anyways, I am sending an event message from IOS-Native (written in swift in xCode) to React-native file using the NativeEventEmitter. After the initial render, the value just wont update, and as I understand this issue is not limited to this type of Event. After some googling I found out that everything you read from state inside that event-callback has a reference to only the first render, and will not update on future renders.
Solution; use useRef so you keep a reference to the the updated value. useRef keeps the value across renders and event-callbacks. This is not something I have found out myself, please look at https://medium.com/geographit/accessing-react-state-in-event-listeners-with-usestate-and-useref-hooks-8cceee73c559 and React useState hook event handler using initial state for, they are the one that deserves the credit.

compiled application not loading in real device

For the life of me, I can't figure it out. All it shows is spinning without end and i am confused on the order of the life cycle happening. Basically, it goes to login or home screen and it works correctly on emulator but not on real device. I am on react 16.8.6 and react-native 0.60.5 environment.
I am getting started with RN and my debugging tools are not great. But for now just used Alert to see and the logic that was supposed to redirect to login/home screen is never reached. The Alerts shown are in the following order:
BS
mount2
render
mount1
My code is below: if the token exists, load home screen. else load auth screen is what I wanted to achieve but for now the line:
this.props.navigation.navigate(!goToLogin ? 'App' : 'Auth');
is never reached and so, spins a lot. Any help?
import React, {Component} from 'react';
import {StatusBar, View, Alert} from 'react-native';
import {
getUserToken,
loggedInToAssociation,
extractToken,
} from '../shared/loggedinUser';
import {setLanguage} from '../shared/localization';
import {appOptions} from '../config';
import Spinner from '../components/Spinner';
export default class AuthLoadingScreen extends Component {
constructor() {
super();
this.state = {
languageLoaded: false
};
}
componentDidMount() {
Alert.alert("mount1","oumnt1") // shown
loggedInToAssociation()
.then(details => {
// details is an array now
setLanguage(details['language']);
this.setState({languageLoaded: true});
Alert.alert("mount2","oumnt2") // SHOWN
})
.catch(err => {
setLanguage(appOptions.defaultLanguage);
this.setState({languageLoaded: true});
Alert.alert("mount3","oumnt3")
});
}
// Fetch the token from storage then navigate to our appropriate place
_bootstrapAsync = async () => {
const userToken = await getUserToken();
Alert.alert("bs","bs") // SHOWN
const tokenInfo = extractToken(userToken, 'both');
let goToLogin = true; // force user to go to the login page
if (tokenInfo.length == 2) {
goToLogin = false;
}
Alert.alert("bs2","bs2") // NEVER SHOWN
this.props.navigation.navigate(!goToLogin ? 'App' : 'Auth');
};
// Render any loading content that you like here
render() {
if (this.state.languageLoaded){
this._bootstrapAsync().then(s=>{
console.log(s)
}).catch(e=>{
console.log(e)
})
}
return (
<View>
<Spinner />
<StatusBar barStyle="default" />
</View>
);
}
}
did you check your debug console when running on device? There might be an unhandled promise rejection. The promise didn't go through but nowhere to handle the catch (consider try-catch scenario for this context).
It might be having a problem with this method.
extractToken(userToken, 'both')

Sending push notifications using Expo in background

Expo has Notification API for sending push notifications. However, how does sending push notifications work when app is in background? Can they be triggered from server or scheduled in advance?
Sending push notifications is done from your server so you are totally free in sending them whenever you want and schedule jobs to take care of it.
However you won't be able to handle the receiving of the notification on the client if the app is backgrounded and the user has not selected the notification. According to expo documentation:
You cannot handle push notifications in the background. This is a work
in progress.
But if the app is in foreground or the user has selected the notification, then a notification will be passed to the listener in your app.
We can handle push notifications in the background.
For this we have to dismission notification if AppState is 'active' and origin === 'received'.
Here is the full code.
import React from 'react';
import { Text, View, Vibration, AppState } from 'react-native';
import { Notifications } from 'expo';
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
notification: {}
}
}
componentDidMount() {
this._notificationSubscription = Notifications.addListener(
this._handleNotification
);
}
_handleNotification = notification => {
if (AppState.currentState == 'active' && notification.origin === 'received') {
Notifications.dismissNotificationAsync(notification.notificationId);
} else {
Vibration.vibrate()
this.setState({ notification: notification });
}
};
render() {
return (
<View><Text>Push Notifications Sample</Text></View>
);
}
}

Detect system language change in react native

I am using i18next for localization. I am able to change manually using i18next changelanguage method. But I need to manually change language while change the system language. So is there any package to detect language change in react native
Use react-native-device-info
npm install --save react-native-device-info
getDeviceLocale()
Gets the device locale.
const deviceLocale = DeviceInfo.getDeviceLocale();
// iOS: "en"
// Android: "en-US"
// Windows: ?
Update:
We can AppState if the app is in the foreground or background, and notify you when the state changes.
If user moved the application to background state, the language has been changed and then open the app again.
While application coming to the foreground state we can check for the language with DeviceInfo.getDeviceLocale().
It's the good way to tell that the device's language has been changed or not.
Without moving to background, user will not change the language right?
state = {
appState: AppState.currentState
}
componentDidMount() {
AppState.addEventListener('change', this._handleAppStateChange);
}
componentWillUnmount() {
AppState.removeEventListener('change', this._handleAppStateChange);
}
_handleAppStateChange = (nextAppState) => {
if (this.state.appState.match(/inactive|background/) && nextAppState === 'active') {
console.log('App has come to the foreground!')
}
this.setState({appState: nextAppState});
}

IOS Expo Push Notifications when app is in foreground

reading from the expo docs:
For iOS, you would be wise to handle push notifications that are
received while the app is foregrounded, because otherwise the user
will never see them. Notifications that arrive while the app are
foregrounded on iOS do not show up in the system notification list. A
common solution is to just show the notification manually. For
example, if you get a message on Messenger for iOS, have the app
foregrounded, but do not have that conversation open, you will see the
notification slide down from the top of the screen with a custom
notification UI.
What I don't understand is what is the best approach for that? is there an Expo API for showing such messages? or should I create an alert component of my own? It is not really clear from the docs.
Thanks.
This answer is outdated as of February 20, 2020. Please see https://stackoverflow.com/a/60344280/2441420 for how to show iOS Notification when your application is in the Foreground
There isn't an Expo API for showing those messages. You can use any 'toast' library of your choosing and display the notification message, but that should be all your code.
For example, this is how we are doing right now:
export default class HomeScreen extends React.Component {
componentDidMount() {
this.notificationSubscription = Notifications.addListener(
(notification) => this.handlePushNotification(notification),
);
}
handlePushNotification(notification) {
const { navigation } = this.props;
PushNotificationsService.handleNotification(notification, navigation);
}
(...)
import Toast from 'react-native-root-toast';
export default class PushNotificationsService {
static handleNotification(notification, navigation) {
if (notification.data.screen && notification.origin === 'selected') {
navigation.navigate(notification.data.screen);
}
Toast.show(notification.data.message);
}
}
Toast libraries include:
react-native-root-toast
react-native-easy-toast
react-native-simple-toast
Now you can just add that in one of your app entry point. The shouldShowAlert is what you want here
import * as Notifications from 'expo-notifications';
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowAlert: true,
shouldPlaySound: false,
shouldSetBadge: false,
}),
});
App.json :
{
"expo": {
"notification": {
"iosDisplayInForeground": true
}
}
DEMO
I'm not sure exactly when this was added to Expo, but as of Expo version 36 is easily doable.
To show Expo Push Notifications on iOS when your app is in the foreground, please do the following:
import { Vibration } from "react-native";
import { Notifications } from "expo";
import * as Permissions from "expo-permissions";
import Constants from "expo-constants";
registerForPushNotificationsAsync = async () => {
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") {
alert("Failed to get push token for push notification!");
return;
}
let token = await Notifications.getExpoPushTokenAsync();
console.log("Go to https://expo.io/notifications and copy the token below to easily send yourself a notification.");
console.warn("Notifications on iOS (and I believe Android) ONLY WORK ON A PHYSICAL DEVICE, not a simulator or emulator!!!")
console.log(token);
this.setState({ expoPushToken: token });
} else {
alert("Must use physical device for Push Notifications");
}
};
componentDidMount() {
this.registerForPushNotificationsAsync();
this._notificationSubscription = Notifications.addListener(
this._handleNotification
);
}
_handleNotification = async notification => {
if (notification.remote) {
Vibration.vibrate();
const notificationId = Notifications.presentLocalNotificationAsync({
title: "Follow #technoplato",
body: "To learn yourself goodly (also follow PewDiePie)",
ios: { _displayInForeground: true } // <-- HERE'S WHERE THE MAGIC HAPPENS
});
}
};
Quick and Easy Sanity Check
1) Go here: https://expo.io/notifications
2) Copy the token that is output to the terminal when your application is run.
3) Open your application on iOS.
4) Send a notification to yourself from https://expo.io/notifications and observe that it shows up even when your app is foregrounded.
Notes
Notifications WILL NOT BE RECEIVED ON AN IOS SIMULATOR
Expo makes Notifications ridiculously easy. I honestly can't believe it.
No idea why displayInForeground is false by default and not more prominent in the documentation. I'll submit a PR for it if I can.
Code originally found at this Snack: https://snack.expo.io/#documentation/pushnotifications?platform=ios
LocalNotification.ios._displayInForeground found here: https://docs.expo.io/versions/v36.0.0/sdk/notifications/#localnotification