I am using plugin
https://github.com/seididieci/capacitor-backround-geolocation
to watch the user's location. then I am tracking that location with the help of a pusher. this background location works only for 5 minutes after that it just stops. I am using Capacitor's Background task. But that plugin also keeps data on phone after the user opens the app. the Background task sends data to the pusher.
Here is watch location function
getLocation: async function () {
BackgroundGeolocation.initialize({
notificationText: "Your app is running, tap to open.",
notificationTitle: "App Running",
updateInterval: 10000,
requestedAccuracy: BgGeolocationAccuracy.HIGH_ACCURACY,
// Small icon has to be in 'drawable' resources of your app
// if you does not provide one (or it is not found) a fallback icon will be used.
smallIcon: "ic_small_icon",
// Start getting location updates right away. You can set this to false or not set at all (se below).
startImmediately: true,
});
// const geolocation = new Geolocation.Geolocation();
BackgroundGeolocation.addListener("onLocation", (location) => {
// console.log("Got new location", location);
this.subscribe(location.longitude, location.latitude);
console.log(location)
});
BackgroundGeolocation.addListener("onPermissions", (location) => {
// console.log("BGLocation permissions:", location);
this.subscribe(location.longitude, location.latitude);
// Do something with data
});
BackgroundGeolocation.start();
},
Then calling function in mounted()
mounted(){
this.getLocation
App.addListener("appStateChange", (state) => {
setInterval(this.getLocation, 120000);
if (!state.isActive) {
BackgroundTask.beforeExit(async () => {
setInterval(this.getLocation, 120000);
console.og('Why')
});
}
if (state.isActive) {
setInterval(this.getLocation, 120000);
console.log('Active')
}
});
}
You need to use https://ionicframework.com/docs/native/foreground-service like this for running the background.
Related
I am writing an app with Expo that uses expo-location to track the location of a user in the background. I would like to use hooks (states, useEffect...) when my app is in the background. At the moment the background tracking code looks like that
export default function BackgroundLocationHook() {
[...]
const [position, setPosition] = useState(null);
const [newLocation, setNewLocation] = useState(null) ;
TaskManager.defineTask(LOCATION_TASK_NAME, async ({ data, error }) => {
if (error) {
console.error(error);
return;
}
if (data) {
// Extract location coordinates from data
const { locations } = data;
const location = locations[0];
if (location) {
console.log("Location in background", location.coords);
}
}
setPosition(location.coords);
});
[...]
return [position];
}
But it is a bit hacky as the geolocation_tracking task shares some states with the
I would also like to play some sounds when I am close to a some location even when my app is in the background. I plan to do it with useEffect like that:
useEffect(() => {
const requestPermissions = async () => {
if(shouldPlaySound(newLocation)){
playSound()
}
};
requestPermissions();
}, [newLocation]);
This works when my app is in the foreground but I heard that react hooks such as states, and useEffect do not work when the app is in the background. So my question is what is the alternative to make sure I still have a sound being played when my app is in the background and if it is possible to have hooks working even when the app is in the background.
I see you want to perform some task in the background when you pass a specific location,
With the expo location, we can achieve this implementation.
You can add fencing to your desired location and when the device will enter the fencing area or exits from the fencing area you will get an event to handle some tasks and you are also able to listen to the event in the background with the Expo Task manager.
You need to follow the steps to achieve this.
Define a task using Expo Task Manager outside the react life cycle,
and read the official documentation for API usage. Expo Task Manager
Take the necessary permissions to access the location in the background, and start geofencing with your component. Expo Location
Stop the fencing listener using stopGeofencingAsync from expo-location when it is not needed anymore.
Now you will get events every time you enter or exit from the specified location in startGeofencingAsync until you stop using the stopGeofencingAsync method.
Hope this will help you achieve your desired input.
to run a task in the background you can check any of these library.
react-native-background-actions
react-native-background-timer
this is some example code
import BackgroundTimer from 'react-native-background-timer';
// Start a timer that runs continuous after X milliseconds
const intervalId = BackgroundTimer.setInterval(() => {
// this will be executed every 200 ms
// even when app is the background
console.log('tic');
}, 200);
// Cancel the timer when you are done with it
BackgroundTimer.clearInterval(intervalId);
// Start a timer that runs once after X milliseconds
const timeoutId = BackgroundTimer.setTimeout(() => {
// this will be executed once after 10 seconds
// even when app is the background
console.log('tac');
}, 10000);
// Cancel the timeout if necessary
BackgroundTimer.clearTimeout(timeoutId);
this is another example of this code
import BackgroundService from 'react-native-background-actions';
const sleep = (time) => new Promise((resolve) => setTimeout(() => resolve(), time));
// You can do anything in your task such as network requests, timers and so on,
// as long as it doesn't touch UI. Once your task completes (i.e. the promise is resolved),
// React Native will go into "paused" mode (unless there are other tasks running,
// or there is a foreground app).
const veryIntensiveTask = async (taskDataArguments) => {
// Example of an infinite loop task
const { delay } = taskDataArguments;
await new Promise( async (resolve) => {
for (let i = 0; BackgroundService.isRunning(); i++) {
console.log(i);
await sleep(delay);
}
});
};
const options = {
taskName: 'Example',
taskTitle: 'ExampleTask title',
taskDesc: 'ExampleTask description',
taskIcon: {
name: 'ic_launcher',
type: 'mipmap',
},
color: '#ff00ff',
linkingURI: 'yourSchemeHere://chat/jane', // See Deep Linking for more info
parameters: {
delay: 1000,
},
};
await BackgroundService.start(veryIntensiveTask, options);
await BackgroundService.updateNotification({taskDesc: 'New ExampleTask description'}); // Only Android, iOS will ignore this call
// iOS will also run everything here in the background until .stop() is called
await BackgroundService.stop();
A third solution for android is the headlessjs that only works on android
you can tak help from this
I'm a beginner at React Native.
I am trying to access a native(built-in) camera app on Android device.
I used React-Native-Image-Picker to open the camera app but I would like to record a video somehow automatically(?) I mean not using my finger.
I need codes that make it to record and stop the video.
(I don't mean to give me a code rather, please advise if it is even possible?)
Any help would be very appreciated.
Thank you!
It is possible.
Package: https://github.com/mrousavy/react-native-vision-camera
Review the API and Guide section to see how to start and stop recording programmatically.
They also show an example app that demonstrates different types of capture including video recording, ref: https://github.com/mrousavy/react-native-vision-camera/blob/28fc6a68a5744efc85b532a338e2ab1bc8fa45fe/example/src/views/CaptureButton.tsx
...
const onStoppedRecording = useCallback(() => {
isRecording.current = false;
cancelAnimation(recordingProgress);
console.log('stopped recording video!');
}, [recordingProgress]);
const stopRecording = useCallback(async () => {
try {
if (camera.current == null) throw new Error('Camera ref is null!');
console.log('calling stopRecording()...');
await camera.current.stopRecording();
console.log('called stopRecording()!');
} catch (e) {
console.error('failed to stop recording!', e);
}
}, [camera]);
const startRecording = useCallback(() => {
try {
if (camera.current == null) throw new Error('Camera ref is null!');
console.log('calling startRecording()...');
camera.current.startRecording({
flash: flash,
onRecordingError: (error) => {
console.error('Recording failed!', error);
onStoppedRecording();
},
onRecordingFinished: (video) => {
console.log(`Recording successfully finished! ${video.path}`);
onMediaCaptured(video, 'video');
onStoppedRecording();
},
});
// TODO: wait until startRecording returns to actually find out if the recording has successfully started
console.log('called startRecording()!');
isRecording.current = true;
} catch (e) {
console.error('failed to start recording!', e, 'camera');
}
}, [camera, flash, onMediaCaptured, onStoppedRecording]);
//#endregion
...
I have an Android app that its built with React Native + Expo. My Expo is currently in version 39.0.0.
The user starts a location service by clicking in a button and when users changes position the app with its new position execute some rules based on location and show some notifications depending of user position.
Everything works great when the app is in foreground, in every tested devices. But, when the app goes to background it stops to work in some devices.
For example, in Xiaomi devices the app works great but in Samsung don't. =[
Can anyone help me?
Here`s part of my "app.json":
"android": {
"package": "br.i.dont.know.whats.going.on",
"versionCode": 86,
"permissions": [
"ACCESS_COARSE_LOCATION",
"ACCESS_FINE_LOCATION",
"ACCESS_BACKGROUND_LOCATION",
"CAMERA",
"READ_EXTERNAL_STORAGE",
"WRITE_EXTERNAL_STORAGE",
"NOTIFICATIONS",
"USER_FACING_NOTIFICATIONS",
"FOREGROUND_SERVICE"
],
This is the location task registered when app starts:
export async function registerFetchTask() {
TaskManager.defineTask(LOCATION_TASK, async ({ data: { locations }, error }) => {
if (error) {
console.error(error);
return;
}
const [location] = locations;
try {
if (location) {
AppState.currentState === "background" && updateChart(location);
}
} catch (err) {
console.error("Error while getting user location: ", err);
}
});
const status = await BackgroundFetch.getStatusAsync();
switch (status) {
case BackgroundFetch.Status.Restricted:
case BackgroundFetch.Status.Denied:
console.log("Background execution is disabled");
return;
default: {
console.debug("Background execution allowed");
let tasks = await TaskManager.getRegisteredTasksAsync();
if (tasks.find(f => f.taskName === LOCATION_TASK) == null) {
console.log("Registering task");
await BackgroundFetch.registerTaskAsync(LOCATION_TASK);
tasks = await TaskManager.getRegisteredTasksAsync();
console.debug("Registered tasks", tasks);
} else {
console.log(`Task ${LOCATION_TASK} already registered, skipping`);
}
console.log("Setting interval to", 5);
await BackgroundFetch.setMinimumIntervalAsync(5);
}
}
}
Task location started when user press the button
await Location.startLocationUpdatesAsync(LOCATION_TASK, {
accuracy: Location.Accuracy.Balanced,
timeInterval: 5000,
distanceInterval: 1, // minimum change (in meters) betweens updates
deferredUpdatesTimeout: 5000,
deferredUpdatesInterval: 5000, // minimum interval (in milliseconds) between updates
// foregroundService is how you get the task to be updated as often as would be if the app was open
foregroundService: {
notificationTitle: 'App is using your location',
notificationBody: 'To turn off, go back to the app.',
},
});
I am implementing FCM notifications in an Ionic React application. I am having trouble navigating to another page to display the notification details.
I have created a FCMService class in my react App, and initialising this in the index.ts file.
// FCMService.ts
export default class FCMService {
public static Instance: FCMService;
private _store: Store<IAppState>;
constructor(store: Store<IAppState>) {
this._store = store;
}
public static Initalise(store: Store<IAppState>) {
if (!FCMService.Instance) {
FCMService.Instance = new FCMService(store);
FCMService.Instance.InitaliseFCM();
FCMService.Instance._store.subscribe(() => { console.log(store.getState()) });
} else {
console.debug("FCM service already intialised. Please use FCMService.Instance");
}
}
private InitaliseFCM() {
// Request permission to use push notifications
// iOS will prompt user and return if they granted permission or not
// Android will just grant without prompting
PushNotifications.requestPermission().then(result => {
console.log(result);
if (result.granted) {
// Register with Apple / Google to receive push via APNS/FCM
PushNotifications.register();
} else {
// Show some error
}
});
// On success, we should be able to receive notifications
PushNotifications.addListener('registration', (token: PushNotificationToken) => {
console.log(token);
localStorage.setItem("FCM_TOKEN", token.value);
}
);
// Some issue with our setup and push will not work
PushNotifications.addListener('registrationError',
(error: any) => {
console.log(error);
}
);
// Show us the notification payload if the app is open on our device
PushNotifications.addListener('pushNotificationReceived',
(notification: PushNotification) => {
console.log(notification);
let data = notification.notification.data as INotificationData;
}
);
// Method called when tapping on a notification
PushNotifications.addListener('pushNotificationActionPerformed',
(notification: PushNotificationActionPerformed) => {
console.log(notification);
let data = notification.notification.data as INotificationData;
this._store.dispatch(setNotificationActionCreator(data));
}
);
}
}
and then the index.ts
const store = configureStore();
interface MainProps {
store: Store<IAppState>;
}
FCMService.Initalise(store);
ReactDOM.render(<Provider store={store}><App /> </Provider>, document.getElementById('root'));
serviceWorker.unregister();
I even tried using the Redux store to save the notification on Tap - and then that would publish the notification change event (which might of worked - if I could access the useHistory() hook in the App.tsx file)
This was my attempt at navigating via Redux store in App.tsx
const App: React.FC<IProps> = ({ getCompanies, getUser, notification }) => {
console.log('app');
console.log(process.env);
const history = useHistory();
if(notification){
history.push(`/page/plot-position/{notification.id}`);
}
return (
<IonApp>
<IonReactRouter>
<IonSplitPane contentId="main" when="false">
<Menu />
<IonRouterOutlet id="main">
<Route path="/login" component={LoginPage} exact />
<PrivateRoute path="/page/plot-position/:notificationId/" component={PlotPositionPage} exact />
<Redirect from="/" to="/login" exact />
</IonRouterOutlet>
</IonSplitPane>
</IonReactRouter>
</IonApp>
);
};
const mapStateToProps = (store: IAppState) => {
return {
user: store.user.user as UserDTO,
notification: store.notificationState.notification
};
};
const mapDispatchToProps = (dispatch: any) => {
return {
getCompanies: () => dispatch(getCompaniesStartActionCreator()),
getUser: () => dispatch(getUserStartActionCreator())
}
};
export default connect(mapStateToProps, mapDispatchToProps)(App);
It looks like your navigation works, but you're having trouble passing the notification object through to the page? You can pass the object through history state.
To access the useHistory hook you would need to make your FCMService a custom hook.
const useFCMService = (): void => {
const history = useHistory();
React.useEffect(() => {
// Method called when tapping on a notification
PushNotifications.addListener('pushNotificationActionPerformed',
(action: PushNotificationActionPerformed) => {
const notification = action.notification.data as INotificationData;
history.push({ pathname: '/page/plot-position/', state: { notification } });
}
);
}, []);
}
And then include your useFCMService custom hook in your App component.
const App: React.FC<IProps> = ({ getCompanies, getUser }) => {
useFCMService();
...
};
Deep linking provides us a way to do this: Using both an action to open the application and an action at opening the application we can enroute the user to the correct destination.
Opening the application
Here we will create an action to open the url when the user taps on the push notification; to do this less use a listener:
const {PushNotifications, App} = Plugins
***
PushNotifications.addListener(
"pushNotificationActionPerformed",
(notification: PushNotificationActionPerformed) =>{
const data = notification.notification.data;
if (data.packageNumber) App.openUrl({url: `com.company.appname://tabs/package-details/${data.packageNumber}`})
else App.openUrl({url:'/tabs'})
}
)
com.company.app:// is of capital importance since the app must reach the application must reach an existing given url, otherwise the following action(catching the url) won't be triggers since it waits a complete true from the App.openUrl function; as we are opening an internal url, this must begin with the apps given name in the capacitor config page(see the following example where we can realize how use the local url).
In this way we are adding a function to open the application in an specific route.
Redirecting the user
Here, we will complete the application's part from the deep linking tutorial: we create a new listener component who handles the appOpenUrl events and redirects to the user and we will put it on the main App file inside of its respective IonRouter:
const AppUrlListener: React.FC<any> = () => {
let history = useHistory();
useEffect(() => {
App.addListener('appUrlOpen', (data: any) => {
const slug = data.url.split(':/').pop();
if (slug) {
history.push(slug);
}
});
}, []);
return null;
};
Don't forget the route in router must begin with /, and since the application url contains :/, we split the url here and we get the second part, the slug; we push it on the history, triggering the router and getting the normal behaviour when you entering in a new route.
We will add this component inside of the router:
<IonReactRouter>
<IonSplitPane contentId="main">
<Menu />
<AppUrlListener />
<IonRouterOutlet id="main">
Now, the application will be listening the appOpenUrl event, and when it gets a new of this events, it will push the gotten url to the history, redirecting the user to that route.
I'm using React Native with Expo Location.startLocationUpdatesAsync. I have created three functions in the same file, the first two are in my export default App function, with TaskManager defined globally in the file.
async startBackgroundUpdate() {
await Location.startLocationUpdatesAsync(taskName, {
accuracy: Location.Accuracy.Highest,
timeInterval: 0,
distanceInterval: 0,
forgroundService: {
notificationTitle: "I'm a notification title",
notificationBody: "I'm a notification body",
notificationBody: "#abcdef"
}
});
}
async stopBackgroundUpdate() {
await Location.stopLocationUpdatesAsync(taskName)
}
TaskManager.defineTask(taskName, async ({ data, error }) => {
if (error) {
return console.log(error)
}
if (data) {
const { locations } = data;
locationGlobal = locations[0];
let date = new Date(locationGlobal.timestamp).toISOString().slice(-13, -5)
// App.setLocation(location)
console.log("Task has been running "+ count +" time(s). With data: "+data);
console.log(locationGlobal);
console.log(count++);
console.log(date)
}
});
I also have a global variable that holds the taskName
const taskName = 'geofencing'
The stop and start background location functions are hooked up to a button. I can start the background location fine, but when I try and stop it I get the error
Task 'geofencing' not found for app ID '#anonymous/track-it-fecc322d-0ade-4239-8bd1-fdcb47b0397e'.
- node_modules\react-native\Libraries\BatchedBridge\NativeModules.js:104:55 in <unknown>
- node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:414:4 in __invokeCallback
- ... 4 more stack frames from framework internals
I am new to this and I think I am missing a piece of the puzzle. I can see the taskName and create the task from inside my App function but at the same level I can't reference it to stop it, any ideas?