flutter_background_geolocation icon indicating copy to clipboard operation
flutter_background_geolocation copied to clipboard

On Change activity not working

Open adn9990 opened this issue 1 year ago • 9 comments

Your Environment

  • Plugin version: flutter_background_geolocation: ^4.9.0 , background_fetch: ^1.1.5
  • Platform: iOS or Android : ANDROID
  • OS version:
  • Device manufacturer / model: Motorola
  • Flutter info (flutter doctor): Doctor summary (to see all details, run flutter doctor -v): [✓] Flutter (Channel stable, 3.7.5, 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:
runApp(const MyApp());

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

  // Register to receive BackgroundFetch events after app is terminated.
  // Requires {stopOnTerminate: false, enableHeadless: true}
  BackgroundFetch.registerHeadlessTask(backgroundFetchHeadlessTask);
class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
  DateTime get _now => DateTime.now();
  // final data = GetStorage();

  // Platform messages are asynchronous, so we initialize in an async method.
  Future<void> initPlatformState() async {
    // Configure BackgroundFetch.
    int status = await BackgroundFetch.configure(
        BackgroundFetchConfig(
            minimumFetchInterval: 15,
            stopOnTerminate: false,
            enableHeadless: true,
            startOnBoot: true,
            requiresBatteryNotLow: false,
            requiresCharging: false,
            requiresStorageNotLow: false,
            requiresDeviceIdle: false,
            requiredNetworkType: NetworkType.NONE), (String taskId) async {
      // <-- Event handler
      // This is the fetch-event callback.
      print("[BackgroundFetch] Event received $taskId");
      // setState(() {
      //   _events.insert(0, new DateTime.now());
      // });
      // IMPORTANT:  You must signal completion of your task or the OS can punish your app
      // for taking too long in the background.
      BackgroundFetch.finish(taskId);
    }, (String taskId) async {
      // <-- Task timeout handler.
      // This task has exceeded its allowed running-time.  You must stop what you're doing and immediately .finish(taskId)
      print("[BackgroundFetch] TASK TIMEOUT taskId: $taskId");
      BackgroundFetch.finish(taskId);
    });
    print('[BackgroundFetch] configure success: $status');
    // setState(() {
    //   _status = status;
    // });

    // If the widget was removed from the tree while the asynchronous platform
    // message was in flight, we want to discard the reply rather than calling
    // setState to update our non-existent appearance.
    if (!mounted) return;
  }

  @override
  void initState() {
    super.initState();
    // WidgetsBinding.instance.addObserver(this);

    bg.BackgroundGeolocation.onHeartbeat((bg.HeartbeatEvent location) {
      print('[Heart Beat] - $location');
      storingLocationDataLocally();
    });

    // Fired whenever the plugin changes motion-state (stationary->moving and vice-versa)
    bg.BackgroundGeolocation.onMotionChange((bg.Location location) {
      print('[motionchange] - $location');
      storingLocationDataLocally();
    });

    // Fired whenever a location is recorded
    bg.BackgroundGeolocation.onLocation((bg.Location location) {
      print('[location] - $location');
      // storingLocationDataLocally();
    });

    bg.BackgroundGeolocation.onActivityChange(
        (bg.ActivityChangeEvent location) {
      print('[Activity Change] - $location');
      storingLocationDataLocally();
    });

    bg.BackgroundGeolocation.onConnectivityChange(
        (bg.ConnectivityChangeEvent location) {
      print('[Connectivity Change] - $location');
      storingLocationDataLocally();
    });

    ////
    // 2.  Configure the plugin
    //
    bg.BackgroundGeolocation.ready(bg.Config(
        enableHeadless: true,
        heartbeatInterval: 300,
        desiredAccuracy: bg.Config.DESIRED_ACCURACY_HIGH,
        distanceFilter: 100,
        stopOnTerminate: false,
        preventSuspend: true,
        startOnBoot: true,
        debug: false,
        logLevel: bg.Config.LOG_LEVEL_VERBOSE,
        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'),
        notification: bg.Notification(
            title: 'Getting Location',
            text: ' Location',
            priority: bg.Config.NOTIFICATION_PRIORITY_HIGH,
            sticky: true,
            layout: 'notification_layout',
            channelId: 'my_channel_id',
            actions: ["notificationButtonFoo", "notificationButtonBar"])));

    initPlatformState();
  }

  @override
  Widget build(BuildContext context) {
    print('TOKEN : ${prefs!.getString('token')}');

    return ScreenUtilInit(
        designSize: const Size(360, 800),
        minTextAdapt: true,
        splitScreenMode: false,
        builder: (context, snapshot) {
          return GetMaterialApp(
              navigatorKey: navigatorKey,
              scaffoldMessengerKey: snackbarKey,
              debugShowCheckedModeBanner: false,
              theme:
                  ThemeData(primarySwatch: Colors.blue, fontFamily: 'Poppins'),
              home: const SplashScreen());
        });
  }
}

Below code for configuring headless events


/// 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});
        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:
      storingLocationDataLocally();
      bg.Location location = headlessEvent.event;
      print(location);
      break;
    case bg.Event.ACTIVITYCHANGE:
      storingLocationDataLocally();
      bg.ActivityChangeEvent event = headlessEvent.event;
      print(event);
      break;
    case bg.Event.HTTP:
      bg.HttpEvent response = headlessEvent.event;
      print(response);
      break;
    case bg.Event.POWERSAVECHANGE:
      // storingLocationDataLocally();
      bool enabled = headlessEvent.event;
      print(enabled);
      break;
    case bg.Event.CONNECTIVITYCHANGE:
      storingLocationDataLocally();
      bg.ConnectivityChangeEvent event = headlessEvent.event;
      print(event);
      break;
    case bg.Event.ENABLEDCHANGE:
      // storingLocationDataLocally();
      bool enabled = headlessEvent.event;
      print(enabled);
      break;
    case bg.Event.AUTHORIZATION:
      bg.AuthorizationEvent event = headlessEvent.event;
      print(event);
      bg.BackgroundGeolocation.setConfig(bg.Config(url: ""));
      break;
  }
}
// [Android-only] This "Headless Task" is run when the Android app is terminated with `enableHeadless: true`
// Be sure to annotate your callback function to avoid issues in release mode on Flutter >= 3.3.0
@pragma('vm:entry-point')
void backgroundFetchHeadlessTask(HeadlessTask task) async {
  String taskId = task.taskId;
  bool isTimeout = task.timeout;
  if (isTimeout) {
    // This task has exceeded its allowed running-time.
    // You must stop what you're doing and immediately .finish(taskId)
    print("[BackgroundFetch] Headless task timed-out: $taskId");
    BackgroundFetch.finish(taskId);
    return;
  }
  print('[BackgroundFetch] Headless event received.');
  // Do your work here...
  BackgroundFetch.finish(taskId);
}

Using below code to run the plugin bg.BackgroundGeolocation.start();

PASTE_YOUR_CODE_HERE

Expected Behavior

App should collect and send data to server on regular on the basis of distance filter 100 meter.

Actual Behavior

App is not collecting and sending data while device/user moves from one location to another, this is working fine in example app.

Steps to Reproduce

Context

want to fetch and send location whenever user moves from location with distance filter 100 + with heartbeat also, even in headless mode.

Debug logs

Logs
D/TSLocationManager(16671):   🎾  ActivityRecognitionService [eventCount: 1]
D/TSLocationManager(16671): [c.t.l.s.ActivityRecognitionService handleActivityRecognitionResult]
D/TSLocationManager(16671):   🚘 ️DetectedActivity [type=STILL, confidence=100]
D/TSLocationManager(16671): [c.t.l.service.AbstractService finish] ⚙️︎  finish ActivityRecognitionService [eventCount: 0, sticky: false]
D/LongScreenshotUtil(16671): ScreenshotService already unbind, return
D/libMEOW (16671): meow new tls: 0xb400007879974240
D/libMEOW (16671): applied 1 plugins for [app.fieldmagna.com]:
D/libMEOW (16671):   plugin 1: [libMEOW_gift.so]: 0xb400007839736010
D/libMEOW (16671): rebuild call chain: 0xb400007879a142e0
D/libMEOW (16671): meow delete tls: 0xb400007879974240
D/TSLocationManager(16671): [c.t.l.a.TSLocationManagerActivity execute] locationsettings
D/TSLocationManager(16671): [c.t.l.adapter.TSConfig translateDesiredAccuracy] translateDesiredAccuracy (true): -1
I/TSLocationManager(16671): [c.t.l.s.TSScheduleManager cancelOneShot]
I/TSLocationManager(16671):   ⏰ Cancel OneShot: TERMINATE_EVENT
D/ViewRootImpl[TSLocationManagerActivity](16671): hardware acceleration = true, sRendererEnabled = true, forceHwAccelerated = false
E/TSLocationManager(16671): [c.t.l.s.ForegroundNotification buildCustomLayout]
E/TSLocationManager(16671):   ‼️  Could not find custom notification layout 'notification_layout' in app/src/main/res/layout
D/TSLocationManager(16671): [c.t.l.service.AbstractService start]
D/TSLocationManager(16671):   🎾  LocationRequestService [eventCount: 1]
D/TSLocationManager(16671): [c.t.l.a.TSLocationManagerActivity stop] eventCount: 0
I/TSLocationManager(16671): [c.t.l.s.LocationRequestService handleLocationResult]
I/TSLocationManager(16671): ╔═════════════════════════════════════════════
I/TSLocationManager(16671): ║ providerchange LocationResult: 444
I/TSLocationManager(16671): ╠═════════════════════════════════════════════
I/TSLocationManager(16671): ╟─ 📍  Location[fused 29.973115,76.852725 hAcc=7.0 et=+3d22h39m44s316ms alt=117.62453908 vAcc=0.1 vel=0.0 sAcc=0.01 bear=331.51056 bAcc=0.1 mock {Bundle[{}]}], age: 391ms, time: 1678176711896
I/TSLocationManager(16671): [c.t.l.l.TSLocationManager onSingleLocationResult]
I/TSLocationManager(16671):   🔵  Acquired providerchange position
I/BLASTBufferQueue(16671): [ViewRootImpl[TSLocationManagerActivity]#416] constructor()
D/TSLocationManager(16671): [c.t.l.s.LocationRequestService handleLocationResult] SingleLocationRequest 444 isFinished? true
D/TSLocationManager(16671): [c.t.l.service.AbstractService finish] ⚙️︎  finish LocationRequestService [eventCount: 0, sticky: false]
I/TSLocationManager(16671): [c.t.l.data.sqlite.b persist]
I/TSLocationManager(16671):   ✅  INSERT: ec2e24a9-cab8-4ce6-85b2-09c5d40bc19f
E/TSLocationManager(16671): [c.t.l.s.ForegroundNotification buildCustomLayout]
E/TSLocationManager(16671):   ‼️  Could not find custom notification layout 'notification_layout' in app/src/main/res/layout
D/TSLocationManager(16671): [c.t.l.service.AbstractService finish] ⚙️︎  finish LocationRequestService [eventCount: 0, sticky: false]
D/TSLocationManager(16671): [c.t.l.l.TSLocationManager$b onLocation]
D/TSLocationManager(16671):   ℹ️  Distance from last location: 14.104805
I/flutter (16671): [location] - [Location {odometer: 0.0, activity: {confidence: 100, type: still}, provider: {airplane: false, gps: true, accuracyAuthorization: 0, enabled: true, network: true, status: 3}, mock: true, extras: {}, event: providerchange, battery: {level: 1.0, is_charging: true}, uuid: ec2e24a9-cab8-4ce6-85b2-09c5d40bc19f, coords: {altitude: 117.6, heading: 331.51, latitude: 29.97311503, accuracy: 7.0, heading_accuracy: 0.1, altitude_accuracy: 0.1, speed_accuracy: 0.01, speed: 0.0, longitude: 76.85272545, ellipsoidal_altitude: 117.6}, is_moving: false, timestamp: 2023-03-07T08:11:51.896Z}]
I/TSLocationManager(16671): [c.t.l.s.TSScheduleManager oneShot]
I/TSLocationManager(16671):   ⏰ Scheduled OneShot: TERMINATE_EVENT in 10000ms (jobID: -1708771588)
D/TSLocationManager(16671): [c.t.l.a.TSLocationManagerActivity onDestroy]
I/BLASTBufferQueue(16671): [ViewRootImpl[TSLocationManagerActivity]#416] destructor()
D/View    (16671): [Warning] assignParent to null: this = DecorView@abfd614[TSLocationManagerActivity]
D/TSLocationManager(16671): [c.t.l.service.AbstractService onDestroy]
D/TSLocationManager(16671):   🔴  LocationRequestService stopped
D/TSLocationManager(16671): [c.t.l.service.AbstractService onDestroy]
D/TSLocationManager(16671):   🔴  ActivityRecognitionService stopped
I/TSLocationManager(16671): [c.t.l.scheduler.ScheduleEvent onOneShot]
I/TSLocationManager(16671): ╔═════════════════════════════════════════════
I/TSLocationManager(16671): ║ ⏰ OneShot event fired: TERMINATE_EVENT
I/TSLocationManager(16671): ╠═════════════════════════════════════════════
D/TSLocationManager(16671): [c.t.l.event.TerminateEvent$a a]
D/TSLocationManager(16671):   ℹ️  TERMINATE_EVENT ignored (MainActivity is still active).

adn9990 avatar Mar 07 '23 08:03 adn9990

layout: 'notification_layout',

16671): ‼️ Could not find custom notification layout 'notification_layout' in app/src/main/res/layout

If you don't know how to implement a custom notification layout (an advanced config option), don't use a custom layout.

christocracy avatar Mar 07 '23 14:03 christocracy

Working better, But not getting location while travelling. Like in demo app

adn9990 avatar Mar 13 '23 08:03 adn9990

The answer to why is written in the plug-in logs. See wiki "Debugging" and learn to observe the plug-in's incredibly verbose logs.

christocracy avatar Mar 13 '23 12:03 christocracy

Can you please tell me, I am not able to understand logs

adn9990 avatar Mar 13 '23 12:03 adn9990

Can you please tell me

Go fetch 1-3 days of logs and post them here. tell me what time period(s) you're interested in.

Also see https://dontkillmyapp.com

christocracy avatar Mar 13 '23 12:03 christocracy

Ohk, Let me fetch the logs. I want distance filter 100 if device is in motion and if device is still I want in every 5-20 min this is working fine in example

adn9990 avatar Mar 13 '23 13:03 adn9990

and if device is still I want in every 5-20 min. this is working fine in example

That's what BackgroundFetch is for: executing a periodic task, such as calling BackgroundGeolocation.getCurrentPosition().

https://github.com/transistorsoft/flutter_background_fetch

christocracy avatar Mar 13 '23 13:03 christocracy

periodic task is working fine, On issue when device is in motion location updates get stopped

adn9990 avatar Mar 13 '23 13:03 adn9990

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

github-actions[bot] avatar May 07 '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 May 22 '24 01:05 github-actions[bot]