Shiny.NET
Geofencing

|Area|Info| |---|---| |Description|| |Service|Shiny.Locations.IGeofenceManager| |NuGet|| |Static Generated Class|ShinyGeofencing|

using Microsoft.Extensions.DependencyInjection;
using Shiny;

namespace YourNamespace
{
    public class YourShinyStartup : ShinyStartup
    {
        public override void ConfigureServices(IServiceCollection services, IPlatform platform)
        { 
            
<p>services.UseGeofencing&lt;Shiny.Locations.IGeofenceDelegate&gt;();
services.UseGeofencing 
        }
    }
}

Minimum Version: 8.0

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8" ?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.org.yourapp" android:installLocation="preferExternal">
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-feature android:name="android.hardware.location.gps" />
    <uses-feature android:name="android.hardware.location.network" />
</manifest>

Minimum Version: 10

Info.plist

<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE plist PUBLIC " -//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>NSLocationAlwaysUsageDescription</key>
    <string>Say something useful here that your users will understand</string>
    <key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
    <string>Say something useful here that your users will understand</string>
    <key>NSLocationWhenInUseUsageDescription</key>
    <string>Say something useful here that your users will understand</string>
</dict>
</plist>

Package.appxmanifest

<?xml version="1.0" encoding="utf-8" ?>
<Package>
    <Applications>
    </Applications>
    <Capabilities>
        <DeviceCapability Name="location" />
    </Capabilities>
</Package>
using Microsoft.Extensions.DependencyInjection;
using Shiny;

public class GeofenceStartup : ShinyStartup
{
    public override void ConfigureServices(IServiceCollection services, IPlatform platform)
    {
        services.UseGeofencing<GeofenceDelegate>();
    }
}

using System;
using System.Collections.Generic;
using System.Threading.Tasks;


namespace Shiny.Locations
{
    public class GpsGeofenceDelegate : NotifyPropertyChanged, IGpsDelegate
    {
        readonly IGeofenceManager geofenceManager;
        readonly IGeofenceDelegate geofenceDelegate;
        public Dictionary<string, GeofenceState> CurrentStates { get; set; }


        public GpsGeofenceDelegate(IGeofenceManager geofenceManager, IGeofenceDelegate geofenceDelegate)
        {
            this.CurrentStates = new Dictionary<string, GeofenceState>();
            this.geofenceManager = geofenceManager;
            this.geofenceDelegate = geofenceDelegate;
        }


        public async Task OnReading(IGpsReading reading)
        {
            var geofences = await this.geofenceManager.GetMonitorRegions();
            foreach (var geofence in geofences)
            {
                var state = geofence.IsPositionInside(reading.Position)
                    ? GeofenceState.Entered
                    : GeofenceState.Exited;

                var current = this.GetState(geofence.Identifier);
                if (state != current)
                {
                    this.SetState(geofence.Identifier, state);
                    await this.geofenceDelegate.OnStatusChanged(state, geofence);
                }
            }
        }


        protected GeofenceState GetState(string geofenceId)
            => this.CurrentStates.ContainsKey(geofenceId)
                ? this.CurrentStates[geofenceId]
                : GeofenceState.Unknown;


        protected virtual void SetState(string geofenceId, GeofenceState state)
        {
            this.CurrentStates[geofenceId] = state;
            this.RaisePropertyChanged(nameof(this.CurrentStates));
        }
    }

}

using System;
using System.Threading.Tasks;
using Shiny.Infrastructure;


namespace Shiny.Locations
{
    public interface IGeofenceDelegate : IShinyDelegate
    {
        /// <summary>
        /// This is fired when the geofence region status has changed
        /// </summary>
        /// <param name="newStatus">The new geofence state.</param>
        /// <param name="region">The geofence region.</param>
        Task OnStatusChanged(GeofenceState newStatus, GeofenceRegion region);
    }
}

using System;
using System.Threading.Tasks;
using Shiny.Jobs;


namespace Shiny.Locations.Sync.Infrastructure
{
    public class SyncGeofenceDelegate : IGeofenceDelegate
    {
        readonly IJobManager jobManager;
        readonly IMotionActivityManager activityManager;
        readonly IGeofenceDataService dataService;


        public SyncGeofenceDelegate(IJobManager jobManager,
                                    IMotionActivityManager activityManager,
                                    IGeofenceDataService dataService)
        {
            this.jobManager = jobManager;
            this.activityManager = activityManager;
            this.dataService = dataService;
        }


        public async Task OnStatusChanged(GeofenceState newStatus, GeofenceRegion region)
        {
            var job = await this.jobManager.GetJob(Constants.GeofenceJobIdentifer);
            if (job == null)
                return;

            var config = job.GetSyncConfig();
            MotionActivityEvent? activity = null;
            if (config.IncludeMotionActivityEvents)
                activity = await this.activityManager.GetCurrentActivity();

            var e = new GeofenceEvent
            {
                Id = Guid.NewGuid().ToString(),
                DateCreated = DateTimeOffset.UtcNow,

                Identifier = region.Identifier,
                Entered = newStatus == GeofenceState.Entered,
                Activities = activity?.Types
            };
            await this.dataService.Create(e);
            if (!this.jobManager.IsRunning)
                await this.jobManager.RunJobAsTask(Constants.GeofenceJobIdentifer);
        }
    }
}

using System.Threading.Tasks;
using Shiny.Locations;

public class GeofenceDelegate : IGeofenceDelegate
{
    public async Task OnStatusChanged(GeofenceState newStatus, GeofenceRegion region)
    {
    }
}

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Shiny.Locations.Sync;

public class LocationSyncGeofenceDelegate : IGeofenceSyncDelegate
{
    public Task Process(IEnumerable<GeofenceEvent> geofence, CancellationToken cancelToken)
    {
        throw new NotImplementedException();
    }
}
using System.Threading.Tasks;
using Shiny;
using Shiny.Locations;

public class GeofenceUsage
{
    public async Task Usage()
    {
        var manager = ShinyHost.Resolve<IGeofenceManager>();
        var result = await manager.RequestAccess();
        if (result == AccessState.Available)
        {
            await manager.StartMonitoring(new GeofenceRegion(
                "YourIdentifier",
                new Position(1, 1),
                Distance.FromKilometers(1)
            ));
        }
    }


    public async Task Stop()
    {
        await ShinyHost.Resolve<IGeofenceManager>().StopMonitoring("YourIdentifier");

        // or

        await ShinyHost.Resolve<IGeofenceManager>().StopAllMonitoring();
    }
}