Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
MehulGosar opened this issue Sep 13, 2024 · 9 comments

Comments

@MehulGosar
Copy link

Your Environment

  • Plugin version:
    For capacitor background geolocation: 4.10.0 and for background fetch 1.0.4

  • Platform: iOS or Android
    Platform is Android

  • Device OS version:
    For Redmi K50: 1.0.2.0.ULOINXM (Android 14)
    For One Plus Nord CE2: : OxygenOS 14.0 (Android 14)
    For Motorola Moto G52L Software channel Retin (Android 13)

  • Device manufacturer / model:
    Tested on Redmi K50
    Tested on One Plus Nord CE2
    Tested on Motorola Moto G52

  • XCode version:
    NA as Windows been used

  • Capacitor info (npx cap doctor)
    Capacitor Doctor
    Latest Dependencies:
    @capacitor/cli: 6.1.2
    @capacitor/core: 6.1.2
    @capacitor/android: 6.1.2
    @capacitor/ios: 6.1.2.
    Installed Dependencies:
    @capacitor/ios: not installed
    @capacitor/cli: 4.5.0
    @capacitor/android: 4.3.0
    @capacitor/core: 4.3.0
    [success] Android looking great!

  • Plugin config provided to #ready:

INSERT_YOUR_CODE_HERE

**************************** app.component.ts ***************************

import {App} from "@capacitor/app"
import { Platform } from '@ionic/angular';
import { LocationService } from './Services/location.service';
import { Capacitor } from '@capacitor/core';
@Component({
selector: 'app-root',
templateUrl: 'app.component.html',
styleUrls: ['app.component.scss'],
})
export class AppComponent {
constructor(private platform: Platform, private locationService: LocationService) {
}
ngOnInit(){
localStorage.setItem(userID', “ABC123”);
this.locationService.initialize()
}
}```
**************************** location.service.ts ***************************
```//OTHER IMPORTS AND:
import BackgroundGeolocation, { DeviceSettingsRequest, Subscription} from "@transistorsoft/capacitor-background-geolocation";
export class locationService {
//THE SAMPLE CODE:
initialize(){
this.startBackgroundGeolocation()
}




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<void> (
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

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

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

  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

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

Logs
PASTE_YOUR_LOGS_HERE

<!—No Logs to share -->

@christocracy
Copy link
Member

Sounds like a case for useLegacyBridge: true

@MehulGosar
Copy link
Author

MehulGosar commented Sep 20, 2024

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"

},

@christocracy
Copy link
Member

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
Copy link
Author

MehulGosar commented Sep 23, 2024

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
Copy link

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
Copy link
Member

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
Copy link
Author

@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
Copy link

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
Copy link
Member

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants