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

geofence events not logged to server in real-time #43

Closed
joserocha3 opened this issue Mar 19, 2019 · 8 comments
Closed

geofence events not logged to server in real-time #43

joserocha3 opened this issue Mar 19, 2019 · 8 comments

Comments

@joserocha3
Copy link
Contributor

  • Plugin version: flutter_background_geolocation: ^1.0.0-beta.5
  • Platform: iOS
  • OS version: 12.1
  • Device manufacturer / model: iPhone X Simulator
  • Flutter info (flutter doctor):
[✓] Flutter (Channel stable, v1.2.1, on Mac OS X 10.14.3 18D109, locale en-US)
[✓] Android toolchain - develop for Android devices (Android SDK version 28.0.3)
[✓] iOS toolchain - develop for iOS devices (Xcode 10.1)
[✓] Android Studio (version 3.3)
[✓] VS Code (version 1.32.3)
[✓] Connected device (1 available)

• No issues found!
  • Plugin config:
  await bg.BackgroundGeolocation.ready(bg.Config(
    desiredAccuracy: bg.Config.DESIRED_ACCURACY_HIGH,
    persistMode: bg.Config.PERSIST_MODE_ALL,
    logLevel: bg.Config.LOG_LEVEL_VERBOSE,
    stopOnTerminate: false,
    enableHeadless: true,
    startOnBoot: true,
    reset: true,
    autoSync: true,
    httpRootProperty: '.',
    locationTemplate:
        '{ "query": "mutation { insert_location(objects: { lat: <%= latitude %> lng: <%= longitude %> speed: <%= speed %> heading: <%= heading %> accuracy: <%= accuracy %> altitude: <%= altitude %> altitude_accuracy: <%= altitude_accuracy %> timestamp: \\\"<%= timestamp %>\\\" uuid: \\\"<%= uuid %>\\\" event: \\\"<%= event %>\\\" odometer: <%= odometer %> activity_type: \\\"<%= activity.type %>\\\" activity_confidence: <%= activity.confidence %> battery_level: <%= battery.level %> battery_is_charging: <%= battery.is_charging %> device_model: \\\"${deviceParams['device']['model']}\\\" device_platform: \\\"${deviceParams['device']['platform']}\\\" device_manufacturer: \\\"${deviceParams['device']['manufacturer']}\\\" device_version: \\\"${deviceParams['device']['version']}\\\" } ) { affected_rows } }" }',
    geofenceTemplate:
        '{ "query": "mutation { insert_location(objects: { lat: <%= latitude %> lng: <%= longitude %> speed: <%= speed %> heading: <%= heading %> accuracy: <%= accuracy %> altitude: <%= altitude %> altitude_accuracy: <%= altitude_accuracy %> timestamp: \\\"<%= timestamp %>\\\" uuid: \\\"<%= uuid %>\\\" event: \\\"<%= event %>\\\" odometer: <%= odometer %> activity_type: \\\"<%= activity.type %>\\\" activity_confidence: <%= activity.confidence %> battery_level: <%= battery.level %> battery_is_charging: <%= battery.is_charging %> device_model: \\\"${deviceParams['device']['model']}\\\" device_platform: \\\"${deviceParams['device']['platform']}\\\" device_manufacturer: \\\"${deviceParams['device']['manufacturer']}\\\" device_version: \\\"${deviceParams['device']['version']}\\\" geofence_identifier: \\\"<%= geofence.identifier %>\\\" geofence_action: \\\"<%= geofence.action %>\\\" } ) { affected_rows } }" }',
    url: '${appBase.hasuraUrl}',
    locationAuthorizationRequest: 'Always',
    locationAuthorizationAlert: {
      'titleWhenNotEnabled': 'Location services not enabled',
      'titleWhenOff': 'Location services are OFF',
      'instructions':
          'Location services required to start earning points. Please enable \'Always\' in location settings.',
      'cancelButton': 'Cancel',
      'settingsButton': 'Settings'
    },
    // Do not record location event if more than 300 meters/second since last
    // recorded event - Android only, iOS does not allow mock locations on device
    speedJumpFilter: 300,
    foregroundService: true,
    notificationPriority: bg.Config.NOTIFICATION_PRIORITY_MIN,
    notificationTitle: 'Zone Tracker',
    notificationText: 'Zone search underway',
    notificationChannelName: 'Zone Tracker',
  ));

Expected Behavior

When app is terminated both location and geofence events produce an http request to the server.

Actual Behavior

When app is terminated only location events produce an http request to the server. When app is reopened the geofence events are sent to the server all at once.

Context

On iOS simulator when the app is terminated location events are reliably sent to the server. However, geofence events are not sent to the server until the application is reopened. That causes the timestamp to be identical on all geofence records, thus giving the appearance the geofence events happened after the location events. I have not tried to replicate yet on Android.

Ideally, the geofence events would be sent in real-time, just like the location events.

The log for this issue can be found here.

Screen Shot 2019-03-19 at 11 39 46 AM

geofence event uuid values:
ed229f41-1f76-4d08-815c-4abf2e5d8d1f
ab8965fa-1ccc-46d1-8d20-db1ea675152f
5d534cd7-6e4f-4bb6-a644-a9a7caa54d04
1d3ddf9e-a5df-441b-a462-e06e963a03c8
cb196d57-f705-4562-aa6a-057c5e9597f9
1e535b14-2e9c-48ea-b8c1-30eb7ca27d25
1abe219f-a707-4510-84d3-c9d0d76c81f1
1159efaf-fd77-40fa-b422-56abfd2497c4
75123ec7-c6ee-40c7-adae-519ce403003c

@christocracy
Copy link
Member

Show me where you’re listening to #onLocation, #onGeofence relative to where you’re executing #ready.

@joserocha3
Copy link
Contributor Author

joserocha3 commented Mar 19, 2019

My #startTracking is called on application start.

import 'package:background_fetch/background_fetch.dart';
import 'package:flutter/material.dart';
import 'package:flutter_background_geolocation/flutter_background_geolocation.dart'
    as bg;

import 'package:pointbee/utilities/notifications.dart';
import 'package:pointbee/state/app_state.dart';

/// Receive events from BackgroundGeolocation in Headless state
void backgroundGeolocationHeadlessTask(bg.HeadlessEvent headlessEvent) async {
  print('📬 --> $headlessEvent');

  await setupNotifications();

  switch (headlessEvent.name) {
    case bg.Event.LOCATION:
      bg.Location location = headlessEvent.event;
      print('[BackgroundGeolocation] Headless LOCATION: $location');
      showNotification(
        title: '[BackgroundGeolocation] Headless LOCATION',
        body: 'speed: ${location.coords.speed} time: ${location.timestamp}',
      );
      break;
    case bg.Event.GEOFENCE:
      bg.GeofenceEvent geofenceEvent = headlessEvent.event;
      print('[BackgroundGeolocation] Headless GEOFENCE: $geofenceEvent');
      showNotification(
        title: '[BackgroundGeolocation] Headless GEOFENCE',
        body:
            'speed: ${geofenceEvent.location.coords.speed} time: ${geofenceEvent.location.timestamp}',
      );
      break;
    case bg.Event.GEOFENCESCHANGE:
      bg.GeofencesChangeEvent event = headlessEvent.event;
      print('[BackgroundGeolocation] Headless GEOFENCESCHANGE: $event');
      showNotification(
        title: '[BackgroundGeolocation] Headless GEOFENCESCHANGE',
        body: 'speed: $event',
      );
      break;
  }
}

/// Receive events from BackgroundFetch in Headless state
void backgroundFetchHeadlessTask() async {
  bg.Location location =
      await bg.BackgroundGeolocation.getCurrentPosition(samples: 1);
  await setupNotifications();
  print('[BackgroundFetch] Headless LOCATION: $location');
  showNotification(
    title: '[BackgroundFetch] Headless LOCATION',
    body: 'speed: ${location.coords.speed} time: ${location.timestamp}',
  );
  BackgroundFetch.finish();
}

/// Add a geofence to the plugin
void addGeofence({
  @required String id,
  @required double lat,
  @required double lng,
  @required int radius,
}) async {
  bg.Geofence geofence = bg.Geofence(
    identifier: id,
    latitude: lat,
    longitude: lng,
    radius: double.parse(radius.toString()),
    notifyOnEntry: true,
    notifyOnDwell: true,
    notifyOnExit: true,
  );

  try {
    await bg.BackgroundGeolocation.addGeofence(geofence);
  } catch (e) {
    print('Error adding geofence with id $id');
    return;
  }

  print('Successfully added geofence with id $id');
}

/// Initialize plugin to start tracking
Future<bool> startTracking({String env}) async {
  // Apply tracker url with username & device params for recognition by tracking server
  Map<String, dynamic> deviceParams = await bg.Config.deviceParams;

  await bg.BackgroundGeolocation.ready(bg.Config(
    desiredAccuracy: bg.Config.DESIRED_ACCURACY_HIGH,
    persistMode: bg.Config.PERSIST_MODE_GEOFENCE,
    logLevel: bg.Config.LOG_LEVEL_VERBOSE,
    stopOnTerminate: false,
    enableHeadless: true,
    startOnBoot: true,
    reset: true,
    autoSync: true,
    httpRootProperty: '.',
    locationTemplate:
        '{ "query": "mutation { insert_location(objects: { lat: <%= latitude %> lng: <%= longitude %> speed: <%= speed %> heading: <%= heading %> accuracy: <%= accuracy %> altitude: <%= altitude %> altitude_accuracy: <%= altitude_accuracy %> timestamp: \\\"<%= timestamp %>\\\" uuid: \\\"<%= uuid %>\\\" event: \\\"<%= event %>\\\" odometer: <%= odometer %> activity_type: \\\"<%= activity.type %>\\\" activity_confidence: <%= activity.confidence %> battery_level: <%= battery.level %> battery_is_charging: <%= battery.is_charging %> device_model: \\\"${deviceParams['device']['model']}\\\" device_platform: \\\"${deviceParams['device']['platform']}\\\" device_manufacturer: \\\"${deviceParams['device']['manufacturer']}\\\" device_version: \\\"${deviceParams['device']['version']}\\\" } ) { affected_rows } }" }',
    geofenceTemplate:
        '{ "query": "mutation { insert_location(objects: { lat: <%= latitude %> lng: <%= longitude %> speed: <%= speed %> heading: <%= heading %> accuracy: <%= accuracy %> altitude: <%= altitude %> altitude_accuracy: <%= altitude_accuracy %> timestamp: \\\"<%= timestamp %>\\\" uuid: \\\"<%= uuid %>\\\" event: \\\"<%= event %>\\\" odometer: <%= odometer %> activity_type: \\\"<%= activity.type %>\\\" activity_confidence: <%= activity.confidence %> battery_level: <%= battery.level %> battery_is_charging: <%= battery.is_charging %> device_model: \\\"${deviceParams['device']['model']}\\\" device_platform: \\\"${deviceParams['device']['platform']}\\\" device_manufacturer: \\\"${deviceParams['device']['manufacturer']}\\\" device_version: \\\"${deviceParams['device']['version']}\\\" geofence_identifier: \\\"<%= geofence.identifier %>\\\" geofence_action: \\\"<%= geofence.action %>\\\" } ) { affected_rows } }" }',
    url: '${appBase.hasuraUrl}',
    locationAuthorizationRequest: 'Always',
    locationAuthorizationAlert: {
      'titleWhenNotEnabled': 'Location services not enabled',
      'titleWhenOff': 'Location services are OFF',
      'instructions':
          'Location services required to start earning points. Please enable \'Always\' in location settings.',
      'cancelButton': 'Cancel',
      'settingsButton': 'Settings'
    },
    // Do not record location event if more than 300 meters/second since last
    // recorded event - Android only, iOS does not allow mock locations on device
    speedJumpFilter: 300,
    foregroundService: true,
    notificationPriority: bg.Config.NOTIFICATION_PRIORITY_MIN,
    notificationTitle: 'Zone Tracker',
    notificationText: 'Zone search underway',
    notificationChannelName: 'Zone Tracker',
  ));

  try {
    await bg.BackgroundGeolocation.startGeofences();
  } catch (e) {
    print(e);
  }

  // Register BackgroundGeolocation headless-task
  bg.BackgroundGeolocation.registerHeadlessTask(
      backgroundGeolocationHeadlessTask);

  // Register BackgroundFetch headless-task
  BackgroundFetch.registerHeadlessTask(backgroundFetchHeadlessTask);

  // Fired whenever a location is recorded
  bg.BackgroundGeolocation.onLocation((bg.Location location) {
    print('[BackgroundGeolocation] onLocation $location');
    showNotification(
      title: '[BackgroundGeolocation] onLocation',
      body: 'speed: ${location.coords.speed} time: ${location.timestamp}',
    );
  }, (bg.LocationError error) {
    print('[onLocation] ERROR: $error');
  });

  bg.BackgroundGeolocation.onHttp((bg.HttpEvent event) {
    print('[BackgroundGeolocation] onHttp $event');
  });

  bg.BackgroundGeolocation.onGeofence((bg.GeofenceEvent event) {
    print('[BackgroundGeolocation] onGeofence $event');
    showNotification(
      title: '[BackgroundGeolocation] onGeofence',
      body:
          'speed: ${event.location.coords.speed} time: ${event.location.timestamp}',
    );
  });

  try {
    // force location permission dialog
    await bg.BackgroundGeolocation.getCurrentPosition(
      persist: false,
      samples: 1,
    );
  } catch (e) {
    print(e);
    // Permission was denied
    if (e.code == 1) return false;
  }

  return true;
}

Later in the application on a button tap before starting bicycle ride this is executed:

await bg.BackgroundGeolocation.start();

@christocracy
Copy link
Member

  • Listen to all events before #ready

@joserocha3
Copy link
Contributor Author

joserocha3 commented Mar 19, 2019

With that change timestamp values now reflect the correct time. However, the geofence events are still not logged until app is reopened.

The log for this last run is available here. For this run I did not persist location, only geofence.

@joserocha3
Copy link
Contributor Author

joserocha3 commented Mar 19, 2019

With further observation the http requests for the geofence events is performed in the background, just not in perfect real-time. If I wait a minute eventually the request is sent for a group of pending events at once while in background. If I open the app it sends them all at that time. I have autoSync: true set.

@christocracy
Copy link
Member

I see you closed this? it's no issue?

@joserocha3
Copy link
Contributor Author

Once I set the listeners before calling ready the timestamp issue was fixed.

After that there was a slight delay in the time the data was appearing on my server, but I was think I just being impatient. Will keep monitoring and reopen if needed.

Thanks for checking up on this issue.

@christocracy
Copy link
Member

Next beta arriving soon.

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

2 participants