2using LocalAuthentication;
5using System.Diagnostics.CodeAnalysis;
11using CommunityToolkit.Mvvm.Messaging;
12using Plugin.Firebase.CloudMessaging;
13using UserNotifications;
22 private LAContext? localAuthenticationContext;
23 private bool isDisposed;
30 NSNotificationCenter.DefaultCenter.AddObserver(UIKeyboard.WillShowNotification,
this.OnKeyboardWillShow);
31 NSNotificationCenter.DefaultCenter.AddObserver(UIKeyboard.WillHideNotification,
this.OnKeyboardWillHide);
40 GC.SuppressFinalize(
this);
46 protected virtual void Dispose(
bool Disposing)
51 NSNotificationCenter.DefaultCenter.RemoveObserver(UIKeyboard.WillShowNotification);
52 NSNotificationCenter.DefaultCenter.RemoveObserver(UIKeyboard.WillHideNotification);
55 this.DisposeLocalAuthenticationContext();
57 this.isDisposed =
true;
83 string ServiceName = AppInfo.PackageName;
84 const string AccountName =
"DeviceIdentifier";
87 SecRecord searchRecord =
new(SecKind.GenericPassword)
89 Service = ServiceName,
94 SecRecord? existingRecord = SecKeyChain.QueryAsRecord(searchRecord, out SecStatusCode resultCode);
95 if (resultCode == SecStatusCode.Success && existingRecord is not
null && existingRecord?.ValueData is not
null)
98 return existingRecord.ValueData.ToString(NSStringEncoding.UTF8);
100 else if (resultCode == SecStatusCode.ItemNotFound)
103 string identifier = UIDevice.CurrentDevice.IdentifierForVendor.ToString();
106 SecRecord newRecord =
new(SecKind.GenericPassword)
108 Service = ServiceName,
109 Account = AccountName,
110 Label =
"Persistent Device Identifier for Vendor",
111 ValueData = NSData.FromString(identifier),
112 Accessible = SecAccessible.WhenUnlockedThisDeviceOnly,
113 Synchronizable =
false
117 SecKeyChain.Remove(newRecord);
120 SecStatusCode addResult = SecKeyChain.Add(newRecord);
121 if (addResult == SecStatusCode.Success)
124 throw new Exception($
"Unable to store device identifier in Keychain - Code: {addResult} - Description: {SecStatusCodeExtensions.GetStatusDescription(addResult)}");
127 throw new Exception($
"Unable to retrieve device identifier from Keychain - Code: {resultCode} - Description: {SecStatusCodeExtensions.GetStatusDescription(resultCode)}");
136 StringBuilder msg =
new();
138 msg.Append(ex.Message);
139 msg.AppendLine(
"\n\n");
140 msg.AppendLine(
"```");
141 msg.AppendLine(ex.StackTrace);
142 msg.AppendLine(
"```");
144 App.SendAlertAsync(msg.ToString(),
"text/plain").Wait();
160 if (this.localAuthenticationContext is not
null)
162 if (this.localAuthenticationContext.RespondsToSelector(
new Selector(
"invalidate")))
163 this.localAuthenticationContext.Invalidate();
165 this.localAuthenticationContext.Dispose();
166 this.localAuthenticationContext =
null;
170 return Task.CompletedTask;
180 public void ShareImage(
byte[] PngFile,
string Message,
string Title,
string FileName)
182 UIImage? ImageObject = UIImage.LoadFromData(NSData.FromArray(PngFile));
183 UIWindow? KeyWindow = UIApplication.SharedApplication?.KeyWindow;
185 if ((ImageObject is
null) || (KeyWindow is
null))
188 NSString MessageObject =
new(Message);
189 NSObject[] Items = [MessageObject, ImageObject];
190 UIActivityViewController activityController =
new(Items,
null);
192 UIViewController? topController = KeyWindow.RootViewController;
194 if (topController is not
null)
196 while (topController.PresentedViewController is not
null)
197 topController = topController.PresentedViewController;
199 topController.PresentViewController(activityController,
true, () => { });
238 if (!this.HasLocalAuthenticationContext)
243 if (!this.localAuthenticationContext.CanEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, out
_))
263 if (!this.HasLocalAuthenticationContext)
268 if (!this.localAuthenticationContext.CanEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, out
_))
271 return this.localAuthenticationContext.BiometryType
switch
275 _ => BiometricMethod.Unknown
285 [MemberNotNullWhen(
true, nameof(localAuthenticationContext))]
286 private bool HasLocalAuthenticationContext
292 if (this.localAuthenticationContext is
null)
294 NSProcessInfo ProcessInfo =
new();
295 NSOperatingSystemVersion MinVersion =
new(10, 12, 0);
296 if (!ProcessInfo.IsOperatingSystemAtLeastVersion(MinVersion))
299 if (!UIDevice.CurrentDevice.CheckSystemVersion(8, 0))
302 if (Class.GetHandle(typeof(LAContext)) == IntPtr.Zero)
305 this.localAuthenticationContext =
new LAContext();
318 private void DisposeLocalAuthenticationContext()
320 if (this.localAuthenticationContext is not
null)
322 if (this.localAuthenticationContext.RespondsToSelector(
new Selector(
"invalidate")))
323 this.localAuthenticationContext.Invalidate();
325 this.localAuthenticationContext.Dispose();
326 this.localAuthenticationContext =
null;
341 CancellationToken? CancellationToken)
343 if (!this.HasLocalAuthenticationContext)
346 CancellationTokenRegistration? Registration =
null;
350 if (this.localAuthenticationContext.RespondsToSelector(
new Selector(
"localizedFallbackTitle")))
351 this.localAuthenticationContext.LocalizedFallbackTitle = Title;
353 if (this.localAuthenticationContext.RespondsToSelector(
new Selector(
"localizedCancelTitle")))
354 this.localAuthenticationContext.LocalizedCancelTitle = Cancel;
356 Registration = CancellationToken?.Register(this.DisposeLocalAuthenticationContext);
358 (
bool Success, NSError
_) = await this.localAuthenticationContext.EvaluatePolicyAsync(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, Description);
360 this.DisposeLocalAuthenticationContext();
371 if (Registration.HasValue)
372 Registration.Value.Dispose();
382 string Token =
string.Empty;
386 await CrossFirebaseCloudMessaging.Current.CheckIfValidAsync();
387 Token = await CrossFirebaseCloudMessaging.Current.GetTokenAsync();
398 Service = PushMessagingService.Firebase
405 public event EventHandler<KeyboardSizeMessage>?
KeyboardShown;
418 private void OnKeyboardWillShow(NSNotification notification)
420 CoreGraphics.CGRect keyboardFrame = UIKeyboard.FrameEndFromNotification(notification);
421 float keyboardHeight = (float)keyboardFrame.Height;
427 private void OnKeyboardWillHide(NSNotification notification)
429 float keyboardHeight = 0;
438 #region Notifications
445 private async
void ShowLocalNotification(
string title,
string body, IDictionary<string, string> data)
450 UNNotificationSettings Settings = await UNUserNotificationCenter.Current.GetNotificationSettingsAsync();
451 if (Settings.AuthorizationStatus != UNAuthorizationStatus.Authorized)
455 UNMutableNotificationContent Content =
new UNMutableNotificationContent
459 Sound = UNNotificationSound.Default
463 if (data is not
null)
465 NSMutableDictionary UserInfo =
new();
466 foreach (KeyValuePair<string, string> Pair
in data)
468 UserInfo.SetValueForKey(
new NSString(Pair.Value),
new NSString(Pair.Key));
470 Content.UserInfo = UserInfo;
474 UNTimeIntervalNotificationTrigger Trigger = UNTimeIntervalNotificationTrigger.CreateTrigger(1,
false);
475 UNNotificationRequest Request = UNNotificationRequest.FromIdentifier(Guid.NewGuid().ToString(), Content, Trigger);
476 UNUserNotificationCenter.Current.AddNotificationRequest(Request, error =>
478 if (error is not
null)
480 ServiceRef.
LogService.LogWarning($
"Error scheduling notification: {error.LocalizedDescription}");
492 public void ShowMessageNotification(
string Title,
string MessageBody, IDictionary<string, string> Data)
494 this.ShowLocalNotification(Title, MessageBody, Data);
497 public void ShowIdentitiesNotification(
string Title,
string MessageBody, IDictionary<string, string> Data)
499 this.ShowLocalNotification(Title, MessageBody, Data);
502 public void ShowPetitionNotification(
string Title,
string MessageBody, IDictionary<string, string> Data)
504 this.ShowLocalNotification(Title, MessageBody, Data);
507 public void ShowContractsNotification(
string Title,
string MessageBody, IDictionary<string, string> Data)
509 this.ShowLocalNotification(Title, MessageBody, Data);
512 public void ShowEDalerNotification(
string Title,
string MessageBody, IDictionary<string, string> Data)
514 this.ShowLocalNotification(Title, MessageBody, Data);
517 public void ShowTokenNotification(
string Title,
string MessageBody, IDictionary<string, string> Data)
519 this.ShowLocalNotification(Title, MessageBody, Data);
522 public void ShowProvisioningNotification(
string Title,
string MessageBody, IDictionary<string, string> Data)
524 this.ShowLocalNotification(Title, MessageBody, Data);
539 return new Thickness(0);
542 UIView? RootView = Window.RootViewController?.View;
543 if (RootView is not
null)
545 CoreGraphics.CGRect Bounds = RootView.Bounds;
546 CoreGraphics.CGRect LayoutFrame = RootView.SafeAreaLayoutGuide.LayoutFrame;
548 double Left = LayoutFrame.Left - Bounds.Left;
549 double Top = LayoutFrame.Top - Bounds.Top;
550 double Right = Bounds.Right - LayoutFrame.Right;
551 double Bottom = Bounds.Bottom - LayoutFrame.Bottom;
554 if (Left >= 0 && Top >= 0 && Right >= 0 && Bottom >= 0)
555 return new Thickness(Left, Top, Right, Bottom);
559 UIEdgeInsets Insets = Window.SafeAreaInsets;
560 return new Thickness(
563 (
double)Insets.Right,
564 (
double)Insets.Bottom);
Represents 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.