transistorsoft / flutter_background_geolocation

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

Not getting regular location devices #1077

Closed chirag-chopra closed 4 months ago

chirag-chopra commented 1 year ago

Your Environment

• No issues found!

/// Receive events from BackgroundGeolocation in Headless state. @pragma('vm:entry-point') void backgroundGeolocationHeadlessTask(bg.HeadlessEvent headlessEvent) async { 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: break; case bg.Event.LOCATION: bg.Location location = headlessEvent.event; print(location); break; case bg.Event.MOTIONCHANGE: bg.Location location = headlessEvent.event; 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; print(event); break; case bg.Event.HTTP: bg.HttpEvent event = headlessEvent.event; print(event); 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: "https://example.com/AddbackgroundLocation", headers: {"Authorization": "Bearer ${prefs!.getString('token')}"})); 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) { BackgroundFetch.finish(taskId); return; }

// print("[BackgroundFetch] HeadlessTask: $taskId"); try { await bg.BackgroundGeolocation.getCurrentPosition( samples: 1, timeout: 30, extras: {"event": "background-fetch", "headless": true}) .then((value) { print("[location] $value"); }) .onError((error, stackTrace) {}) .catchError((error) {}); } catch (error) { print("[location] ERROR: $error"); }

// 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');

BackgroundFetch.finish(taskId); }

Future sendMyLocation() async { bool isLocService = await Permission.locationWhenInUse.serviceStatus.isEnabled; if (isLocService) { bg.BackgroundGeolocation.getCurrentPosition(samples: 1, timeout: 30) .then((value) async { List placemarks = await placemarkFromCoordinates( value.coords.latitude, value.coords.longitude); final String finalAddress = "${placemarks[0].name}, ${placemarks[0].subLocality}, ${placemarks[0].thoroughfare}, ${placemarks[0].locality}, ${placemarks[0].postalCode}, ${placemarks[0].administrativeArea}, ${placemarks[0].country}";

  http.Response resp = await http
      .post(Uri.parse('${baseUrl}Sendmylocation'),
          body: jsonEncode({
            "locationtime":
                DateTime.now().toIso8601String().replaceAll(' ', ''),
            "latitude": value.coords.latitude.toStringAsPrecision(6),
            "longitude": value.coords.longitude.toStringAsPrecision(6),
            "address": finalAddress,
            "activity_Type": value.activity.type,
            "activity_confidence": value.activity.confidence,
            "battery_Level": (value.battery.level * 100).toInt(),
            "gpSenabled": true,
            "battery_is_charging": value.battery.isCharging,
            "is_Moving": value.isMoving,
            "mocklocation": value.mock,
            "speed": value.coords.speed,
            "accuracy": value.coords.accuracy.toPrecision(2),
            "speed_Accuracy": value.coords.speedAccuracy,
            "heading": value.coords.heading,
            "heading_Accuracy": value.coords.headingAccuracy,
            "altitude": value.coords.altitude,
            "altitude_Accuracy": value.coords.altitudeAccuracy,
            "odometer": value.odometer,
            "unid": 0
          }),
          headers: authHeader)
      .catchError((error) {
    CustomLocationStartdayError();
  });

  if (resp.statusCode != 200) {
    Get.back();
  }
}).onError((error, stackTrace) {});

} }

finishYourDayInBg() async { SharedPreferences prefs = await SharedPreferences.getInstance(); // check if location ON or OFF bool isLocService = await Permission.locationWhenInUse.serviceStatus.isEnabled; if (isLocService) { bg.BackgroundGeolocation.getCurrentPosition(samples: 1, timeout: 30) .then((value) async { List placemarks = await placemarkFromCoordinates( value.coords.latitude, value.coords.longitude); final finalAddress = "${placemarks[0].name}, ${placemarks[0].subLocality}, ${placemarks[0].thoroughfare}, ${placemarks[0].locality}, ${placemarks[0].postalCode}, ${placemarks[0].administrativeArea}, ${placemarks[0].country}";

  http.Response resp = await http.post(Uri.parse('${baseUrl}Finishday'),
      body: jsonEncode({
        "loginid": prefs.getString('loginID') ?? '',
        "latitude": value.coords.latitude,
        "longitude": value.coords.longitude,
        "location": finalAddress,
        "batterylevel": (value.battery.level * 100).toInt(),
        "endtime": DateTime.now().toIso8601String().replaceAll(' ', ''),
        "endgpsenbled": true,
        "endlocationaccuracy": value.coords.accuracy.toPrecision(2),
        "endmocklocationenabled": value.mock
      }),
      headers: {
        "Accept": "application/json",
        "Content-Type": "application/json",
        "Authorization": "Bearer ${prefs.getString('token')}"
      });
  if (resp.statusCode == 200) {}
}).onError((error, stackTrace) {});

} }

@pragma('vm:entry-point') Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async { if (Firebase.apps.isEmpty) { await Firebase.initializeApp(); }

// finish if (message.notification!.body!.toLowerCase() == 'your day is finished') { finishYourDayInBg(); }

// location if (message.notification!.body!.toLowerCase() == 'location') { // final SharedPreferences prefs = await SharedPreferences.getInstance(); sendMyLocation(); }

// will recieve after logout if (message.notification!.body!.toLowerCase() == 'locStart') { // final SharedPreferences prefs = await SharedPreferences.getInstance(); BackgroundFetch.start(); }

// will recieve after logout if (message.notification!.body!.toLowerCase() == 'logout') { // final SharedPreferences prefs = await SharedPreferences.getInstance(); BackgroundFetch.stop(); bg.BackgroundGeolocation.stop(); await bg.BackgroundGeolocation.removeListeners(); // ========================= LOGOUT STUFF START ==================== SharedPreferences prefs = await SharedPreferences.getInstance(); await prefs.clear(); Directory oneTry = await getTemporaryDirectory(); var twoTry;

if (Platform.isAndroid) {
  twoTry = await getExternalStorageDirectory();
} else {
  twoTry = await getApplicationDocumentsDirectory();
}
List<Directory>? threeTry = await getExternalCacheDirectories();
Directory fourTry = await getApplicationSupportDirectory();
Directory fiveTry = await getApplicationDocumentsDirectory();

if (oneTry.existsSync()) {
  oneTry.deleteSync(recursive: true);
}

if (twoTry != null) {
  if (twoTry.existsSync()) {
    twoTry.deleteSync(recursive: true);
  }
}

if (threeTry != null) {
  if (threeTry[0].existsSync()) {
    fourTry.deleteSync(recursive: true);
  }
}

if (fourTry.existsSync()) {
  fourTry.deleteSync(recursive: true);
}

if (fiveTry.existsSync()) {
  fiveTry.deleteSync(recursive: true);
}
await isar!.writeTxn(() async {
  isar!.dashboards.clear();
  isar!.startdays.clear();
  isar!.markets.clear();
  isar!.distributors.clear();
  isar!.retailers.clear();
  isar!.retailerClass.clear();
  isar!.retailerTypes.clear();
  isar!.products.clear();
  isar!.categorys.clear();
  isar!.nonPCallReasons.clear();
  isar!.orderBookings.clear();
  isar!.orderDetails.clear();
  isar!.orderRetailerStocks.clear();
  isar!.orderStockDetails.clear();
  isar!.promotionalActivitys.clear();
  isar!.promotionalActivityDetails.clear();
  isar!.otherActivityTypes.clear();
  isar!.otherActivityTypeDropdowns.clear();
  isar!.meetings.clear();
  isar!.drawers.clear();
});
// ========================= LOGOUT STUFF END ======================

}

if (message.notification!.body!.toLowerCase() == 'your day is about to finish') { BackgroundFetch.stop(); bg.BackgroundGeolocation.stop(); await bg.BackgroundGeolocation.removeListeners(); } }

void main() async { WidgetsFlutterBinding.ensureInitialized(); SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); HttpOverrides.global = PostHttpOverrides(); camerass = await availableCameras(); prefs = await SharedPreferences.getInstance(); // firebase setup await Firebase.initializeApp(); // 2. Instantiate Firebase Messaging FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterFatalError; PlatformDispatcher.instance.onError = (error, stack) { FirebaseCrashlytics.instance.recordError(error, stack, fatal: true); return true; };

Directory dir = Platform.isAndroid ? await getApplicationSupportDirectory() : await getApplicationDocumentsDirectory();

isar = await Isar.open([ DashboardSchema, StartdaySchema, DrawerSchema, MarketSchema, DistributorSchema, RetailerSchema, RetailerClassSchema, RetailerTypeSchema, ProductSchema, CategorySchema, NonPCallReasonSchema, OrderBookingSchema, OrderDetailSchema, OrderRetailerStockSchema, OrderStockDetailSchema, PromotionalActivitySchema, PromotionalActivityDetailSchema, OtherActivityTypeSchema, OtherActivityTypeDropdownSchema, MeetingSchema, MeetingTypeSchema, LeadStatusSchema, ], directory: dir.path);

if (await checkIfInternetWorking() == true) { OnInitStart().storeStartdayItems(); } fbMessaging = FirebaseMessaging.instance; // Register BackgroundGeolocation headless-task. bg.BackgroundGeolocation.registerHeadlessTask( backgroundGeolocationHeadlessTask); BackgroundFetch.registerHeadlessTask(backgroundFetchHeadlessTask); FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); runApp(const MyApp()); fbMessaging!.setForegroundNotificationPresentationOptions( alert: true, badge: true, sound: true); // 3. On iOS, this helps to take the user permissions NotificationSettings settings = await fbMessaging!.requestPermission( alert: true, badge: true, provisional: false, sound: true); if (settings.authorizationStatus == AuthorizationStatus.authorized) { // For handling the received notifications FirebaseMessaging.onMessage.listen((RemoteMessage message) async { if (message.notification!.body!.toLowerCase() == 'logout') { BackgroundFetch.stop(); bg.BackgroundGeolocation.stop(); await bg.BackgroundGeolocation.removeListeners(); // ========================= LOGOUT STUFF START ==================== SharedPreferences prefs = await SharedPreferences.getInstance(); await prefs.clear(); Directory oneTry = await getTemporaryDirectory(); var twoTry; if (Platform.isAndroid) { twoTry = await getExternalStorageDirectory(); } else { twoTry = await getApplicationDocumentsDirectory(); } List? threeTry = await getExternalCacheDirectories(); Directory fourTry = await getApplicationSupportDirectory(); Directory fiveTry = await getApplicationDocumentsDirectory(); if (oneTry.existsSync()) { oneTry.deleteSync(recursive: true); } if (twoTry != null) { if (twoTry.existsSync()) { twoTry.deleteSync(recursive: true); } } if (threeTry != null) { if (threeTry[0].existsSync()) { fourTry.deleteSync(recursive: true); } } if (fourTry.existsSync()) { fourTry.deleteSync(recursive: true); } if (fiveTry.existsSync()) { fiveTry.deleteSync(recursive: true); } await isar!.writeTxn(() async { isar!.dashboards.clear(); isar!.startdays.clear(); isar!.markets.clear(); isar!.distributors.clear(); isar!.retailers.clear(); isar!.retailerClass.clear(); isar!.retailerTypes.clear(); isar!.products.clear(); isar!.categorys.clear(); isar!.nonPCallReasons.clear(); isar!.orderBookings.clear(); isar!.orderDetails.clear(); isar!.orderRetailerStocks.clear(); isar!.orderStockDetails.clear(); isar!.promotionalActivitys.clear(); isar!.promotionalActivityDetails.clear(); isar!.otherActivityTypes.clear(); isar!.otherActivityTypeDropdowns.clear(); isar!.meetings.clear(); isar!.drawers.clear(); }); Get.off(() => const LoginScreen()); CustomSuccessSnackBar(txt: 'User is Logout'); // ========================= LOGOUT STUFF END ====================== } // will recieve after logout if (message.notification!.body!.toLowerCase() == 'locStart') { BackgroundFetch.start(); } if (message.notification!.body!.toLowerCase() != 'logout' && message.notification!.body!.toLowerCase() != 'locStart' && message.notification!.body!.toLowerCase() != 'location') { // RemoteNotification notification = message.notification!; // AppleNotification apple = event.notification!.apple!; // AndroidNotification androidNotification = // message.notification!.android!;

    // sendNotification(title: notification.title, body: notification.body);
  }
  if (message.notification!.body!.toLowerCase() ==
      'your day is about to finish') {
    BackgroundFetch.stop();
  }
  // finish
  if (message.notification!.body!.toLowerCase() == 'your day is finished') {
    finishYourDay();
  }
});

} }

class MyApp extends StatefulWidget { const MyApp({super.key}); @override State createState() => _MyAppState(); }

class _MyAppState extends State with WidgetsBindingObserver { // Platform messages are asynchronous, so we initialize in an async method. Future initPlatformState() async { // Configure BackgroundFetch. int status = await BackgroundFetch.configure( BackgroundFetchConfig( forceAlarmManager: true, minimumFetchInterval: 15, stopOnTerminate: false, enableHeadless: true, startOnBoot: true, requiresBatteryNotLow: false, requiresCharging: false, requiresStorageNotLow: false, requiresDeviceIdle: false, requiredNetworkType: NetworkType.NONE), (String taskId) async { 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');

// await BackgroundFetch.scheduleTask(TaskConfig(
//     taskId: 'stop_app_service', delay: 120000, forceAlarmManager: true));

// 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.odometer}} **** [location] - $location');
    // storingLocationDataLocally();
  },
  (error) {},
);

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: 60,
    url:
        "https://example.com/AddbackgroundLocation",
    headers: {"Authorization": "Bearer ${prefs!.getString('token')}"},
    desiredAccuracy: bg.Config.DESIRED_ACCURACY_HIGH,
    distanceFilter: 150,
    // scheduleUseAlarmManager: true,
    // schedule: ['1-7 21:19-21:20'],
    stopOnTerminate: false,
    disableElasticity: false,
    elasticityMultiplier: 1,
    desiredOdometerAccuracy: 100,
    maxDaysToPersist: 3,
    locationUpdateInterval: 1000,
    stopTimeout: 5,
    disableMotionActivityUpdates: false,
    disableStopDetection: false,
    motionTriggerDelay: 0,
    autoSync: true,
    disableAutoSyncOnCellular: false,
    persistMode: 1,
    preventSuspend: true,
    notificationPriority: 1,
    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'),
    notificationLargeIcon: "drawable/ic_stat_myapplogo",
    notificationSmallIcon: "drawable/ic_stat_myapplogo",
    notification: bg.Notification(
        title: 'MyApp',
        text: 'new app',
        priority: bg.Config.NOTIFICATION_PRIORITY_HIGH,
        sticky: true,
        smallIcon:
            "drawable/ic_stat_myapplogo", // <-- defaults to app icon
        largeIcon: "drawable/ic_stat_myapplogo",
        channelId: 'my_channel_id',
        actions: [
          // "notificationButtonFoo", "notificationButtonBar"
        ])));
initPlatformState();

}

// @override // void dispose() { // WidgetsBinding.instance.removeObserver(this); // super.dispose(); // }

@override void didChangeAppLifecycleState(AppLifecycleState state) async { if (state == AppLifecycleState.resumed) { final SharedPreferences prefs = await SharedPreferences.getInstance(); if (prefs.containsKey('startdayTime')) { if (!DateTime.parse(prefs.getString('startdayTime')!).isToday()) { try { Get.offAll(() => const StartDayScreen()); } catch (e) { return; } } } } super.didChangeAppLifecycleState(state); }

@override Widget build(BuildContext context) { print('TOKEN : ${prefs!.getString('token')}'); return ScreenUtilInit( designSize: const Size(360, 800), minTextAdapt: true, splitScreenMode: false, useInheritedMediaQuery: true, builder: (context, snapshot) { return GetMaterialApp( // showPerformanceOverlay: true, // showSemanticsDebugger: true, // debugShowMaterialGrid: true, navigatorKey: navigatorKey, scaffoldMessengerKey: snackbarKey, debugShowCheckedModeBanner: false, theme: ThemeData( primarySwatch: Colors.blue, fontFamily: 'Montserrat'), home: const SplashScreen()); }); } }

Logs: Below Working mi working.txt

Below Working redmi working.txt

Below Not Working samsung not working.txt

Below Not Working vivo not working.txt


## Expected Behavior
Regular location should be updated.

## Actual Behavior
Regular location tracking is not working on some devices

## Steps to Reproduce
Just run the app with these configurations 

## Context
To get regular location updates

## Debug logs
<!-- include iOS / Android logs
- ios XCode logs,
- use #getLog #emailLog methods (@see docs)
- Android: $ adb logcat -s TSLocationManager
-->
<details>
    <summary>Logs</summary>

``` <!-- syntax-highligting:  DO NOT REMOVE -->

christocracy commented 1 year ago

For problems with certain Android devices, see https://dontkillmyapp.com

chirag-chopra commented 1 year ago

I have already checked and your example application downloaded from play store is working fine. There is something issue with code. Please check our log file. As we are also getting

Failed to validate the certificate chain, error: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
christocracy commented 1 year ago

Failed to validate the certificate chain, error: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.

Don’t use a “self-signed” certificate.

adn9990 commented 1 year ago

in Server side APIs? @christocracy

christocracy commented 1 year ago

Yes. If your server is using a "self-signed" certificate, you cannot use an https url. You must use http.

adn9990 commented 1 year ago

ohk, and have you checked vivo not working.txt log file. Why locations are not working on that device, is there any issue like this on vivo device or any other setup or configuration we need to do. Because example app downloaded from play store and checked on your server is working fine..

christocracy commented 1 year ago

There is nothing unusual in your Vivo logs. See https://dontkillmyapp.com

Then go outside and move > 1km.

adn9990 commented 1 year ago

already checked with the same, all the four devices are together

chirag-chopra commented 1 year ago

we have checked ssl certificate, our server is using lets encrypt and we are able to make all network calls, Our certificate and we have also implemented more then 20 api alls are working fine. Expect your background location in some devices @christocracy

github-actions[bot] commented 6 months ago

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

adn9990 commented 6 months ago

We are still facing this issue, @christocracy please reply

adn9990 commented 6 months ago

Response: 0, java.security.cert.CertPathValidatorException: Trust anchor for certification path not found. Even others http request are working fine

christocracy commented 6 months ago

This plugin uses OkHttp for its http client.

adn9990 commented 6 months ago

is there any solution available? @christocracy

christocracy commented 6 months ago

Is there anything in that thread which seems interesting to you?

I’m not hearing about this problem from anyone else.

carmaa commented 5 months ago

Any chance of introducing a Config property that would disable SSL/TLS certificate checking, say for enabling testing in dev environments that are using self-signed certs?

christocracy commented 5 months ago

I don’t know. Does OkHttp allow it?

carmaa commented 5 months ago

I don’t know. Does OkHttp allow it?

Don't know. But on second thought I'm just trusting the self-signed certs explicitly on my dev machine/device.

github-actions[bot] commented 4 months ago

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

github-actions[bot] commented 4 months ago

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