transistorsoft / flutter_background_geolocation

Sophisticated, battery-conscious background-geolocation & geofencing with motion-detection
https://www.transistorsoft.com/shop/products/flutter-background-geolocation
Other
650 stars 241 forks source link

Own database question. #1374

Open KlipX opened 3 weeks ago

KlipX commented 3 weeks ago

Hi, I use your package and it is great. Thanks for your work. I would like to ask about one thing. I need to synchronize location events with the server, but also save them locally. You answered a similar question here

https://github.com/transistorsoft/flutter_background_geolocation/issues/1318#issuecomment-2187115452

Your answer:

You can’t. The plug-in desires an empty database. If you want the location in a database, it’s up to you to implement your own SQLite database.

So you indicated that it is possible to create your own database.

Can you tell me how it is possible, how I can listen to location events and save them in the background to my database? I thought about bg.BackgroundGeolocation.registerHeadlessTask but from what I understand it is only for Android. I would like it to work for ios and Android in the background. Will appreciate any tip and link to documentation 😄

christocracy commented 3 weeks ago

how I can listen to location events and save them in the background to my database

I thought about bg.BackgroundGeolocation.registerHeadlessTask but from what I understand it is only for Android. I would like it to work for ios and Android in the background

The background state != the terminated state. Android headless-mode is for the terminated state, not background.

For iOS, you only need to implement .onLocation.

KlipX commented 3 weeks ago

how I can listen to location events and save them in the background to my database

  • for both iOS and Android, implement .onLocation to insert records into your own custom database.
  • For Android, also implement a headless-task for the terminated state.

I thought about bg.BackgroundGeolocation.registerHeadlessTask but from what I understand it is only for Android. I would like it to work for ios and Android in the background

The background state != the terminated state. Android headless-mode is for the terminated state, not background.

For iOS, you only need to implement .onLocation.

Ok, so correct me if I'm wrong but I probably meant the terminal state. (The case when the user switches the application or locks the phone for a long time. (We track the user's walk)). Generally I needs to have the exact same locations events saved as your package.

So if the app is to track a user's walk, I have to assume that it collects locations even if the phone remains locked for a long time. In that case, the system may close the app, as far as I know, but your package still collects locations. In summary, I probably need the same location events as your package collects. Is it possible with your package?

Additional question: Are listening on BackgroundGeolocation.onLocation in the background state and saving to storage for both ios and android enough? What are the chances that the system will move application to terminated state when it runs for such a long time in the background and receives a lot of location events which save to storage?

christocracy commented 3 weeks ago

iOS and Android are fundamentally different in the terminated state.

Android

When an Android app is terminated, the OS allows services to continue running "headless". All events while your app is terminated are forwarded to your registered headless-task. To implement a custom database, you must do you INSERT in both your .onLocation listener in addition to your registered headless-task.

iOS

When an iOS app is terminated (either by user or OS), absolutely everything is stopped. There is nothing running. The only thing that will re-awaken a terminated iOS app is to move at least 200 meters, when the OS will automatically relaunch your app in the background, just as if launched from the home-screen icon. All locations will be delivered to your .onLocation event-listener as normal.

KlipX commented 3 weeks ago

iOS and Android are fundamentally different in the terminated state.

Android

When an Android app is terminated, the OS allows services to continue running "headless". All events while your app is terminated are forwarded to your registered headless-task. To implement a custom database, you must do you INSERT in both your .onLocation listener in addition to your registered headless-task.

iOS

When an iOS app is terminated (either by user or OS), absolutely everything is stopped. There is nothing running. The only thing that will re-awaken a terminated iOS app is to move at least 200 meters, when the OS will automatically relaunch your app in the background, just as if launched from the home-screen icon. All locations will be delivered to your .onLocation event-listener as normal.

It's really helpful. It allows me to save all events in the database. I assumed that if headless-task is only for Android, then for iOS it is not possible to save the location in the terminal state in the database. But if I understand correct that for ios terminal state the application is started and .onLocation is called. So for ios, just save the .onLocation events to storage and it should work fine even in the terminal state.

Please confirm that I understand this correctly so that I can rest easy.

And once again thank you for your answers, they are really helpful

christocracy commented 3 weeks ago

Like I said above:

All locations will be delivered to your .onLocation event-listener as normal.

KlipX commented 2 weeks ago

Like I said above:

All locations will be delivered to your .onLocation event-listener as normal.

Today I implemented the solution on andorid and in terminal state I have the following problem. Maybe you know what the problem is?

I/TSLocationManager(26088):   ✅  INSERT: 61049729-91aa-4287-807e-ae54853d4752
I/FA      (26088): Application backgrounded at: timestamp_millis: 1730729324145
E/TSLocationManager(26088): [c.t.f.b.HeadlessTask$TaskRunner run] 
E/TSLocationManager(26088):   ‼️  Invalid Headless Callback ids.  Cannot handle headless event
import 'package:flutter_background_geolocation/flutter_background_geolocation.dart' as bg;
import 'package:hive_flutter/hive_flutter.dart';
import 'package:pergato/app/app.dart';
import 'package:pergato/app/infrastructure/geolocation/storage/location_hive_dto.dart';
import 'package:pergato/app/infrastructure/geolocation/storage/location_storage.dart';
import 'package:pergato/app/infrastructure/geolocation/utils/mappers.dart';
import 'package:pergato/bootstrap.dart';
import 'package:pergato/config/environment/environments.dart';

/// Receive events from BackgroundGeolocation in Headless state.
@pragma('vm:entry-point')
void backgroundGeolocationHeadlessTask(bg.HeadlessEvent headlessEvent) async {
  if (headlessEvent.name == bg.Event.LOCATION) {
    final bg.Location location = headlessEvent.event as bg.Location;
    await Hive.initFlutter();
    await LocationStorage().save(
      LocationHiveDTO.fromDomain(
        location.customLocation,
      ),
    );
  }
}

void main() {
  bg.BackgroundGeolocation.registerHeadlessTask(backgroundGeolocationHeadlessTask);

  bootstrap(() => const App(), dev);
}

`

christocracy commented 2 weeks ago

Maybe you have an error in that file.

KlipX commented 2 weeks ago

Maybe you have an error in that file. which file?

My config:

{
   "desiredAccuracy":-1,
   "distanceFilter":10.0,
   "disableElasticity":true,
   "isMoving":true,
   "activityRecognitionInterval":1000,
   "disableStopDetection":true,
   "stopOnStationary":false,
   "autoSync":false,
   "maxDaysToPersist":2,
   "authorization":{
      "strategy":"jwt",
      "accessToken":null,
      "refreshToken":null,
      "refreshUrl":null,
      "refreshPayload":null,
      "refreshHeaders":null,
      "expires":-1
   },
   "stopOnTerminate":false,
   "startOnBoot":true,
   "debug":true,
   "logLevel":5,
   "reset":false,
   "locationAuthorizationRequest":"Always",
   "disableLocationAuthorizationAlert":true,
   "activityType":4,
   "disableMotionActivityUpdates":false,
   "enableHeadless":true,
   "backgroundPermissionRationale":{
      "title":"xxxx",
      "message":"yyyyy",
      "positiveAction":null,
      "negativeAction":null
   },
   "notification":{
      "layout":null,
      "title":"xxxx.",
      "text":"yyyyy",
      "color":null,
      "channelName":null,
      "channelId":null,
      "smallIcon":"drawable/notification_icon",
      "largeIcon":null,
      "priority":1,
      "sticky":false,
      "strings":null,
      "actions":null
   }
}
christocracy commented 2 weeks ago

I suggest you test in a simple hello-world app. Do nothing more in your headless-task but a simple print statement.