react-native-background-geolocation icon indicating copy to clipboard operation
react-native-background-geolocation copied to clipboard

[Help Wanted]: Recording the location in the background mode

Open bilal-prog opened this issue 5 months ago • 7 comments

Required Reading

  • [x] Confirmed

Plugin Version

4.12.1

Mobile operating-system(s)

  • [ ] iOS
  • [x] Android

Device Manufacturer(s) and Model(s)

Samsung A15

Device operating-systems(s)

Android 14

React Native / Expo version

No response

What do you require assistance about?

"watchPostion" or "onLocation"

I want to store the location (in the background and the foreground mode) on each location recorded by the SDK using AsyncStorage to send it again using socket.

I tried watchPostion but it only works in the foreground mode I tried onLocation but it is a subscription so if I didn't kill its listener it will be repeated in the app

[Optional] Plugin Code and/or Config

const useTrackingService = () => {
  const stateSelector = createStructuredSelector({
    orderAcceptedPartner: makeSelectAcceptedOrderPartner(),
    gpsPermission: makeGpsPermission(),
  });

  const { orderAcceptedPartner, gpsPermission } = useSelector(stateSelector);

  const [isEnabled, setIsEnabled] = useState(false);
  useEffect(() => {
    setTimeout(() => {
      askNotificationPermission();
    }, 2000);

    // return () => {
    //   stopTracking();
    // };
  }, [orderAcceptedPartner, gpsPermission]);

  const askNotificationPermission = useCallback(async () => {
    const permissionResult = await checkNotifications();
    if (permissionResult?.status !== 'granted') {
      OneSignal.promptForPushNotificationsWithUserResponse((per) => {
        if (gpsPermission) {
          initTracking();
        }
      });
    } else {
      if (gpsPermission) {
        initTracking();
      }
    }
  }, [gpsPermission]);

  const initTracking = useCallback(async () => {
    try {
      const subscription = BackgroundGeolocation.onLocation(
        (location) => {
          console.log('[onLocation] success: ', location);
          AsyncStorage.setItem(
            'driver_location_v2',
            JSON.stringify({
              position: {
                latitude: location?.coords?.latitude,
                longitude: location?.coords?.longitude,
              },
              // fromMockProvider: false, //location?.fromMockProvider,
            }),
          );
        },
        (error) => {
          console.log('[onLocation] ERROR: ', error);
        },
      );

      //refreshToken
      //accessToken
      const token = await AsyncStorage.getItem('accessToken');

      BackgroundGeolocation.ready({
        //The minimum distance (measured in meters) a device must move horizontally before an update event is generated.
        distanceFilter: 50,
        // locationUpdateInterval: 30000,
        // fastestLocationUpdateInterval: 10000,
        //disable automatic speed-based distanceFilter elasticity. eg: When device is moving at highway speeds, locations are returned at ~ 1 / km.
        disableElasticity: false,
        //indicating whether the status bar changes its appearance when an app uses location services in the background with Always authorization.
        showsBackgroundLocationIndicator: true,
        //The Android plugin will ignore a received location when it is identical to the last location. Set true to override this behaviour and record every location, regardless if it is identical to the last location.
        allowIdenticalLocations: true,
        //The number of minutes to wait before turning off location-services after the ActivityRecognition System (ARS) detects the device is STILL
        //TODO THIS IS THE FIRST EXPERMENT RESULT
        stopTimeout: 2880000,
        //enableHeadless: true,
        //Set false to continue tracking after user terminates the app.
        stopOnTerminate: false,
        //Set to true to enable background-tracking after the device reboots.
        startOnBoot: true,
        //Enable this to prevent iOS from suspending your app in the background while in the stationary state. Must be used in conjunction with a heartbeatInterval.
        preventSuspend: true,
        //Set true to make the plugin mostly immune to OS termination due to memory pressure from other apps.
        foregroundService: true,
        // Set to true to enable "Headless" mode when the user terminates the application. In this mode, you can respond to all the plugin's events in the native Android environment. For more information, see the wiki for Android Headless Mode
        enableHeadless: true,
        disableStopDetection: true,
        // disableMotionActivityUpdates:true,
        pausesLocationUpdatesAutomatically: false,
        // allowIdenticalLocations: true,
        // isMoving: true,
        //TODO Default: false. When enabled, the plugin will emit sounds & notifications for life-cycle events of background-geolocation
        debug: false,
        logLevel: BackgroundGeolocation.LOG_LEVEL_OFF,
        //IOS ACTIVITY TYPE
        activityType: BackgroundGeolocation.ACTIVITY_TYPE_OTHER,
        //PERMISSION messages
        backgroundPermissionRationale: {
          title:
            'Autoriser DeliveryMan à accéder à la position de cet appareil en arrière-plan ?',
          message:
            'Afin de suivre votre activité en arrière-plan, veuillez activer {backgroundPermissionOptionLabel} autorisation de localisation',
          positiveAction: 'Passer à {backgroundPermissionOptionLabel}',
          negativeAction: 'Annuler',
        },

        locationAuthorizationAlert: {
          titleWhenNotEnabled:
            'les services de localisation ne sont pas activés',
          titleWhenOff: 'services de localisation désactivés',
          instructions:
            "Vous devez activer 'Toujours' dans les services de localisation",
          cancelButton: 'Annuler',
          settingsButton: 'Réglages',
        },
        //Specify the desired-accuracy of the geolocation system. NOTE : Only DESIRED_ACCURACY_HIGH uses GPS. speed, heading and altitude are available only from GPS.
        desiredAccuracy: BackgroundGeolocation.DESIRED_ACCURACY_LOW,
        notification: {
          title: 'Localisation en background',
          text: 'Cliquez pour ouvrir',
        },
        url: ``, 
        autoSync: true,
        // autoSyncThreshold: 5, //Set this to a low value to ensure that locations are sent to the server more frequently, reducing the number of stored locations.
        batchSync: false, //Set this to false to ensure that each location is sent individually rather than in batches.
        maxRecordsToPersist: 1,
        persistMode: BackgroundGeolocation.PERSIST_MODE_LOCATION, // Set this to BackgroundGeolocation.PERSIST_MODE_NONE to avoid persisting locations in the SQLite database. This ensures that only the current location is sent.
        headers: {
          Accept: 'application/json',
          'Content-Type': 'application/json',
          Authorization: 'Bearer ' + token,
        },
      }).then((state) => {
        // this restart is a temporary fix of a bug in starting the service of foreground tasks
        if (!state?.enabled) {
          setTimeout(() => {
            RNRestart.Restart();
          }, 1000);
        }
        setIsEnabled(state?.enabled);
        startTracking();

        // BackgroundGeolocation.start().then((res) => {
        //   // Update user location
        // });
      });
    } catch (err) {
      // displayToast('TRACKING ERROR : ' + (err instanceof Error ? err.message : ''));
      console.log('TRACKING Ready ERR : ', err);
    }
  }, [SOCKET_URL]);

  const startTracking = useCallback(() => {
    try {
      BackgroundGeolocation.start()
        .then((res) => {
          setIsEnabled(true);
        })
        .catch((e) => console.log('startTracking', e));
      BackgroundGeolocation.changePace(true);

     
    } catch (err) {
      //   displayToast('TRACKING NOT STARTING : ' + (err instanceof Error ? err.message : ''));
      console.log('TRACKING start ERR : ', err);
    }
  }, []);

  const stopTracking = useCallback(() => {
    try {
      BackgroundGeolocation.stop()
        .then((res) => {
          setIsEnabled(false);
        })
        .catch((e) => console.log('stopTracking', e));
      // BackgroundGeolocation.stopWatchPosition();
    } catch (err) {
      //   displayToast('TRACKING NOT STOPPING : ' + (err instanceof Error ? err.message : ''));
      console.log('TRACKING STOP ERR : ', err);
    }
  }, []);

  return [{ isEnabled }, startTracking, stopTracking];
};

export default useTrackingService;

[Optional] Relevant log output


bilal-prog avatar Jun 10 '25 15:06 bilal-prog

The issue template asked you to post your code and config.

christocracy avatar Jun 10 '25 16:06 christocracy

Edit your post and attach code / config where requested

christocracy avatar Jun 10 '25 16:06 christocracy

I edited it

bilal-prog avatar Jun 10 '25 16:06 bilal-prog

I don’t understand your problem.

.onLocation receives every recorded location, regardless of foreground or background.

christocracy avatar Jun 10 '25 16:06 christocracy

Yes I tried onLocation and it works in background mode, but it's a listener, and the listener needs to be killed to avoid repeating, and if I killed it on closing the app the record will not keep working in background mode

bilal-prog avatar Jun 10 '25 16:06 bilal-prog

There are 3 states in an Android app: foreground, background and terminated.

For the terminated case, see api docs Config.enableHeadless and learn about “Android Headless Mode”.

christocracy avatar Jun 10 '25 16:06 christocracy

Thank you so much @christocracy

bilal-prog avatar Jun 10 '25 16:06 bilal-prog

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

github-actions[bot] avatar Jul 11 '25 02:07 github-actions[bot]

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

github-actions[bot] avatar Jul 25 '25 02:07 github-actions[bot]