flutter_background_geolocation icon indicating copy to clipboard operation
flutter_background_geolocation copied to clipboard

Not sending location in background, Not working Properly

Open adn9990 opened this issue 2 years ago • 17 comments

Your Environment

  • Plugin version:
  • Platform: Android
  • OS version: 12
  • Device manufacturer / model: Nothing 1
  • Flutter info (flutter doctor):
  • Plugin config:
PASTE_YOUR_CODE_HERE

Expected Behavior

Should work in background Should send location automatically

Actual Behavior

SomeTime It works somwtime not

Steps to Reproduce

Context

Debug logs

Logs
PASTE_YOUR_LOGS_HERE

Implemented and tested this plugin on multiple devices and found some issues.

  1. Suppose went 10KM, this plugin got location for 2- 3 Km and then its stopped sending location until I open the app or automatically starts after sometime.
  2. Sometime It send multiple location within a sec or within some seconds. Our setting for headless task is 5 min and motion filter distance in 1 KM but still it sends 5-10 location in a sec,

adn9990 avatar Dec 27 '22 09:12 adn9990

Something Like This image

image Got 2 location at same time and another one in some seconds, How can I stop this.

adn9990 avatar Dec 27 '22 14:12 adn9990

Plugin version: Device manufacturer / model: Flutter info (flutter doctor): Plugin config:

christocracy avatar Dec 27 '22 14:12 christocracy

Plugin version: Device manufacturer / model: Flutter info (flutter doctor): Plugin config:

your screenshots are unhelpful.

christocracy avatar Dec 27 '22 15:12 christocracy

Plugin version: background_fetch: ^1.1.2 flutter_background_geolocation: ^4.8.5

Device manufacturer / model: Nothing Phone 1

Flutter info (flutter doctor):

Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.3.10, on macOS 12.6 21G115 darwin-x64, locale en-IN)
[✓] Android toolchain - develop for Android devices (Android SDK version 33.0.0)
[✗] Xcode - develop for iOS and macOS
    ✗ Xcode installation is incomplete; a full installation is necessary for iOS development.
      Download at: https://developer.apple.com/xcode/download/
      Or install Xcode via the App Store.
      Once installed, run:
        sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer
        sudo xcodebuild -runFirstLaunch
    ✗ CocoaPods not installed.
        CocoaPods is used to retrieve the iOS and macOS platform side's plugin code that responds to your plugin
        usage on the Dart side.
        Without CocoaPods, plugins will not work on iOS or macOS.
        For more info, see https://flutter.dev/platform-plugins
      To install see https://guides.cocoapods.org/using/getting-started.html#installation for instructions.
[✓] Chrome - develop for the web
[✓] Android Studio (version 2021.3)
[✓] VS Code (version 1.72.2)
[✓] VS Code (version 1.72.2)
[✓] Connected device (3 available)
[✓] HTTP Host Availability

! Doctor found issues in 1 category.

Plugin config:

'''

void _configureBackgroundGeolocation() async { // 1. Listen to events (See docs for all 13 available events). bg.BackgroundGeolocation.onLocation(_onLocation, _onLocationError); bg.BackgroundGeolocation.onMotionChange(_onMotionChange); bg.BackgroundGeolocation.onActivityChange(_onActivityChange); bg.BackgroundGeolocation.onProviderChange(_onProviderChange); bg.BackgroundGeolocation.onHttp(_onHttp); bg.BackgroundGeolocation.onConnectivityChange(_onConnectivityChange); bg.BackgroundGeolocation.onHeartbeat(_onHeartbeat); bg.BackgroundGeolocation.onGeofence(_onGeofence); bg.BackgroundGeolocation.onSchedule(_onSchedule); bg.BackgroundGeolocation.onPowerSaveChange(_onPowerSaveChange); bg.BackgroundGeolocation.onEnabledChange(_onEnabledChange); bg.BackgroundGeolocation.onNotificationAction(_onNotificationAction);

// bg.TransistorAuthorizationToken token =
//     await bg.TransistorAuthorizationToken.findOrCreate(
//         orgname, username, ENV.TRACKER_HOST);

// 2.  Configure the plugin
bg.BackgroundGeolocation.ready(bg.Config(
        // preventSuspend: true,
        // showsBackgroundLocationIndicator: true,
        reset:
            false, // <-- lets the Settings screen drive the config rather than re-applying each boot.
        // Convenience option to automatically configure the SDK to post to Transistor Demo server.
        // transistorAuthorizationToken: token,
        // Logging & Debug
        debug: true,
        logLevel: bg.Config.LOG_LEVEL_VERBOSE,
        // Geolocation options
        desiredAccuracy: bg.Config.DESIRED_ACCURACY_NAVIGATION,
        distanceFilter: 500.0,
        // Activity recognition options
        stopTimeout: 5,
        backgroundPermissionRationale: bg.PermissionRationale(
            title:
                "Allow {applicationName} to access this device's location even when the app is closed or not in use.",
            message:
                "This app collects location data to enable recording your trips to work and calculate distance-travelled.",
            positiveAction: 'Change to "{backgroundPermissionOptionLabel}"',
            negativeAction: 'Cancel'),
        // HTTP & Persistence
        autoSync: false,
        // Application options
        stopOnTerminate: false,
        // motionTriggerDelay: 300000,
        startOnBoot: true,
        enableHeadless: true,
        heartbeatInterval: 300))
    .then((bg.State state) async {
  print('[ready] ${state.toMap()}');
  print('[didDeviceReboot] ${state.didDeviceReboot}');
  if (state.schedule!.isNotEmpty) {
    bg.BackgroundGeolocation.startSchedule();
  }
  // setState(() {
  //   _enabled = state.enabled;
  //   _isMoving = state.isMoving;
  // });
}).catchError((error) {
  print('[ready] ERROR: $error');
});

// Fetch currently selected tab.
// SharedPreferences prefs = await _prefs;
// int tabIndex = prefs.getInt("tabIndex");

// Which tab to view?  MapView || EventList.   Must wait until after build before switching tab or bad things happen.
// WidgetsBinding.instance.addPostFrameCallback((_) {
//   if (tabIndex != null) {
//     _tabController.animateTo(tabIndex);
//   }
// });

}

// Configure BackgroundFetch (not required by BackgroundGeolocation). void _configureBackgroundFetch() async { BackgroundFetch.configure( BackgroundFetchConfig( minimumFetchInterval: 15, startOnBoot: true, stopOnTerminate: false, enableHeadless: true, requiresStorageNotLow: false, requiresBatteryNotLow: false, requiresCharging: false, requiresDeviceIdle: false, requiredNetworkType: NetworkType.NONE), (String taskId) async { print("[BackgroundFetch] received event $taskId");

  SharedPreferences prefs = await SharedPreferences.getInstance();
  // int count = 0;
  // if (prefs.get("fetch-count") != null) {
  //   // count = prefs.getInt("fetch-count");
  // }
  // prefs.setInt("fetch-count", ++count);
  // print('[BackgroundFetch] count: $count');

  if (taskId == 'flutter_background_fetch') {
    try {
      // Fetch current position
      var location = await bg.BackgroundGeolocation.getCurrentPosition(
          samples: 1,
          extras: {"event": "background-fetch", "headless": true});
      print("[location] $location");
      storingLocationDataLocally();
    } catch (error) {
      print("[location] ERROR: $error");
    }

    // Test scheduling a custom-task in fetch event.
    BackgroundFetch.scheduleTask(TaskConfig(
        taskId: "com.transistorsoft.customtask",
        delay: 5000,
        periodic: true,
        forceAlarmManager: true,
        stopOnTerminate: false,
        enableHeadless: true));
  }
  BackgroundFetch.finish(taskId);
});

}

'''


  void _onLocation(bg.Location location) {
    print('[${bg.Event.LOCATION}] - $location');
  }

  void _onLocationError(bg.LocationError error) {
    print('[${bg.Event.LOCATION}] ERROR - $error');
  }

  void _onMotionChange(bg.Location location) {
    print('[${bg.Event.MOTIONCHANGE}] - $location');
    storingLocationDataLocally();
  }

  void _onActivityChange(bg.ActivityChangeEvent event) {
    print('[${bg.Event.ACTIVITYCHANGE}] - $event');
    storingLocationDataLocally();
  }

  void _onProviderChange(bg.ProviderChangeEvent event) async {
    print('[${bg.Event.PROVIDERCHANGE}] - $event');

    if ((event.status == bg.ProviderChangeEvent.AUTHORIZATION_STATUS_ALWAYS) &&
        (event.accuracyAuthorization ==
            bg.ProviderChangeEvent.ACCURACY_AUTHORIZATION_REDUCED)) {
      // Supply "Purpose" key from Info.plist as 1st argument.
      bg.BackgroundGeolocation.requestTemporaryFullAccuracy("DemoPurpose")
          .then((int accuracyAuthorization) {
        if (accuracyAuthorization ==
            bg.ProviderChangeEvent.ACCURACY_AUTHORIZATION_FULL) {
          print(
              "[requestTemporaryFullAccuracy] GRANTED:  $accuracyAuthorization");
        } else {
          print(
              "[requestTemporaryFullAccuracy] DENIED:  $accuracyAuthorization");
        }
      }).catchError((error) {
        print("[requestTemporaryFullAccuracy] FAILED TO SHOW DIALOG: $error");
      });
    }
  }

  void _onHttp(bg.HttpEvent event) async {
    print('[${bg.Event.HTTP}] - $event');
  }

  void _onConnectivityChange(bg.ConnectivityChangeEvent event) {
    print('[${bg.Event.CONNECTIVITYCHANGE}] - $event');
  }

  void _onHeartbeat(bg.HeartbeatEvent event) {
    print('[${bg.Event.HEARTBEAT}] -JOKER I HIGH- $event');
    storingLocationDataLocally();
  }

  void _onGeofence(bg.GeofenceEvent event) async {
    print('[${bg.Event.GEOFENCE}] - $event');

    // bg.BackgroundGeolocation.startBackgroundTask().then((int taskId) async {
    //   // Execute an HTTP request to test an async operation completes.
    //   String url = "${ENV.TRACKER_HOST}/api/devices";
    //   bg.State state = await bg.BackgroundGeolocation.state;
    //   http.read(Uri.parse(url), headers: {
    //     "Authorization": "Bearer ${state.authorization.accessToken}"
    //   }).then((String result) {
    //     print("[http test] success: $result");
    //     bg.BackgroundGeolocation.playSound(
    //         util.Dialog.getSoundId("TEST_MODE_CLICK"));
    //     bg.BackgroundGeolocation.stopBackgroundTask(taskId);
    //   }).catchError((dynamic error) {
    //     print("[http test] failed: $error");
    //     bg.BackgroundGeolocation.stopBackgroundTask(taskId);
    //   });
    // });

    // setState(() {
    //   events.insert(
    //       0, Event(bg.Event.GEOFENCE, event, event.toString(compact: false)));
    // });
  }

  void _onSchedule(bg.State state) {
    print('[${bg.Event.SCHEDULE}] - $state');
  }

  void _onPowerSaveChange(bool enabled) {
    print('[${bg.Event.POWERSAVECHANGE}] - $enabled');
    // setState(() {
    //   events.insert(
    //       0,
    //       Event(bg.Event.POWERSAVECHANGE, enabled,
    //           'Power-saving enabled: $enabled'));
    // });
  }

  void _onEnabledChange(bool enabled) {
    print('[${bg.Event.ENABLEDCHANGE}] - $enabled');
    // setState(() {
    //   _enabled = enabled;
    //   events.clear();
    //   events.insert(
    //       0,
    //       Event(bg.Event.ENABLEDCHANGE, enabled,
    //           '[EnabledChangeEvent enabled: $enabled]'));
    // });
  }

  void _onNotificationAction(String action) {
    print('[onNotificationAction] $action');
    switch (action) {
      case 'notificationButtonFoo':
        bg.BackgroundGeolocation.changePace(false);
        break;
      case 'notificationButtonBar':
        break;
    }
  }
/// Receive events from BackgroundGeolocation in Headless state.
@pragma('vm:entry-point')
void backgroundGeolocationHeadlessTask(bg.HeadlessEvent headlessEvent) async {
  print('📬 --> $headlessEvent');

  switch (headlessEvent.name) {
    case bg.Event.BOOT:
      bg.State state = await bg.BackgroundGeolocation.state;
      print("📬 didDeviceReboot: ${state.didDeviceReboot}");
      break;
    case bg.Event.TERMINATE:
      try {
        bg.Location location =
            await bg.BackgroundGeolocation.getCurrentPosition(
                samples: 1, extras: {"event": "terminate", "headless": true});
        storingLocationDataLocally();
        print("[getCurrentPosition] Headless: $location");
      } catch (error) {
        print("[getCurrentPosition] Headless ERROR: $error");
      }

      break;
    case bg.Event.HEARTBEAT:
      storingLocationDataLocally();
      /* DISABLED getCurrentPosition on heartbeat
      try {
        bg.Location location = await bg.BackgroundGeolocation.getCurrentPosition(
          samples: 1,
          extras: {
            "event": "heartbeat",
            "headless": true
          }
        );
        print('[getCurrentPosition] Headless: $location');
      } catch (error) {
        print('[getCurrentPosition] Headless ERROR: $error');
      }
      */
      break;
    case bg.Event.LOCATION:
      bg.Location location = headlessEvent.event;
      print(location);
      break;
    case bg.Event.MOTIONCHANGE:
      bg.Location location = headlessEvent.event;
      storingLocationDataLocally();
      print(location);
      break;
    case bg.Event.GEOFENCE:
      bg.GeofenceEvent geofenceEvent = headlessEvent.event;
      print(geofenceEvent);
      break;
    case bg.Event.GEOFENCESCHANGE:
      bg.GeofencesChangeEvent event = headlessEvent.event;
      print(event);
      break;
    case bg.Event.SCHEDULE:
      bg.State state = headlessEvent.event;
      print(state);
      break;
    case bg.Event.ACTIVITYCHANGE:
      bg.ActivityChangeEvent event = headlessEvent.event;
      storingLocationDataLocally();
      print(event);
      break;
    case bg.Event.HTTP:
      bg.HttpEvent response = headlessEvent.event;
      print(response);
      break;
    case bg.Event.POWERSAVECHANGE:
      bool enabled = headlessEvent.event;
      print(enabled);
      break;
    case bg.Event.CONNECTIVITYCHANGE:
      bg.ConnectivityChangeEvent event = headlessEvent.event;
      print(event);
      break;
    case bg.Event.ENABLEDCHANGE:
      bool enabled = headlessEvent.event;
      print(enabled);
      break;
    case bg.Event.AUTHORIZATION:
      bg.AuthorizationEvent event = headlessEvent.event;
      print(event);
      // bg.BackgroundGeolocation.setConfig(
      //     bg.Config(url: "${ENV.TRACKER_HOST}/api/locations"));
      break;
  }
}

/// Receive events from BackgroundFetch in Headless state.
@pragma('vm:entry-point')
void backgroundFetchHeadlessTask(HeadlessTask task) async {
  String taskId = task.taskId;

  // Is this a background_fetch timeout event?  If so, simply #finish and bail-out.
  if (task.timeout) {
    print("[BackgroundFetch] HeadlessTask TIMEOUT: $taskId");
    BackgroundFetch.finish(taskId);
    return;
  }

  print("[BackgroundFetch] HeadlessTask: $taskId");

  try {
    var location = await bg.BackgroundGeolocation.getCurrentPosition(
        samples: 1, extras: {"event": "background-fetch", "headless": true});
    print("[location] $location");
  } catch (error) {
    print("[location] ERROR: $error");
  }

  BackgroundFetch.finish(taskId);
}

Calling below code in main function of main.,dart

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

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

adn9990 avatar Dec 28 '22 10:12 adn9990

See https://dontkillmyapp.com

christocracy avatar Dec 28 '22 11:12 christocracy

For this issue https://dontkillmyapp.com will not help Sometime It send multiple location within a sec or within some seconds. Our setting for headless task is 5 min and motion filter distance in 1 KM but still it sends 5-10 location in a sec,

adn9990 avatar Dec 28 '22 11:12 adn9990

I se you basically copy/pasted code from the /example app, that code is far more complex than what you need.

i suspect you don’t k know what this does:

reset false,

that option is set specifically for the /example app and not meant to be used by 99% of people.

christocracy avatar Dec 28 '22 11:12 christocracy

can you please tell me the use of reset false, yes we picked up code from example app, please tell us if there is any changes required to make location setting smooth and bug free, we need location at minimum of 5 min of difference

adn9990 avatar Dec 28 '22 13:12 adn9990

If you don’t know what it is, you don’t need it. You likely never will need it.

delete it from your Config then go outside and do more field testing.

christocracy avatar Dec 28 '22 13:12 christocracy

is there any changes required to make it smoother

adn9990 avatar Dec 28 '22 13:12 adn9990

Dear @christocracy , This is not just a question from one person. Many users will need to know how to get along smoothly. A simple example with just enough materials to do a basic background location collection would have been more helpful. As a developer, we hardly ever get time to go through the full documentation and understand what is happening and what to do to meet specific use cases. Apart from that, this is a wonderful plugin! In addition to it, testing this in the provided node.js application is just Awesome! All the features of geo-fencing are just mind-blowing! I don't think any other plugin provides this many features. Thanks again

anoop-tm avatar Feb 26 '23 16:02 anoop-tm

A simple example with just enough materials to do a basic background location collection would have been more helpful

The default configuration of the plug-in is sufficient to track location.

This is a perfectly sufficient config:

BackgroundGeolocation.ready(Config(
  stopOnTerminate: false,
  startOnBoot: true
))

This Config will track a device every where it goes, regardless if app terminated or device rebooted, with the default distanceFilter: 10 and highest possible accuracy.

If there are tracking problems, it is due to the device (see https://dontkillmyapp.com), not due to some missing Config option.

christocracy avatar Feb 26 '23 17:02 christocracy

Hi Chris

If there are tracking problems, it is due to the device (see https://dontkillmyapp.com), not due to some missing Config option.

Does that mean Samsung users with a new Android version will by default get their app's background process shut down if app was terminated for a few days?

n-kulic avatar Apr 02 '23 22:04 n-kulic

Does that mean Samsung users

I have no idea. This plug-in is designed to work with the documented Android api. I have no control over how particular Android manufacturers violate the Android api by imposing undocumented restrictions.

christocracy avatar Apr 02 '23 22:04 christocracy

Does that mean Samsung users

I have no idea. This plug-in is designed to work with the documented Android api. I have no control over how particular Android manufacturers violate the Android api by imposing undocumented restrictions.

Are you aware of some Android models which stop headless task after app is killed or you didn't had problems with it yet?

n-kulic avatar Apr 02 '23 22:04 n-kulic

Are you aware of some Android models

I don’t keep track of what particular manufacturers do. I develop against the public Android api docs and develop primarily on Pixel devices. My test devices have Settings applied according to https://dontkillmyapp.com

I don’t test what doesn’t work.

christocracy avatar Apr 02 '23 22:04 christocracy

Thanks for answer

n-kulic avatar Apr 02 '23 23:04 n-kulic

This issue is stale because it has been open for 30 days with no activity.

github-actions[bot] avatar May 21 '24 01:05 github-actions[bot]

This issue was closed because it has been inactive for 14 days since being marked as stale.

github-actions[bot] avatar Jun 04 '24 01:06 github-actions[bot]