Xamarin forms application Android background location services

Jitendra Jadav 21 Reputation points
2024-02-13T05:13:51.61+00:00

I have Xamarin forms application which I am using for location services where I have background service which fetching late and long, I am trying to make notification and background services needs to stop while application goes to background here is my code

using System;
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.OS;
using AndroidX.Core.App;
using Microsoft.AppCenter.Analytics.Android;
using PCF.DspMobile.Xamarin.Core.DependencyInjection;
using PCF.DspMobile.Xamarin.Core.ServiceContracts;
using PCF.DspMobile.Xamarin.Core.Services;
using Service = Android.App.Service;

namespace PCF.DspMobile.Xamarin.Droid.Services.Geolocation;

[Service(ForegroundServiceType = ForegroundService.TypeLocation)]
public class BackgroundGeolocationService : Service
{
    private readonly PCFLogger _pcfLogger;
    const int _channelId = 785123754;
    const int _serviceId = 19461923;
    bool _isStarted;
    Handler _handler;
    Action _runnable;
    Dependency<IGeolocationService> _locationServiceDependency = new();
    NotificationChannel? _channel;
    PowerManager.WakeLock partialWakeLock;

    public BackgroundGeolocationService()
    {

    }
    public BackgroundGeolocationService(PCFLogger pcfLogger)
    {
        _pcfLogger = pcfLogger;
    }

    public override void OnCreate()
    {
        _handler = new Handler(Looper.MainLooper!);
        _runnable = new Action(() =>
        {
            if (_isStarted)
            {
                Execute();
                _handler.PostDelayed(_runnable, 1000);
            }
        });
        base.OnCreate();
    }

    public override StartCommandResult OnStartCommand(
        Intent? intent,
        StartCommandFlags flags,
        int startId
    )
    {
        // Acquire a partial WakeLock
        var wakeLock = (PowerManager)GetSystemService(PowerService);
        partialWakeLock = wakeLock.NewWakeLock(WakeLockFlags.Partial, "BackgroundGeolocationService");
        partialWakeLock.Acquire();

        if (intent == null)
        {
            intent = new Intent(GooglePlayLocationService.Context, typeof(BackgroundGeolocationService))
                .AddFlags(ActivityFlags.ReceiverForeground);

            // TODO add logging
            Analytics.TrackEvent("Received null intent from the service... rebuilding...");
            _pcfLogger.LogServiceInstance.GetPublisher().Publish(Logging.LogLevel.Info, "Received null intent from the service... rebuilding...");
        }

        if (_isStarted)
        {
            // Do Nothing
        }
        else
        {
            StartInForeground(Application.Context, intent);
            _handler.PostDelayed(_runnable, 1000);
            _isStarted = true;
        }

        return StartCommandResult.Sticky;
    }

    void Execute()
    {
        var locationService = (GooglePlayLocationService)_locationServiceDependency.Resolve();
        locationService.RequestLocationUpdate();
    }

    void StartInForeground(Context context, Intent? intent)
    {
        var pendingIntent = PendingIntent
            .GetActivity(context, 0, intent, PendingIntentFlags.Mutable);

        var builder = new NotificationCompat.Builder(context, _channelId.ToString())
            .SetContentTitle("DSP mobile is Tracking You")
            .SetContentText("Your location is being tracked")
            .SetSmallIcon(Resource.Drawable.dart_mobile_icon)
            .SetOngoing(true)
            .SetContentIntent(pendingIntent);

        if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
        {
            _channel ??=
                new NotificationChannel(_channelId.ToString(), "Title", NotificationImportance.High);
            _channel.Importance = NotificationImportance.High;
            _channel.EnableLights(true);
            _channel.EnableVibration(true);
            _channel.SetShowBadge(true);
            _channel.SetVibrationPattern(new long[] { 100, 200, 300 });

            var notificationManager = GetSystemService(NotificationService) as NotificationManager;

            if (notificationManager != null)
            {
                builder.SetChannelId(_channelId.ToString());
                notificationManager.CreateNotificationChannel(_channel);
            }
        }

        StartForeground(_serviceId, builder.Build());
    }

    public override ComponentName? StartForegroundService(Intent? service)
    {
        return base.StartForegroundService(service);
    }

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

    public override void OnTaskRemoved(Intent rootIntent)
    {
        StopForeground(StopForegroundFlags.Remove);
        // Release the partial WakeLock
        if (partialWakeLock != null && partialWakeLock.IsHeld)
        {
            partialWakeLock.Release();
        }
        base.OnTaskRemoved(rootIntent);
    }
}
			


now I want to call OnTaskRemoved so that I can close sticky location services notification and stop location services but unfortunate this is override so I can't access this is there any other way I can stop while my application goes to background I am open if any better approach apart from what I have mention

Developer technologies .NET Xamarin
Developer technologies C#
0 comments No comments
{count} votes

1 answer

Sort by: Most helpful
  1. Anonymous
    2024-02-14T08:21:18.2666667+00:00

    Hello, You can stop services in the MainActivity OnStop method, when your application in the background, OnStop method will be executed. We can stop it By StopService with intent.

    public class MainActivity : Activity
    {
         protected override void OnCreate(Bundle? savedInstanceState)
         {
             base.OnCreate(savedInstanceState);
    
    
            Intent startServiceIntent = new Intent(this, typeof(BackgroundGeolocationService));
             startServiceIntent.SetAction(Constants.ACTION_START_SERVICE);
    
    
            StartService(startServiceIntent);
         }
    
    
        protected override void OnStop()
         {
             base.OnStop();
            Intent stopServiceIntent = new Intent(this, typeof(BackgroundGeolocationService));
             stopServiceIntent.SetAction(Constants.ACTION_STOP_SERVICE);
             StopService(stopServiceIntent);
         }
    }
    

    Then we need to handle the stop intent in OnStartCommand to stop services by StopForeground(true); StopSelf(); method.

    [Service(ForegroundServiceType = ForegroundService.TypeLocation)]
    public class BackgroundGeolocationService : Service
    {
         public override IBinder? OnBind(Intent? intent)
         {
             
             return null;
         }
         bool isStarted;
    
    
        static readonly string TAG = typeof(BackgroundGeolocationService).FullName;
    
    
        [return: GeneratedEnum]
         public override StartCommandResult OnStartCommand(Intent? intent, [GeneratedEnum] StartCommandFlags flags, int startId)
         {
             if (intent.Action.Equals(Constants.ACTION_START_SERVICE))
             {
                 if (isStarted)
                 {
                     Log.Info(TAG, "OnStartCommand: The service is already running.");
                 }
                 else
                 {
                     Log.Info(TAG, "OnStartCommand: The service is starting.");
                     RegisterForegroundService();
                    
                     isStarted = true;
                 }
             }
             else if (intent.Action.Equals(Constants.ACTION_STOP_SERVICE))
             {
                 Log.Info(TAG, "OnStartCommand: The service is stopping.");        
                 StopForeground(true);
                 StopSelf();
                 isStarted = false;
            }
             else if (intent.Action.Equals(Constants.ACTION_RESTART_TIMER))
             {
                 Log.Info(TAG, "OnStartCommand: Restarting the timer.");          
            }
            // This tells Android not to restart the service if it is killed to reclaim resources.
             return StartCommandResult.Sticky;
         }
    }
    

    By the way, Constants is a static class.

     public static class Constants
      {
          public const string ACTION_START_SERVICE = "ServicesDemo3.action.START_SERVICE";
          public const string ACTION_STOP_SERVICE = "ServicesDemo3.action.STOP_SERVICE";
          public const string ACTION_RESTART_TIMER = "ServicesDemo3.action.RESTART_TIMER";
     }
    

    Best Regards, Leon Lu


    If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment". Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.


Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.