user1501591
user1501591

Reputation: 41

Xamarin Forms Getting foreground service location updates with CrossGeolocator

I am using the CrossGeolocator plugin to subscribe to location updates in a Xamarin Forms application, for Android (for iOS, location updates will continue even when the app is in the background).

I implemented a Foreground Service so that I can continue to get location even while the app goes to the background.

As I understand, I should be able to get location updates if I listen to them in a foreground service.

However, I do not get location updates when the app goes the the background.

My StartCommand of my service datasource:

public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
    Notification notif = DependencyService.Get<INotification>().ReturnNotif();
    StartForeground(ServiceRunningNotifID, notif);

    CrossGeolocator.Current.StartListeningAsync(TimeSpan.FromSeconds(10), 0, true, new Plugin.Geolocator.Abstractions.ListenerSettings
    {
        ActivityType = ActivityType.Other,
        PauseLocationUpdatesAutomatically = false,
        AllowBackgroundUpdates = true,
        DeferLocationUpdates = false,
        ListenForSignificantChanges = false,
    });

    CrossGeolocator.Current.PositionChanged += PositionChanged;
    CrossGeolocator.Current.PositionError += PositionError;


    return StartCommandResult.Sticky;
}

And events:

private void PositionError(object sender, PositionErrorEventArgs e)
{
    Console.WriteLine($"{e.Error}");
}

private void PositionChanged(object sender, PositionEventArgs e)
{
    Console.WriteLine($"{e.Position.Latitude}, {e.Position.Longitude}");
}

The events only fire when the app is active. The service starts, and the device shows the notification badge that the service is running.

I have assigned the required permissions:

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.LOCATION_HARDWARE" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

Can anyone shed some light on the implementation? I can provide additional code if required.

Upvotes: 1

Views: 1643

Answers (2)

user1501591
user1501591

Reputation: 41

I found the solution by adding this:

ForegroundServiceType = ForegroundService.TypeDataSync | ForegroundService.TypeLocation

As the Service Attribute in the Foreground Service

[Service(ForegroundServiceType = ForegroundService.TypeDataSync | ForegroundService.TypeLocation)]
public class LocationForegroundService : Service
{
    ...
}

Upvotes: 3

Wendy Zang - MSFT
Wendy Zang - MSFT

Reputation: 10938

Background updates are handled a bit different on each platform. For Android, you will want to integrate a foreground service that subscribes to location changes and the user interface binds to.

You could use the Xamarin.Essentials and MessagingCenter for this.

  [Service]
 public class AndroidLocationService : Service
 {
    CancellationTokenSource _cts;
    public const int SERVICE_RUNNING_NOTIFICATION_ID = 10000;

    public override IBinder OnBind(Intent intent)
    {
        return null;
    }

    public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
    {
        _cts = new CancellationTokenSource();

        Notification notif = DependencyService.Get<INotification>().ReturnNotif();
        StartForeground(SERVICE_RUNNING_NOTIFICATION_ID, notif);

        Task.Run(() => {
            try
            {
                var locShared = new Location();
                locShared.Run(_cts.Token).Wait();
            }
            catch (OperationCanceledException)
            {
            }
            finally
            {
                if (_cts.IsCancellationRequested)
                {
                    var message = new StopServiceMessage();
                    Device.BeginInvokeOnMainThread(
                        () => MessagingCenter.Send(message, "ServiceStopped")
                    );
                }
            }
        }, _cts.Token);

        return StartCommandResult.Sticky;
    }

    public override void OnDestroy()
    {
        if (_cts != null)
        {
            _cts.Token.ThrowIfCancellationRequested();
            _cts.Cancel();
        }
        base.OnDestroy();
    }
}
}

For more details, please check the code sample in the link below. https://stackoverflow.com/a/63665571/11850033

Upvotes: 0

Related Questions