transistorsoft / capacitor-background-geolocation

The most sophisticated background location-tracking & geofencing module with battery-conscious motion-detection intelligence for iOS and Android.
MIT License
96 stars 16 forks source link

Background geolocation stops working after 5 minutes when the application is in the Background or Screen is locked #276

Open MehulGosar opened 1 month ago

MehulGosar commented 1 month ago

Your Environment

startBackgroundGeolocation() { console.log("Reached: startBackgroundGeolocation()") //Step 1: Just starting: Wire up event-listeners

BackgroundGeolocation.onLocation((location) => { this.updateUserLocationValue('onLocation', location) }) BackgroundGeolocation.onMotionChange((event) => { this.updateUserLocationValue('onMotionChange', event) }) BackgroundGeolocation.onActivityChange((event) => { this.updateUserLocationValue('onActivityChange', event) })

BackgroundGeolocation.onHeartbeat (async (heartbeatEvent) => { console.log("[heartbeat] ", heartbeatEvent); let location = await BackgroundGeolocation.getCurrentPosition({ timeout: 30, // 30 second timeout to fetch location maximumAge: 5000, // Accept the last-known-location if not older than 5000 ms. // Try to fetch a location with an accuracy of 10 meters. desiredAccuracy: 10, samples: 3, // How many location samples to attempt. extras: { // Custom meta-data. "route_id": 123 } }); this.updateUserLocationValue('onHeartbeat', location) })

BackgroundGeolocation.ready({ // Geolocation Config reset: true, debug: true, //Enable for debugging, disable in production logLevel: BackgroundGeolocation.LOG_LEVEL_VERBOSE, desiredAccuracy: BackgroundGeolocation.DESIRED_ACCURACY_HIGH, locationAuthorizationRequest: 'Always', distanceFilter: 10, stopTimeout: 5, // Stop tracking after 1 minute of no movement stopOnTerminate: false, // Continue tracking even if the app is terminated

startonBoot: true, // Start tracking when the device boots up enableHeadless: true, preventSuspend: true, // Prevent the OS from suspending the app heartbeatInterval: 60, // Send heartbeat every 60 seconds to keep the service alive foregroundService: true, // Enable foreground service to ensure tracking continues in the background maxDaysToPersist: 14, notification: { title: "Location Tracking", text: "Your location is being tracked in the background", Tracking", channelName: "Background Tracking", priority: BackgroundGeolocation.NOTIFICATION_PRIORITY_HIGH }, // Application config background PermissionRationale: { title: "Allow {applicationName} to access to this device's location in the background?", message: 'This app collects location data to enable tracking even when the app is closed or not in use. Please enable "Allow all the time" location permission', positiveAction: 'Change to "Allow all the time" ', negativeAction: "Cancel" } }).then((state) => { // BackgroundGeolocation is now ready to use. console.log("Inside Web SS- BackgroundGeolocation.ready() state.enabled: state.enabled) if (!state.enabled) { BackgroundGeolocation.start().then((state) => { console.log('Inside start() !state.enabled: !state.enabled) }) } }); } async updateUserLocationValue(name, event: any): Promise ( let userID = localStorage.getItem(“userID”) if (name == "onLocation" || name=="onHeartbeat") { console.log("OnLocation lat: "+ event.coords.latitude +" long: "+ event.coords.longitude +" "+event.coords) //POSTING DATA TO SERVER }

if (name == "onMotionChange") { console.log("onMotionChange lat:" event.location.coords.latitude + "long:" event.location.coords.longitude +" "+ event.location.coords) //POSTING DATA TO SERVER } if (name =="onActivityChange") { console.log("onActivityChange lat:"+ event.location.coords.latitude+" long: "+ event.location.coords.longitude +" "+ event.location.coords”); //POSTING DATA TO SERVER } }

## Expected Behavior
<!--- Tell us what should happen -->
Whenever a user logs into the application, we capture their user ID and store it locally in our Sqlite database or local storage on the phone. Now, whenever the user uses our application, whether in the foreground or background, we want to capture the user's location and post it on our server along with the timestamp and the UserID. The unique identifier between the users is the User ID. 
Note 1: Storing in local Sqlite storage helps us retrieve the user Id even when the user has logged out of the application but still has the application in the background or when the screen is locked. 
Note 2: Capture the user’s location in foreground and background irrespective of the phone’s screen been locked or not
## Actual Behavior
<!--- Tell us what happens instead -->
We can capture the user’s location when the application is in the foreground irrespective of whether the phone’s screen has been locked or unlocked. But:
As soon as the application goes to the background following behaviour is observed. Irrespective of the order of occurrence of the below scenarios, the application can capture the user’s location for a maximum of 5 minutes:
1. The application is in the background but other applications have not been used – For both, the screen locked and unlocked
2. The application is in the background while other applications have been used on top of it. 
## Steps to Reproduce
<!--- reproduce this issue; include code to reproduce, if relevant -->
1. Install the necessary packages, and add the provided code in the app component
2. Can hardcode and create a local storage variable with an alphanumeric user ID as shown in the code
3. Create a service and add the necessary code to the service file, in the updateUserLocationValue() function, write code to post data to the server which has: {latitude, longitude, userID, and timestampOfCapture}
4. Install the application and provide it with all the necessary permissions. Use the application under the following scenarios for more than 5 minutes while moving: 
    - Keep the App in the foreground, and the screen locked for more than 10 minutes. 
    - Keep the App in the background, and the screen unlocked without using other applications for more than 10 minutes. 
    - Keep the App in the background, and the screen unlocked while using other applications for more than 10 minutes
    - Keep the App in the background, and screen locked for more than 10 minutes. 

## Context
<!--- What were you trying to do? -->
Note: I have used the Flutter application from the Play Store. It tick marks all our requirements. But we can't replicate the same in our application after more than a month of multiple attempts and referring documentation.
We want to capture the user’s location whenever the user is moving having the application is been used irrespective of it is in foreground or background and the screen been locked or unlocked
## Debug logs
<!-- include iOS / Android logs
- ios XCode logs,
- use #getLog / #emailLog methods (@see docs)
- Android: $ adb logcat
-->
<details>
    <summary>Logs</summary>

```<!-- Syntax highlighting:  DO NOT REMOVE -->
PASTE_YOUR_LOGS_HERE

<!—No Logs to share -->

christocracy commented 1 month ago

Sounds like a case for useLegacyBridge: true

MehulGosar commented 1 month ago

Hello @christocracy !! Thank you for the reply. The issue is still there. Also, we were already using useLegacyBridge:true in our application. Current Behaviour: When I tested the app yesterday, the following behavior is seen: When the application is in the background, for 5 minutes the updates are happening fine. Post that the application is queuing up all the requests i.e. "throttling" them for the rest of the period. How do I know that? Using debug:true. Once the application comes to foreground, the app is then making all the requests at once from the same place. Thus, making 150-200 location capture calls from the same location at the same timestamp are captured.

Here are some other piece of code to refer: I hope they help in deducing the issue because I'm not able to since past few days.

capacitor.config.json: import { CapacitorConfig } from '@capacitor/cli';

const config: CapacitorConfig = { appId: 'io.ionic.starter', appName: 'MY APP NAME', webDir: 'www', bundledWebRuntime: false, android: { useLegacyBridge: true,

}

};

export default config;

ionic.config.json:

{

"name": "io.ionic.starter",

"integrations": {

"capacitor": {}

},

"type": "angular"

}

strings.xml:

<?xml version="1.0" encoding="utf-8"?>

MY APP NAME MY APL NAME> io.ionic.starter io.ionic.starter

app.component.ts:

private initializeBackgroundListener() { App.addListener('appStateChange', ({ isActive }) => { //console.log("Is Active"+isActive) if (lisActive) { this.disableWebView() } else { this.enableWebView(); } }); }

disableWebView() { const webview = document.querySelector('ion-app'); if (webview) { webview.style.display = 'none'; // Disables WebView rendering } }

enableWebView() { const webview = document.querySelector('ion-app'); if (webview) { webview.style.display = 'block'; // Enables WebView rendering } }

package.json:

"name": "io.ionic.starter",

"version": "0.0.1", "author": "Ionic Framework", "homepage": "https://ionicframework.com/", Debug "scripts": { "ng": "ng", "start": "ng serve", "build": "ng build", "test": "ng test", "lint": "ng lint", "e2e": "ng e2e"

},

christocracy commented 1 month ago

This is a Capacitor problem. The plug-in did its job firing those events. If Capacitor is failing to do its job, queueing those events instead of firing them immediately, that’s up to Capacitor to fix

MehulGosar commented 1 month ago

Hello @christocracy
I agree, it's an ionic capacitor issue. Was there a similar problem reported by anyone else that got resolved? We're at that stage where we can't replace\ switch the internal technology (ionic capacitor) itself. But we want to use Transistorsoft as it matches our requirements. It's only the throttling issue that is making it unusable at this point. Can you please help us by any chance? We'll be grateful. I'm looking for a solution in parallel. If I find it, I'll post it so it can help others as well. Thanks, Mehul

chrisbiscuit commented 1 month ago

Hi @MehulGosar,

Thanks for posting this. Did you find a solution for your issue? I’m experiencing the same problem with the capacitor-background-geolocation plugin when my app is in the background. I was hoping to persist location data to local storage every time the location event fires.

Currently, I retrieve all location data at the end of the activity using the getLocations method:

let locations = await BackgroundGeolocation.getLocations();

While this works, it’s not perfect. It seems that under certain conditions the SQLite database used by the plugin may get cleared, resulting in missing location data.

@christocracy , do you have any recommendations or best practices to ensure the reliability of location data persistence when the app is in the background?

Thanks, Chris

christocracy commented 1 month ago

It seems that under certain conditions the SQLite database used by the plugin may get cleared

The only reason the plug-in deletes a record from its SQLite db is it your server returns HTTP status 200.

best practices to ensure the reliability of location data persistence when the app is in the background?

The plug-in has no issues with data reliability.

MehulGosar commented 3 weeks ago

@chrisbiscuit and @christocracy

I found a solution, and I'm testing it out. It's not a solution per se, but an amalgam of various things I tried to make it work. And it did. I will soon post all the things I tried for the same post thorough testing with my observations. Stay tuned. Will hit up here with my observations and findings.

msacc-mas commented 2 weeks ago

Hello @christocracy !! Thank you for the reply. The issue is still there. Also, we were already using useLegacyBridge:true in our application. Current Behaviour: When I tested the app yesterday, the following behavior is seen: When the application is in the background, for 5 minutes the updates are happening fine. Post that the application is queuing up all the requests i.e. "throttling" them for the rest of the period. How do I know that? Using debug:true. Once the application comes to foreground, the app is then making all the requests at once from the same place. Thus, making 150-200 location capture calls from the same location at the same timestamp are captured.

Here are some other piece of code to refer: I hope they help in deducing the issue because I'm not able to since past few days.

capacitor.config.json: import { CapacitorConfig } from '@capacitor/cli';

const config: CapacitorConfig = { appId: 'io.ionic.starter', appName: 'MY APP NAME', webDir: 'www', bundledWebRuntime: false, android: { useLegacyBridge: true,

}

};

export default config;

ionic.config.json:

{

"name": "io.ionic.starter",

"integrations": {

"capacitor": {}

},

"type": "angular"

}

strings.xml:

MY APP NAME

MY APL NAME>

io.ionic.starter

io.ionic.starter

app.component.ts:

private initializeBackgroundListener() { App.addListener('appStateChange', ({ isActive }) => { //console.log("Is Active"+isActive) if (lisActive) { this.disableWebView() } else { this.enableWebView(); } }); }

disableWebView() { const webview = document.querySelector('ion-app'); if (webview) { webview.style.display = 'none'; // Disables WebView rendering } }

enableWebView() { const webview = document.querySelector('ion-app'); if (webview) { webview.style.display = 'block'; // Enables WebView rendering } }

package.json:

"name": "io.ionic.starter",

"version": "0.0.1", "author": "Ionic Framework", "homepage": "https://ionicframework.com/", Debug "scripts": { "ng": "ng", "start": "ng serve", "build": "ng build", "test": "ng test", "lint": "ng lint", "e2e": "ng e2e"

},

So are saying that after 5 minutes, it keeps recording the same last known location (location at the 5 minute mark) to the database, over and over? Then when the app is forgrounded, it takes all of those repeated locations and sends the requests?

Or are you saying that after 5 minutes, it queues up a bunch of blank/pending requests, and then when the app is forgrounded - it fills in all those requests with the devices current location and sends them?

in either case - what interval does this happen?

Like in scenario A, after 5 minutes does it record the duplicate location to the DB every x seconds?

or in scenario B, how what is the interval between the requests that get set up pending ?

christocracy commented 2 weeks ago

Does the plug-in continue to operate, witnessed by the debug sound FX?