Skip to content

[Help Wanted]: Not always getting locations when app is closed #318

Open
@Serra19

Description

@Serra19

Required Reading

  • Confirmed

Plugin Version

6.1.0

Mobile operating-system(s)

  • iOS
  • Android

What do you require assistance about?

Dear,

In September 2023 we purchased the Starter license for CAPACITOR BACKGROUND GEOLOCATION, and we are carrying out satisfactory tests, however we have not exactly achieved our goal.

Please let me give you some details;
We are implementing your library in an App programmed in IONIC.
The requirement is that a user who goes from point A to B and does so with our App closed (not in the background) can still collect his location.

Currently we use polygons, which we activate at point a and point b.
To make sure we don't lose any information, we activate a geofence at both points with a radius of 100m, and another one with a radius of 150m. In addition to those two geofences, we add 4 more geofences at a distance of 200m in the North, East, South and West, with a radius of 200m, so we have 6 geofences for point A and 6 for point B.
The result is that it collects location data when it is already about 100-150 meters away from point A.

Sometimes it captures the location between the two points, other times only the surroundings of A and B.
We would like to be able to make full use of this library to collect the location with the highest possible precision.

Can you help us with the library configuration?

Thank you so much.

[Optional] Plugin Code and/or Config

import { BehaviorSubject } from 'rxjs';

import { Injectable } from '@angular/core';
import Ubicacion from '@app/models/Ubicacion';
import BackgroundGeolocation, {
  Location
} from "@transistorsoft/capacitor-background-geolocation";
import { AuthenticationService } from '@services/authentication/authentication.service';
import { Preferences } from '@capacitor/preferences';
import { environment } from '@envs';

import { CapacitorHttp } from "@capacitor/core";
import { throttle } from '@app/utils'
import { Capacitor } from '@capacitor/core';
import { MapGeocoder } from '@angular/google-maps';

declare type Coords = {
  latitude: number;
  longitude: number;
  accuracy: number;
  speed?: number;
};

declare type Geofence = {
  identifier: string;
  latitude: number;
  longitude: number;
  radius?: number;
  notifyOnEntry?: boolean;
  notifyOnExit?: boolean;
  notifyOnDwell?: boolean;
  loiteringDelay?: number;
}

@Injectable({
  providedIn: 'root',
})
export class GeolocationService {
  public location = new BehaviorSubject<Ubicacion | undefined>(null);

  isNativePlatform = Capacitor.isNativePlatform();

  userLocations = new BehaviorSubject<{
    lat: number;
    lng: number;
    captured_at: string;
    source: string;
  }[]>([])

  constructor(
    private authentication: AuthenticationService,
    private geocoder: MapGeocoder
  ) {
    this.userLocations.subscribe(userLocations => {
      if (userLocations.length > 0) {
        throttle(() => this.sendUserLocations(), 60000)
      }
    })

    this.watchBackgroundPosition()
  }

  locality: string;
  address: string = "";

  watchBackgroundPosition() {
    if (!this.isNativePlatform) {
      return
    }
    BackgroundGeolocation.onLocation((location: Location) => {
      const existingLocation = this.userLocations.getValue().find(item => {
        return location.sample || (item.lat === location.coords.latitude && item.lng === location.coords.longitude && item.captured_at === location.timestamp)
      })
      if (!existingLocation) {
        this.userLocations.next([
          ...this.userLocations.getValue(),
          {
            lat: location.coords.latitude,
            lng: location.coords.longitude,
            captured_at: location.timestamp,
            source: "background"
          }
        ])
      }
    })

    BackgroundGeolocation.onGeofence(geofence => {
      const location = geofence?.location
      const existingLocation = this.userLocations.getValue().find(item => {
        return location?.sample || (item.lat === location?.coords?.latitude && item.lng === location?.coords?.longitude && item.captured_at === location?.timestamp)
      })

      if (!existingLocation) {
        this.userLocations.next([
          ...this.userLocations.getValue(),
          {
            lat: location.coords.latitude,
            lng: location.coords.longitude,
            captured_at: location.timestamp,
            source: `geofence/${geofence.action}`
          }
        ])

        this.sendUserLocations()
      }
    });

    BackgroundGeolocation.onNotificationAction((buttonId: string) => {
      console.warn('debug [onNotificationAction] buttonId:', buttonId);

      // Handle the notification click here
      // For example, navigate to a specific page in your app
    });

    BackgroundGeolocation.ready({
      desiredAccuracy: BackgroundGeolocation.DESIRED_ACCURACY_HIGH,
      distanceFilter: 50,
      stopTimeout: 5,
      enableHeadless: true,
      stopOnTerminate: false,
      startOnBoot: true,
      showsBackgroundLocationIndicator: false,
      notification: {
        sticky: true,
        title: "Waiis",
        text: "Seguimiento de la ubicación activado",
        priority: BackgroundGeolocation.NOTIFICATION_PRIORITY_MIN,
        layout: "location_tracking_layout",
        strings: {
          notificationButtonBar: "Desactivar notificación"
        },
        actions: [
          'notificationButtonBar',
        ],
      }
    }).then((state) => {
      BackgroundGeolocation.start()
    });
  }

  async addGeofence(geofence: Geofence) {
    if (!this.isNativePlatform) {
      return
    }
    const existingGeofence = await BackgroundGeolocation.getGeofence(geofence.identifier)

    if (!existingGeofence) {
      BackgroundGeolocation.addGeofence({
        ...geofence,
        radius: geofence.radius ?? 200,
        notifyOnEntry: geofence.notifyOnEntry ?? true,
        notifyOnExit: geofence.notifyOnExit ?? true,
        notifyOnDwell: false,
        loiteringDelay: geofence.loiteringDelay ?? 600000,
      })
    }
  }

  async updatePosition(position: Coords) {
    const latLng = {
      lat: position.latitude,
      lng: position.longitude
    }

    this.geocoder
      .geocode({ location: latLng })
      .subscribe(res => {
        if (res.results.length > 0) {
          this.address = res.results[0].formatted_address
          this.locality = res.results[0].address_components.find(address => address.types.includes('locality')).long_name
          this.location.next({
            nombre: "Your location",
            nombre_largo: this.address,
            locality: this.locality,
            coords: {
              lat: position.latitude,
              lng: position.longitude,
              precision: position.accuracy,
              speed: position.speed
            },
            place_id: 'located'
          });
        }
      })
  }

  sendUserLocations() {
    try {
      if (this.authentication.isAuthenticated.getValue()) {
        Preferences.get({ key: 'token' }).then(async (token) => {
          const options = {
            url: environment.apiUrl + '/user-locations/bulk',
            headers: {
              Authorization: "Bearer " + token.value,
              'Content-Type': 'application/json'
            },
            data: {
              locations: this.userLocations.getValue()
            },
          };

          const response = await CapacitorHttp.post(options);
          this.userLocations.next([])
        })
      }
    } catch (error) {
      console.warn("error", error)
    }
  }
}

[Optional] Relevant log output

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions