2using LocalAuthentication;
5using System.Diagnostics.CodeAnalysis;
11using CommunityToolkit.Mvvm.Messaging;
19 private LAContext? localAuthenticationContext;
20 private bool isDisposed;
27 NSNotificationCenter.DefaultCenter.AddObserver(UIKeyboard.WillShowNotification,
this.OnKeyboardWillShow);
28 NSNotificationCenter.DefaultCenter.AddObserver(UIKeyboard.WillHideNotification,
this.OnKeyboardWillHide);
37 GC.SuppressFinalize(
this);
43 protected virtual void Dispose(
bool Disposing)
48 NSNotificationCenter.DefaultCenter.RemoveObserver(UIKeyboard.WillShowNotification);
49 NSNotificationCenter.DefaultCenter.RemoveObserver(UIKeyboard.WillHideNotification);
52 this.DisposeLocalAuthenticationContext();
54 this.isDisposed =
true;
80 string ServiceName = AppInfo.PackageName;
81 const string AccountName =
"DeviceIdentifier";
84 SecRecord searchRecord =
new(SecKind.GenericPassword)
86 Service = ServiceName,
91 SecRecord? existingRecord = SecKeyChain.QueryAsRecord(searchRecord, out SecStatusCode resultCode);
92 if (resultCode == SecStatusCode.Success && existingRecord is not
null && existingRecord?.ValueData is not
null)
95 return existingRecord.ValueData.ToString(NSStringEncoding.UTF8);
97 else if (resultCode == SecStatusCode.ItemNotFound)
100 string identifier = UIDevice.CurrentDevice.IdentifierForVendor.ToString();
103 SecRecord newRecord =
new(SecKind.GenericPassword)
105 Service = ServiceName,
106 Account = AccountName,
107 Label =
"Persistent Device Identifier for Vendor",
108 ValueData = NSData.FromString(identifier),
109 Accessible = SecAccessible.WhenUnlockedThisDeviceOnly,
110 Synchronizable =
false
114 SecKeyChain.Remove(newRecord);
117 SecStatusCode addResult = SecKeyChain.Add(newRecord);
118 if (addResult == SecStatusCode.Success)
121 throw new Exception($
"Unable to store device identifier in Keychain - Code: {addResult} - Description: {SecStatusCodeExtensions.GetStatusDescription(addResult)}");
124 throw new Exception($
"Unable to retrieve device identifier from Keychain - Code: {resultCode} - Description: {SecStatusCodeExtensions.GetStatusDescription(resultCode)}");
133 StringBuilder msg =
new();
135 msg.Append(ex.Message);
137 msg.AppendLine(
"```");
138 msg.AppendLine(ex.StackTrace);
139 msg.AppendLine(
"```");
141 App.SendAlert(msg.ToString(),
"text/plain").Wait();
157 if (this.localAuthenticationContext is not
null)
159 if (this.localAuthenticationContext.RespondsToSelector(
new Selector(
"invalidate")))
160 this.localAuthenticationContext.Invalidate();
162 this.localAuthenticationContext.Dispose();
163 this.localAuthenticationContext =
null;
167 return Task.CompletedTask;
177 public void ShareImage(
byte[] PngFile,
string Message,
string Title,
string FileName)
179 UIImage? ImageObject = UIImage.LoadFromData(NSData.FromArray(PngFile));
180 UIWindow? KeyWindow = UIApplication.SharedApplication?.KeyWindow;
182 if ((ImageObject is
null) || (KeyWindow is
null))
185 NSString MessageObject =
new(Message);
186 NSObject[] Items = [MessageObject, ImageObject];
187 UIActivityViewController activityController =
new(Items,
null);
189 UIViewController? topController = KeyWindow.RootViewController;
191 if (topController is not
null)
193 while (topController.PresentedViewController is not
null)
194 topController = topController.PresentedViewController;
196 topController.PresentViewController(activityController,
true, () => { });
207 blurRadius = Math.Min(25, Math.Max(blurRadius, 0));
210 using UIBlurEffect blurEffect = UIBlurEffect.FromStyle(UIBlurEffectStyle.Regular);
211 using UIVisualEffectView blurWindow =
new(blurEffect);
213 blurWindow.Frame = UIScreen.MainScreen.Bounds;
214 blurWindow.Alpha = Math.Min(1.0f, (1.0f / 25.0f) * blurRadius);
216 UIView? subview = UIScreen.MainScreen.SnapshotView(
true);
219 subview?.AddSubview(blurWindow);
220 capture = subview?.Capture(
true);
221 blurWindow.RemoveFromSuperview();
225 return Task.FromResult(Array.Empty<
byte>());
235 if (!this.HasLocalAuthenticationContext)
240 if (!this.localAuthenticationContext.CanEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, out _))
260 if (!this.HasLocalAuthenticationContext)
265 if (!this.localAuthenticationContext.CanEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, out _))
268 return this.localAuthenticationContext.BiometryType
switch
272 _ => BiometricMethod.Unknown
282 [MemberNotNullWhen(
true, nameof(localAuthenticationContext))]
283 private bool HasLocalAuthenticationContext
289 if (this.localAuthenticationContext is
null)
291 NSProcessInfo ProcessInfo =
new();
292 NSOperatingSystemVersion MinVersion =
new(10, 12, 0);
293 if (!ProcessInfo.IsOperatingSystemAtLeastVersion(MinVersion))
296 if (!UIDevice.CurrentDevice.CheckSystemVersion(8, 0))
299 if (Class.GetHandle(typeof(LAContext)) == IntPtr.Zero)
302 this.localAuthenticationContext =
new LAContext();
315 private void DisposeLocalAuthenticationContext()
317 if (this.localAuthenticationContext is not
null)
319 if (this.localAuthenticationContext.RespondsToSelector(
new Selector(
"invalidate")))
320 this.localAuthenticationContext.Invalidate();
322 this.localAuthenticationContext.Dispose();
323 this.localAuthenticationContext =
null;
338 CancellationToken? CancellationToken)
340 if (!this.HasLocalAuthenticationContext)
343 CancellationTokenRegistration? Registration =
null;
347 if (this.localAuthenticationContext.RespondsToSelector(
new Selector(
"localizedFallbackTitle")))
348 this.localAuthenticationContext.LocalizedFallbackTitle = Title;
350 if (this.localAuthenticationContext.RespondsToSelector(
new Selector(
"localizedCancelTitle")))
351 this.localAuthenticationContext.LocalizedCancelTitle = Cancel;
353 Registration = CancellationToken?.Register(this.DisposeLocalAuthenticationContext);
355 (
bool Success, NSError _) = await this.localAuthenticationContext.EvaluatePolicyAsync(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, Description);
357 this.DisposeLocalAuthenticationContext();
368 if (Registration.HasValue)
369 Registration.Value.Dispose();
379 string Token =
string.Empty;
394 Service = PushMessagingService.Firebase
401 public event EventHandler<KeyboardSizeMessage>?
KeyboardShown;
413 private void OnKeyboardWillShow(NSNotification notification)
415 CoreGraphics.CGRect keyboardFrame = UIKeyboard.FrameEndFromNotification(notification);
416 float keyboardHeight = (float)keyboardFrame.Height;
422 private void OnKeyboardWillHide(NSNotification notification)
424 float keyboardHeight = 0;
The Application class, representing an instance of the Neuro-Access app.
Base class that references services in the app.
static ILogService LogService
Log service.
Static class managing the application event log. Applications and services log events on this static ...
static void Exception(Exception Exception, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, params KeyValuePair< string, object >[] Tags)
Logs an exception. Event type will be determined by the severity of the exception.
BiometricMethod
Enum representing the device biometric method for authentication.
class KeyboardSizeMessage(float KeyboardSize)
Keyboard size change message
ClientType
Type of client requesting notification.