Skip to content
This repository was archived by the owner on Nov 8, 2023. It is now read-only.
This repository was archived by the owner on Nov 8, 2023. It is now read-only.

watchLocation method doesn't get location in background mode android #256

Open

Description

@bhoomiJagani

Which platform(s) does your issue occur on?
Android
Android 10
Device Moto G(2nd Generation)

Please, provide the following version numbers that your issue occurs with:
CLI: 8.3.18

In, package.json
"nativescript-geolocation": "^5.1.0",

Please, tell us how to recreate the issue in as much detail as possible.

I'm using Geolocation apis to get location in background in android. When I call watchLocation in foreground, it's giving location, but when app goes to background, I didn't get any location. I am displaying location in console.log. For ios, when app goes in background mode, it's display log of location. I am using nativescript with angular.

Is there any code involved?

public buttonStartTap() {
   console.log("buttonStartTap");
   try {
     let that = this;
     this.watchIds.push(geolocation.watchLocation(
       function (loc) {
         if (loc) {
           that.locations.push(loc);
           console.log("loc", loc);
         }
       },
       function (e) {
         console.log("Error: " + e.message);
       },
       {
         desiredAccuracy: Accuracy.high,
         updateDistance: 1,
         updateTime: 3000,
         minimumUpdateTime: 100,
         iosAllowsBackgroundLocationUpdates: true,
         iosPausesLocationUpdatesAutomatically: false,

       }));
   } catch (ex) {
     console.log("Error: " + ex.message);
   }
 }

Activity

  1. butaminas commented on Dec 7, 2019

    @butaminas

    I had a similar problem. It is not directly related to nativescript-geolocation rather to Android itself. You have to create a foreground service in order to track geolocation while the app is in the foreground.

    Read more here: https://developer.android.com/about/versions/oreo/background-location-limits#tuning-behavior

  2. akarsh014 commented on Apr 1, 2020

    @akarsh014

    I still the same issue, i get only few coordinates after which "service onStopJob" is called

  3. butaminas commented on Apr 24, 2020

    @butaminas

    I still the same issue, i get only few coordinates after which "service onStopJob" is called

    My initial fix seems to only fix this problem on Xiaomi devices. I've tried it on pure Android (like Samsung phones) and it would stop working after few seconds.

  4. butaminas commented on Apr 24, 2020

    @butaminas

    I think I found the solution!!!
    Hopefully, more can test this and confirm.

    What I did was I added android:foregroundServiceType="location" to my ForegroundService Service in Android.xml

    My final service tag looks like this:

            <service
                    android:name="your-name-here"
                    android:foregroundServiceType="location"
                    android:stopWithTask="true"
                    android:enabled="true"
                    android:exported="false" />
    

    My minSdkVersion is 21

    Hope it helps someone as this was really painful to me... 🤯

  5. JuanDeLeon commented on May 1, 2020

    @JuanDeLeon

    Background still not working for me on android, Nexus 5X API 28 emulator.
    Pulled the Vue demo, and then followed the instructions to add the android service.
    The service starts tracking correctly and keeps working as long as the app is in foreground.

    I can see the logs on console:
    JS: 'Background Location: 37.3521938 -122.0620458'
    JS: 'Background Location: 37.3521957 -122.0620825'
    JS: 'Background Location: 37.352197 -122.0621304'

    But as soon as I send it to the background, the Geolocation icon at the top of the phone is gone, and it stops logging my location.

    I have added the following to Android Manifest:

    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    
    <!-- This is at application level: -->
    <service android:name="com.nativescript.location.BackgroundService" 
    	android:exported="false" >
    </service>
    
    <service android:name="com.nativescript.location.BackgroundService26" 
    	android:permission="android.permission.BIND_JOB_SERVICE"
                android:enabled="true"
                android:exported="false"
                android:stopWithTask="true"
                android:foregroundServiceType="location">
    </service>
    

    Any ideas of what could be missing? Thanks in advance.

    Edit: Background service actually seems to work fine when ran on my Samsung Galaxy tablet though. Might be something specific with the simulator...

  6. PlayNext commented on Jul 3, 2020

    @PlayNext

    What I did was I added android:foregroundServiceType="location" to my ForegroundService Service in Android.xml

    Confirmed on Samsung Android 10. Thank you butaminas!
    It should be mentioned also you need to add compileSdkVersion 29 to your app.gradle for this to work.

  7. dlcole commented on Feb 3, 2021

    @dlcole

    I'm working through this issue presently. The guide suggested by @Aceman18 looks pertinent, 'tho it's an Angular example and my project Javascript. So far I've not been successful in even getting a clean build, even though I've done lots of such conversions over the years. If anyone has a working version in Javascript I'd love to connect with you.

    [Edit]

    I have this working nicely now in a JavaScript project. Here's the changes I made:

    Create a new file, foreground-service.android.js in the /app folder:

    /**
     * Provide Android foreground service for nativescript-geolocation.watchLocation() 
     * @see https://appnative.org/nativescript-geolocation-in-the-background-for-android/
     * @see https://github.com/NativeScript/NativeScript/issues/3422
     */
    
    android.app.Service.extend('org.nativescript.geolocation.ForegroundService', {
    
      onStartCommand: function (intent, flags, startId) {
        this.super.onStartCommand(intent, flags, startId);
        return android.app.Service.START_STICKY;
      },
    
      onCreate: function () {
        this.super.onCreate();
        this.startForeground(1, this.getNotification());
      },
    
      onBind: function (intent) {
        return this.super.onBind(intent);
      },
    
      onUnbind: function (intent) {
        return this.super.onUnbind(intent);
      },
    
      onDestroy: function () {
        this.stopForeground(true);
      },
    
      getNotification: function () {
        const channel = new android.app.NotificationChannel(
          'channel_01',
          'ForegroundService Channel',
          android.app.NotificationManager.IMPORTANCE_DEFAULT
        );
        const notificationManager = this.getSystemService(android.content.Context.NOTIFICATION_SERVICE);
        notificationManager.createNotificationChannel(channel);
        const builder = new android.app.Notification.Builder(this.getApplicationContext(), 'channel_01');
    
        return builder.build();
      },
    });
    

    Add the permission in AndroidManifest.xml

    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

    And define the service in AndroidManifest.xml

    <service
          android:name="org.nativescript.geolocation.ForegroundService"
          android:enabled="true"
          android:stopWithTask="true"
          android:exported="false" 
          android:foregroundServiceType="location" />
    

    Resolve the path in webpack.config.js:

    appComponents.push(...[
            "tns-core-modules/ui/frame",
            "tns-core-modules/ui/frame/activity",
            resolve(__dirname, "./app/foreground-service.android.js"), // <-- this is the addition
        ]);
    

    Set up the foreground service in app.js:

    // Set up foreground service for Android background location 
    
    startForegroundService();
    application.on(application.exitEvent, () => {
      stopForegroundService();
    });
    
    function startForegroundService() {
      const context = utils.ad.getApplicationContext();
      const intent = new android.content.Intent();
      intent.setClassName(context, 'org.nativescript.geolocation.ForegroundService');
      context.startForegroundService(intent);
    }
    
    function stopForegroundService() {
      const context = utils.ad.getApplicationContext();
      const intent = new android.content.Intent();
      intent.setClassName(context, 'org.nativescript.geolocation.ForegroundService');
      context.stopService(intent);
    }
    

    And, if you're not already including the utils plugin, add

    const utils = require("tns-core-modules/utils/utils");

    Based on the comments in this issue, I also set compileSdkVersion in app.gradle:

    android {
    compileSdkVersion 29 // <- this was added
    defaultConfig {

  8. dlcole commented on Feb 27, 2022

    @dlcole

    Referencing the above post, for webpack 5, you'll need to adjust webpack.config.js to something like this:

    const webpack = require("@nativescript/webpack");
    
    module.exports = (env) => {
    
      // See https://stackoverflow.com/questions/70428159/nativescript-8-1-migration-adding-external-to-webpack-config-js
      env.appComponents = (env.appComponents || []).concat(['./foreground-service.android'])
    	
      webpack.init(env);
    
    	// Learn how to customize:
    	// https://docs.nativescript.org/webpack
    
    	return webpack.resolveConfig();
    };
    
  9. nmandyam commented on Aug 4, 2022

    @nmandyam

    @dlcole Thank you for the code, that's very helpful - and it works. But unfortunately, this is not enough for a newbie like me. I'm unable to figure out where in your code the response to watchLocation happens. Like for everyone else, watchLocation works well when my app is in the foreground, not all when it is not. I have your code included and compiled, the foreground service starts (I have console-writes to tell me that). But then what? I need to write out the locations - where do I do that from, in your code?

    Related: the location needs to be tracked even if the user swipes away the app without logging out - I'm hoping the foreground service will handle that?

    Last question: Any idea how this would work on iOS?

    Sorry if all this sounds dumb! All help appreciated!

  10. dlcole commented on Aug 4, 2022

    @dlcole

    @nmandyam Your watchLocation function should receive control from the foreground service, just as it does when your app itself is in the foreground. This should work while your app is in the background on Android, but won't work if your app is terminated. (You can take some extra steps to also make it work when your app is terminated - see this article.)

    iOS works differently, but doesn't require additional services. In Xcode under Signing and Capabilities -> Background Modes, you'll need to check "Location Updates".

    In your Info.plist file, you'll need the entries:

      <key>UIBackgroundModes</key>
        <array>
          <string>location</string>
        </array>
    

    and also

      <key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
      <string>This application requires location services to show your position on maps.</string>
      <key>NSLocationWhenInUseUsageDescription</key>
      <string>This application requires location services to show your position on maps.</string>
    

    Good luck!

  11. nmandyam commented on Aug 5, 2022

    @nmandyam

    @dlcole thank you!

  12. nmandyam commented on Aug 9, 2022

    @nmandyam

    @dlcole sorry to bother you about this again, but I'm completely lost. As it stands now:

    • I added your Foreground Service code to my app
    • I invoke the watchLocation on a particular event happening (user clicks on a button in my app).

    So it starts tracking location all OK when the app is in the foreground. And it continues to track location when the app goes to the background. I see the Android notification that the foreground service is running. But when I swipe away the app, the location stops being tracked, like you said would happen.

    BUT: Per that article that you linked me to, it's the foreground service that makes sure that my app continues to report location when swiped away. And the Java code is nearly identical to your snippet above, so I assume the "foreground service" that the article refers to and the "foreground service" that you've helped me build are the same thing. Therefore, by implementing the foreground service, the app should continue reporting location. Yet, swipe away the app and it won't track location (just like you said it won't). What am I missing?

    Thanks for all your help.

  13. dlcole commented on Aug 9, 2022

    @dlcole

    When you say, "swiped away" do you mean that you've actually terminated the app? This is a key distinction.

  14. nmandyam commented on Aug 9, 2022

    @nmandyam
  15. dlcole commented on Aug 9, 2022

    @dlcole

    So when you open the list of Recent apps, your app is no longer there, yes? If so, this means your app is actually terminated and not just in the background. The article I linked to in a prior posts suggests how to make this scenario work, but my code only works when the app is in the foreground (normal case) or background, using the foreground service (I admit the terminology is confusing).

  16. shradovyy commented on Mar 28, 2023

    @shradovyy

    @dlcole I managed to get it working with foreground service, the only problem is after some time location updates stop receiving for some reason. Do you happen to know why? let me know if you can assist

  17. dlcole commented on Mar 28, 2023

    @dlcole

    @shradovyy I haven't see this behavior but I've not tested this code recently. How long does it take to stop receiving updates?

  18. shradovyy commented on Mar 28, 2023

    @shradovyy

    It really depends. It might work for as long as I don't open any other apps and keep the screen on (do not lock the phone). As soon as I open other app it seems like android suspends the app even when foreground service is running.

    When I open the app again from the recent apps it kind of restarts (splash screen is shown)

  19. shradovyy commented on Mar 28, 2023

    @shradovyy

    @dlcole by the way, thank you for a quick reply

  20. dlcole commented on Mar 28, 2023

    @dlcole
  21. dlcole commented on Mar 30, 2023

    @dlcole

    So I tested this back on Tuesday and the Android phone continued to send it's location data. BUT, the phone may need to be moving for that to happen, depending on what you specify in your watchLocation options:

    {  // options
      desiredAccuracy: Accuracy.high,
      updateDistance: 100,
      updateTime: 30000, // ignored on iOS 
      minimumUpdateTime: 10000, // ignored on iOS 
      iosAllowsBackgroundLocationUpdates: true,
    }
    

    I noticed that even with the above specified, I was only receiving location updates from Android when the phone was moving.

  22. ignacio68 commented on Sep 7, 2023

    @ignacio68

    Thanks @dlcole , it works fine in background in android.

    This is the code in Typescript:

    `@NativeClass()
    @javaproxy("org.nativescript.geolocation.ForegroundService")
    class ForegroundService extends android.app.Service {
    onStartCommand(intent: android.content.Intent, flags: number, startId: number) {
    super.onStartCommand(intent, flags, startId);
    return android.app.Service.START_STICKY;
    }

    onCreate() {
    super.onCreate();
    console.log("FOREGROUND SERVICE CREATED!");
    this.startForeground(1, this.getNotification());
    }

    onBind(intent: android.content.Intent) {
    return super.onBind(intent);
    }

    onUnbind(intent: android.content.Intent) {
    return super.onUnbind(intent);
    }

    onDestroy() {
    console.log("FOREGROUND SERVICE DESTROYED");
    super.onDestroy();
    this.stopForeground(true);
    }

    getNotification() {
    const channel = new android.app.NotificationChannel("channel_01", "ForegroundService Channel", android.app.NotificationManager.IMPORTANCE_DEFAULT);
    const notificationManager = this.getSystemService(android.content.Context.NOTIFICATION_SERVICE);
    notificationManager.createNotificationChannel(channel);
    const builder = new android.app.Notification.Builder(this.getApplicationContext(), "channel_01");

    return builder.build();
    

    }
    }`

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions