Browse Source

Added Geolocation and Permissions

Frank van den Bos 2 months ago
parent
commit
0f7ed0717d

+ 24 - 0
InABox.Avalonia.Platform.iOS/Geolocation.iOS.cs

@@ -0,0 +1,24 @@
+using System.Drawing;
+using InABox.Core;
+using Microsoft.Maui.Devices.Sensors;
+using Location = InABox.Core.Location;
+
+namespace InABox.Avalonia.Platform.iOS;
+
+public class iOS_Geolocation : DefaultGeolocation
+{
+    public Logger? Logger { get; set; }
+    
+    public override async Task<GeoPoint?> GetLocationAsync(CancellationTokenSource cancel)
+    {
+        GeolocationRequest request =
+            new GeolocationRequest(GeolocationAccuracy.Medium, TimeSpan.FromSeconds(10));
+        
+        var location =
+            await Geolocation.Default.GetLocationAsync(request, cancel.Token);
+        
+        return location != null 
+            ? new GeoPoint((float)location.Latitude, (float)location.Longitude) 
+            : null;
+    }
+}

+ 1 - 1
InABox.Avalonia.Platform.iOS/InABox.Avalonia.Platform.iOS.csproj

@@ -23,7 +23,7 @@
 
     <ItemGroup>
       <PackageReference Include="Avalonia" Version="11.3.2" />
-      <PackageReference Include="Microsoft.Maui.Essentials" Version="9.0.90" />
+      <PackageReference Include="Microsoft.Maui.Essentials" Version="8.0.100" />
       <PackageReference Include="Plugin.BLE" Version="3.1.0" />
     </ItemGroup>
     

+ 28 - 0
InABox.Avalonia.Platform.iOS/Permissions.iOS.cs

@@ -0,0 +1,28 @@
+using InABox.Core;
+using Microsoft.Maui.ApplicationModel;
+
+namespace InABox.Avalonia.Platform.iOS;
+
+public class iOS_Permissions : IPermissions
+{
+    public Logger? Logger { get; set; }
+    
+    public async Task<bool> IsPermitted(Permission permission)
+    {
+        if (permission == Permission.Camera)
+            return await DoEnabled<Permissions.Camera>();
+        if (permission == Permission.PhotoLibrary || permission == Permission.VideoLibrary)
+            return await DoEnabled<Permissions.Photos>();
+        if (permission == Permission.Geolocation)
+            return await DoEnabled<Permissions.LocationWhenInUse>();
+        return false;
+    }
+
+    private async Task<bool> DoEnabled<TPermission>() where TPermission : Permissions.BasePlatformPermission, new()
+    {
+        var status = await Permissions.CheckStatusAsync<TPermission>();
+        if (status != PermissionStatus.Granted && status != PermissionStatus.Restricted)
+            status = await Permissions.RequestAsync<TPermission>();
+        return status == PermissionStatus.Granted;
+    }
+}

+ 106 - 0
InABox.Avalonia.Platform/Geolocation/DefaultGeolocation.cs

@@ -0,0 +1,106 @@
+using System.ComponentModel;
+using System.Drawing;
+using System.Runtime.CompilerServices;
+using InABox.Core;
+
+namespace InABox.Avalonia.Platform;
+
+public class DefaultGeolocation : INotifyPropertyChanged, IGeolocation
+{
+    
+    private DateTime _nextCheck = DateTime.MaxValue;
+    
+    public bool Scanning
+    {
+        get => _nextCheck == DateTime.MaxValue;
+        set => SetField(ref _nextCheck, value ? DateTime.MinValue : DateTime.MaxValue);
+    }
+
+    private GeoPoint? _currentLocation;
+    public GeoPoint? CurrentLocation
+    {
+        get => _currentLocation;
+        set
+        {
+            SetField(ref _currentLocation, value);
+            LocationChanged?.Invoke(this, EventArgs.Empty);
+        }
+    }
+
+    public event EventHandler? LocationChanged;
+
+    public Logger? Logger { get; set; }
+    
+    public virtual Task<GeoPoint?> GetLocationAsync(CancellationTokenSource cancel)
+    {
+        return Task.FromResult<GeoPoint?>(new GeoPoint());
+    }
+    
+    private readonly CancellationTokenSource _cancelTokenSource;
+    private bool _isCheckingLocation;
+    
+    public DefaultGeolocation()
+    {
+        _cancelTokenSource = new CancellationTokenSource();
+        try
+        {
+            _ = Task.Run(async () =>
+                {
+                    while (!_cancelTokenSource.Token.IsCancellationRequested)
+                    {
+                        if (_nextCheck < DateTime.Now && !_isCheckingLocation)
+                        {
+                            try
+                            {
+                                _isCheckingLocation = true;
+                                CurrentLocation = await PlatformTools.Geolocation.GetLocationAsync(_cancelTokenSource);
+                            }
+                            catch (Exception ex)
+                            {
+                                Logger?.Send(LogType.Error, "", $"GPS Location Error: {ex.Message}");
+                                CurrentLocation = null;
+                            }
+                            _isCheckingLocation = false;
+                            _nextCheck = DateTime.Now.AddSeconds(30);
+                        }
+                    }
+                }
+            );
+        }
+        catch (OperationCanceledException e)
+        {
+            Logger?.Send(LogType.Error, "", $"GPS Scanning Cancelled: {e.Message}");
+        }
+        catch (Exception e)
+        {
+            Logger?.Send(LogType.Error, "", $"GPS Error: {e.Message}");
+        }
+        
+    }
+    
+    public void Cancel()
+    {
+        if (_isCheckingLocation && _cancelTokenSource.IsCancellationRequested == false)
+            _cancelTokenSource.Cancel();
+    }
+    
+    
+    #region INotifyPropertyChanged
+    
+    public event PropertyChangedEventHandler? PropertyChanged;
+
+    protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
+    {
+        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+    }
+
+    protected bool SetField<T>(ref T field, T value, [CallerMemberName] string? propertyName = null)
+    {
+        if (EqualityComparer<T>.Default.Equals(field, value)) return false;
+        field = value;
+        OnPropertyChanged(propertyName);
+        return true;
+    }
+    
+    #endregion
+}

+ 16 - 0
InABox.Avalonia.Platform/Geolocation/IGeolocation.cs

@@ -0,0 +1,16 @@
+using System.Drawing;
+using InABox.Core;
+
+namespace InABox.Avalonia.Platform;
+
+public interface IGeolocation : ILoggable
+{
+    
+    bool Scanning { get; set; }
+    
+    GeoPoint? CurrentLocation { get; set; }
+    
+    public event EventHandler? LocationChanged; 
+    
+    Task<GeoPoint?> GetLocationAsync(CancellationTokenSource cancel);
+}

+ 0 - 1
InABox.Avalonia.Platform/ImageTools/DefaultImageTools.cs

@@ -1,7 +1,6 @@
 using System.Drawing;
 using Avalonia.Controls;
 using InABox.Core;
-using Microsoft.Maui.Storage;
 
 namespace InABox.Avalonia.Platform;
 

+ 2 - 2
InABox.Avalonia.Platform/InABox.Avalonia.Platform.csproj

@@ -11,8 +11,8 @@
 
     <ItemGroup>
       <PackageReference Include="Autofac" Version="8.4.0" />
-      <PackageReference Include="Avalonia" Version="11.2.2" />
-      <PackageReference Include="Microsoft.Maui.Essentials" Version="9.0.90" />
+      <PackageReference Include="Avalonia" Version="11.3.2" />
+      <PackageReference Include="Microsoft.Maui.Graphics" Version="8.0.100" />
     </ItemGroup>
 
     <ItemGroup>

+ 17 - 0
InABox.Avalonia.Platform/Permissions/DefaultPermissions.cs

@@ -0,0 +1,17 @@
+using System.ComponentModel;
+using System.Drawing;
+using InABox.Core;
+
+namespace InABox.Avalonia.Platform;
+
+public partial class DefaultPermissions : INotifyPropertyChanged, IPermissions
+{
+    
+    public Logger? Logger { get; set; }
+    
+    public Task<bool> IsPermitted(Permission permission)
+    {
+        return Task.FromResult(false);
+    }
+
+}

+ 10 - 0
InABox.Avalonia.Platform/Permissions/IPermissions.cs

@@ -0,0 +1,10 @@
+using InABox.Avalonia.Platform;
+
+namespace InABox.Avalonia.Platform;
+
+public interface IPermissions : ILoggable
+{
+    Task<bool> IsPermitted(Permission permission);
+    
+    
+}

+ 9 - 0
InABox.Avalonia.Platform/Permissions/Permission.cs

@@ -0,0 +1,9 @@
+namespace InABox.Avalonia.Platform;
+
+public enum Permission
+{
+    Camera,
+    PhotoLibrary,
+    VideoLibrary,
+    Geolocation
+}

+ 20 - 0
InABox.Avalonia.Platform/PlatformTools.cs

@@ -77,6 +77,26 @@ public static class PlatformTools
         }
     }
     
+    private static IGeolocation? _geolocation;
+    public static IGeolocation Geolocation
+    {
+        get
+        {
+            _geolocation ??= Resolve<IGeolocation, DefaultGeolocation>();
+            return _geolocation;
+        }
+    }
+    
+    private static IPermissions? _permissions;
+    public static IPermissions Permissions
+    {
+        get
+        {
+            _permissions ??= Resolve<IPermissions, DefaultPermissions>();
+            return _permissions;
+        }
+    }
+    
     private static TInterface Resolve<TInterface, TDefault>() where TInterface : notnull, ILoggable where TDefault : TInterface, new() 
     {
         _container ??= _builder.Build();

+ 0 - 9
InABox.Avalonia/Components/AvaloniaDataGrid/Columns/AvaloniaDataGridColumn.cs

@@ -1,18 +1,9 @@
 using Avalonia.Controls;
-using Avalonia.Controls.Converters;
 using Avalonia.Controls.Templates;
 using Avalonia.Data;
-using Avalonia.Layout;
 using Avalonia.Media;
-using Avalonia.Styling;
 using InABox.Core;
-using Microsoft.Maui.Devices;
-using System;
-using System.Collections.Generic;
-using System.Linq;
 using System.Linq.Expressions;
-using System.Text;
-using System.Threading.Tasks;
 
 namespace InABox.Avalonia.Components;
 

+ 0 - 8
InABox.Avalonia/Components/ImageEditor/ImageEditor.axaml.cs

@@ -4,23 +4,15 @@ using Avalonia.Controls.Shapes;
 using Avalonia.Data;
 using Avalonia.Input;
 using Avalonia.Interactivity;
-using Avalonia.Markup.Xaml;
 using Avalonia.Media;
 using Avalonia.Media.Imaging;
 using Avalonia.Layout;
-using Avalonia.Skia.Helpers;
 using CommunityToolkit.Mvvm.Input;
-using FluentResults;
 using InABox.Avalonia.Components.ImageEditing;
 using InABox.Avalonia.Converters;
-using InABox.Core;
-using SkiaSharp;
 using System.Collections.ObjectModel;
-using System.Threading.Tasks;
-using Avalonia.LogicalTree;
 using CommunityToolkit.Mvvm.ComponentModel;
 using InABox.Avalonia.Dialogs;
-using Microsoft.Maui.Devices;
 
 namespace InABox.Avalonia.Components;
 

+ 17 - 0
InABox.Avalonia/Converters/BooleanToStringConverter.cs

@@ -0,0 +1,17 @@
+namespace InABox.Avalonia.Converters;
+
+public class BooleanToStringConverter : AbstractConverter<bool,string?>
+{
+        
+    public string? False{ get; set; }
+        
+    public string? True { get; set; }
+        
+    protected override string? Convert(bool value, object parameter = null)
+    {
+        return value
+            ? True
+            : False;
+    }
+
+}

+ 1 - 1
InABox.Avalonia/InABox.Avalonia.csproj

@@ -31,7 +31,7 @@
       <PackageReference Include="Avalonia.Skia" Version="11.3.2" />
       <PackageReference Include="AvaloniaDialogs" Version="3.6.1" />
       <PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
-      <PackageReference Include="Microsoft.Maui.Essentials" Version="9.0.90" />
+      <PackageReference Include="Microsoft.Maui.Essentials" Version="8.0.100" />
       <PackageReference Include="ReactiveUI" Version="20.4.1" />
       <PackageReference Include="Serilog" Version="4.3.0" />
       <PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />

+ 2 - 4
InABox.Avalonia/MobileDocument/Images/MobileDocumentCameraSource.cs

@@ -1,6 +1,4 @@
 using Avalonia.Controls;
-using Microsoft.Maui.ApplicationModel;
-using Microsoft.Maui.Storage;
 using InABox.Avalonia.Platform;
 
 namespace InABox.Avalonia
@@ -12,9 +10,9 @@ namespace InABox.Avalonia
         }
         
         protected override async Task<bool> IsEnabled() 
-            => await IsEnabled<Permissions.Camera>();
+            => await PlatformTools.Permissions.IsPermitted(Permission.Camera);
         
-        protected override async Task<ImageFile> Capture(TopLevel window) 
+        protected override async Task<ImageFile?> Capture(TopLevel window) 
             => await PlatformTools.ImageTools.CapturePhotoAsync(window, Options.Compression, Options.Constraints);
 
 

+ 2 - 4
InABox.Avalonia/MobileDocument/Images/MobileDocumentPhotoLibrarySource.cs

@@ -1,6 +1,4 @@
 using Avalonia.Controls;
-using Microsoft.Maui.ApplicationModel;
-using Microsoft.Maui.Storage;
 using InABox.Avalonia.Platform;
 
 namespace InABox.Avalonia
@@ -13,9 +11,9 @@ namespace InABox.Avalonia
         }
         
         protected override async Task<bool> IsEnabled()
-            => await IsEnabled<Permissions.Photos>();
+            => await PlatformTools.Permissions.IsPermitted(Permission.VideoLibrary);
 
-        protected override async Task<ImageFile> Capture(TopLevel window) 
+        protected override async Task<ImageFile?> Capture(TopLevel window) 
             => await PlatformTools.ImageTools.PickPhotoAsync(window, Options.Compression, Options.Constraints);
         
     }

+ 20 - 21
InABox.Avalonia/MobileDocument/MobileDocumentSource.cs

@@ -1,6 +1,5 @@
 using Avalonia.Controls;
 using InABox.Avalonia.Platform;
-using Microsoft.Maui.ApplicationModel;
 
 namespace InABox.Avalonia
 {
@@ -23,16 +22,16 @@ namespace InABox.Avalonia
         
         protected abstract Task<bool> IsEnabled();
         
-        protected async Task<bool> IsEnabled<TPermission>()
-            where TPermission : Permissions.BasePermission, new()
-        {
-            var status = await Permissions.CheckStatusAsync<TPermission>();
-            if (status != PermissionStatus.Granted && status != PermissionStatus.Restricted)
-                status = await Permissions.RequestAsync<TPermission>();
-            return status == PermissionStatus.Granted;
-        }
+        // protected async Task<bool> IsEnabled<TPermission>()
+        //     where TPermission : Permissions.BasePermission, new()
+        // {
+        //     var status = await Permissions.CheckStatusAsync<TPermission>();
+        //     if (status != PermissionStatus.Granted && status != PermissionStatus.Restricted)
+        //         status = await Permissions.RequestAsync<TPermission>();
+        //     return status == PermissionStatus.Granted;
+        // }
         
-        protected abstract Task<ImageFile> Capture(TopLevel window);
+        protected abstract Task<ImageFile?> Capture(TopLevel window);
         
         public override async Task<MobileDocument> From(TopLevel window)
         {
@@ -55,20 +54,20 @@ namespace InABox.Avalonia
                     }
                     ApplyOptions(result);
                 }
-                catch (FeatureNotSupportedException fnsEx)
-                {
-                    MobileLogging.Log(fnsEx, "Capture(FNS)");
-                    MobileDialog.ShowMessage("This operation is not Supported on this Device!");
-                }
-                catch (PermissionException pEx)
-                {
-                    MobileLogging.Log(pEx, "Capture(PERM)");
-                    MobileDialog.ShowMessage("This operation is not Permitted on this Device!");
-                }
+                // catch (FeatureNotSupportedException fnsEx)
+                // {
+                //     MobileLogging.Log(fnsEx, "Capture(FNS)");
+                //     MobileDialog.ShowMessage("This operation is not Supported on this Device!");
+                // }
+                // catch (PermissionException pEx)
+                // {
+                //     MobileLogging.Log(pEx, "Capture(PERM)");
+                //     MobileDialog.ShowMessage("This operation is not Permitted on this Device!");
+                // }
                 catch (Exception ex)
                 {
                     MobileLogging.Log(ex, "Capture(ERR)");
-                    MobileDialog.ShowMessage("Oops! Something went wrong!  Please try again..");
+                    MobileDialog.ShowMessage($"Oops! Something went wrong!\n{ex.Message}");
                 }
             }
             return result;

+ 3 - 7
InABox.Avalonia/MobileDocument/Videos/MobileDocumentVideoLibrarySource.cs

@@ -1,10 +1,6 @@
-
-
 using Avalonia.Controls;
 using InABox.Avalonia.Platform;
-using Microsoft.Maui.ApplicationModel;
-using Microsoft.Maui.Media;
-using Microsoft.Maui.Storage;
+
 
 namespace InABox.Avalonia
 {
@@ -16,9 +12,9 @@ namespace InABox.Avalonia
         }
         
         protected override async Task<bool> IsEnabled() 
-            => await IsEnabled<Permissions.Photos>();
+            => await PlatformTools.Permissions.IsPermitted(Permission.VideoLibrary);
 
-        protected override async Task<ImageFile> Capture(TopLevel window)
+        protected override async Task<ImageFile?> Capture(TopLevel window)
             => await PlatformTools.ImageTools.PickVideoAsync(window);
 
 

+ 1 - 4
InABox.Avalonia/MobileDocument/Videos/MobileDocumentVideoSource.cs

@@ -1,8 +1,5 @@
-
-
 using Avalonia.Controls;
 using InABox.Avalonia.Platform;
-using Microsoft.Maui.ApplicationModel;
 
 namespace InABox.Avalonia
 {
@@ -14,7 +11,7 @@ namespace InABox.Avalonia
         }
         
         protected override async Task<bool> IsEnabled() 
-            => await IsEnabled<Permissions.Photos>();
+            => await PlatformTools.Permissions.IsPermitted(Permission.Camera);
         
         protected override async Task<ImageFile> Capture(TopLevel window) 
             => await PlatformTools.ImageTools.CaptureVideoAsync(window);

+ 1 - 21
InABox.Avalonia/MobileUtils.cs

@@ -1,7 +1,4 @@
-using Autofac;
-using InABox.Core;
-using Microsoft.Maui.ApplicationModel;
-
+using InABox.Core;
 
 namespace InABox.Avalonia
 {
@@ -44,23 +41,6 @@ namespace InABox.Avalonia
             return flag;
         }
         
-        public static async Task<bool> IsPermitted<TPermission>() where TPermission : Permissions.BasePermission, new()
-        {
-            try
-            {
-                PermissionStatus status = await Permissions.CheckStatusAsync<TPermission>();
-                if (status == PermissionStatus.Granted)
-                    return true;
-                var request = await Permissions.RequestAsync<TPermission>();
-                return request == PermissionStatus.Granted;
-
-            }
-            catch (TaskCanceledException ex)
-            {
-                return false;
-            }
-        }
-        
         // public static async Task<bool> CheckPermissions<TPermission>() where TPermission : Permissions.BasePermission, new()
         // {
         //     var permissionStatus = await CrossPermissions.Current.CheckPermissionStatusAsync(permission);