Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
PlatformSpecific.cs
1using Foundation;
2using LocalAuthentication;
4using ObjCRuntime;
5using System.Diagnostics.CodeAnalysis;
6using UIKit;
7using Waher.Events;
9using Security;
10using System.Text;
11using CommunityToolkit.Mvvm.Messaging;
13{
18 {
19 private LAContext? localAuthenticationContext;
20 private bool isDisposed;
21
26 {
27 NSNotificationCenter.DefaultCenter.AddObserver(UIKeyboard.WillShowNotification, this.OnKeyboardWillShow);
28 NSNotificationCenter.DefaultCenter.AddObserver(UIKeyboard.WillHideNotification, this.OnKeyboardWillHide);
29 }
30
34 public void Dispose()
35 {
36 this.Dispose(true);
37 GC.SuppressFinalize(this);
38 }
39
43 protected virtual void Dispose(bool Disposing)
44 {
45 if (this.isDisposed)
46 return;
47
48 NSNotificationCenter.DefaultCenter.RemoveObserver(UIKeyboard.WillShowNotification);
49 NSNotificationCenter.DefaultCenter.RemoveObserver(UIKeyboard.WillHideNotification);
50
51 if (Disposing)
52 this.DisposeLocalAuthenticationContext();
53
54 this.isDisposed = true;
55 }
56
60 public bool CanProhibitScreenCapture => false;
61
65 public bool ProhibitScreenCapture // iOS doesn't support screen protection
66 {
67 get => false;
68 set => _ = value; // ignore the value
69 }
70
76 public string? GetDeviceId()
77 {
78 try
79 {
80 string ServiceName = AppInfo.PackageName;
81 const string AccountName = "DeviceIdentifier"; //Basically the key
82
83 // Define the search criteria for the SecRecord
84 SecRecord searchRecord = new(SecKind.GenericPassword)
85 {
86 Service = ServiceName,
87 Account = AccountName
88 };
89
90 // Try to retrieve the existing device identifier from the Keychain
91 SecRecord? existingRecord = SecKeyChain.QueryAsRecord(searchRecord, out SecStatusCode resultCode);
92 if (resultCode == SecStatusCode.Success && existingRecord is not null && existingRecord?.ValueData is not null)
93 {
94 // If the record exists, return the identifier
95 return existingRecord.ValueData.ToString(NSStringEncoding.UTF8);
96 }
97 else if (resultCode == SecStatusCode.ItemNotFound)
98 {
99 // No existing record found, create a new device identifier
100 string identifier = UIDevice.CurrentDevice.IdentifierForVendor.ToString();
101
102 // Define the SecRecord for storing the new identifier
103 SecRecord newRecord = new(SecKind.GenericPassword)
104 {
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
111 };
112
113 // Sanity check: Remove any existing record, which should not exist
114 SecKeyChain.Remove(newRecord);
115
116 // Add the new item to the Keychain
117 SecStatusCode addResult = SecKeyChain.Add(newRecord);
118 if (addResult == SecStatusCode.Success)
119 return identifier; // Return the newly stored identifier
120
121 throw new Exception($"Unable to store device identifier in Keychain - Code: {addResult} - Description: {SecStatusCodeExtensions.GetStatusDescription(addResult)}");
122 }
123 else
124 throw new Exception($"Unable to retrieve device identifier from Keychain - Code: {resultCode} - Description: {SecStatusCodeExtensions.GetStatusDescription(resultCode)}");
125 }
126 catch (Exception ex)
127 {
128 try
129 {
132
133 StringBuilder msg = new();
134
135 msg.Append(ex.Message);
136 msg.AppendLine();
137 msg.AppendLine("```");
138 msg.AppendLine(ex.StackTrace);
139 msg.AppendLine("```");
140
141 App.SendAlert(msg.ToString(), "text/plain").Wait();
142 this.CloseApplication().Wait();
143 }
144 catch (Exception)
145 {
146 Environment.Exit(0);
147 }
148 }
149 return null;
150 }
151
155 public Task CloseApplication()
156 {
157 if (this.localAuthenticationContext is not null)
158 {
159 if (this.localAuthenticationContext.RespondsToSelector(new Selector("invalidate")))
160 this.localAuthenticationContext.Invalidate();
161
162 this.localAuthenticationContext.Dispose();
163 this.localAuthenticationContext = null;
164 }
165
166 Environment.Exit(0);
167 return Task.CompletedTask;
168 }
169
177 public void ShareImage(byte[] PngFile, string Message, string Title, string FileName)
178 {
179 UIImage? ImageObject = UIImage.LoadFromData(NSData.FromArray(PngFile));
180 UIWindow? KeyWindow = UIApplication.SharedApplication?.KeyWindow;
181
182 if ((ImageObject is null) || (KeyWindow is null))
183 return;
184
185 NSString MessageObject = new(Message);
186 NSObject[] Items = [MessageObject, ImageObject];
187 UIActivityViewController activityController = new(Items, null);
188
189 UIViewController? topController = KeyWindow.RootViewController;
190
191 if (topController is not null)
192 {
193 while (topController.PresentedViewController is not null)
194 topController = topController.PresentedViewController;
195
196 topController.PresentViewController(activityController, true, () => { });
197 }
198 }
199
200
205 public Task<byte[]> CaptureScreen(int blurRadius = 25)
206 {
207 blurRadius = Math.Min(25, Math.Max(blurRadius, 0));
208 UIImage? capture;
209
210 using UIBlurEffect blurEffect = UIBlurEffect.FromStyle(UIBlurEffectStyle.Regular);
211 using UIVisualEffectView blurWindow = new(blurEffect);
212
213 blurWindow.Frame = UIScreen.MainScreen.Bounds;
214 blurWindow.Alpha = Math.Min(1.0f, (1.0f / 25.0f) * blurRadius);
215
216 UIView? subview = UIScreen.MainScreen.SnapshotView(true);
217 //capture = UIScreen.MainScreen.Capture();
218 //var subview = new UIImageView(capture);
219 subview?.AddSubview(blurWindow);
220 capture = subview?.Capture(true);
221 blurWindow.RemoveFromSuperview();
222 subview?.Dispose();
223
225 return Task.FromResult(Array.Empty<byte>());
226 }
227
232 {
233 get
234 {
235 if (!this.HasLocalAuthenticationContext)
236 return false;
237
238 try
239 {
240 if (!this.localAuthenticationContext.CanEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, out _))
241 return false;
242
243 return true;
244 }
245 catch (Exception ex)
246 {
247 ServiceRef.LogService.LogException(ex);
248 return false;
249 }
250 }
251 }
252
259 {
260 if (!this.HasLocalAuthenticationContext)
261 return BiometricMethod.None;
262
263 try
264 {
265 if (!this.localAuthenticationContext.CanEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, out _))
266 return BiometricMethod.None;
267
268 return this.localAuthenticationContext.BiometryType switch
269 {
270 LABiometryType.FaceId => BiometricMethod.FaceId,
271 LABiometryType.TouchId => BiometricMethod.TouchId,
272 _ => BiometricMethod.Unknown
273 };
274 }
275 catch (Exception ex)
276 {
277 ServiceRef.LogService.LogException(ex);
278 return BiometricMethod.Unknown;
279 }
280 }
281
282 [MemberNotNullWhen(true, nameof(localAuthenticationContext))]
283 private bool HasLocalAuthenticationContext
284 {
285 get
286 {
287 try
288 {
289 if (this.localAuthenticationContext is null)
290 {
291 NSProcessInfo ProcessInfo = new();
292 NSOperatingSystemVersion MinVersion = new(10, 12, 0);
293 if (!ProcessInfo.IsOperatingSystemAtLeastVersion(MinVersion))
294 return false;
295
296 if (!UIDevice.CurrentDevice.CheckSystemVersion(8, 0))
297 return false;
298
299 if (Class.GetHandle(typeof(LAContext)) == IntPtr.Zero)
300 return false;
301
302 this.localAuthenticationContext = new LAContext();
303 }
304
305 return true;
306 }
307 catch (Exception ex)
308 {
309 ServiceRef.LogService.LogException(ex);
310 return false;
311 }
312 }
313 }
314
315 private void DisposeLocalAuthenticationContext()
316 {
317 if (this.localAuthenticationContext is not null)
318 {
319 if (this.localAuthenticationContext.RespondsToSelector(new Selector("invalidate")))
320 this.localAuthenticationContext.Invalidate();
321
322 this.localAuthenticationContext.Dispose();
323 this.localAuthenticationContext = null;
324 }
325 }
326
337 public async Task<bool> AuthenticateUserFingerprint(string Title, string? Subtitle, string Description, string Cancel,
338 CancellationToken? CancellationToken)
339 {
340 if (!this.HasLocalAuthenticationContext)
341 return false;
342
343 CancellationTokenRegistration? Registration = null;
344
345 try
346 {
347 if (this.localAuthenticationContext.RespondsToSelector(new Selector("localizedFallbackTitle")))
348 this.localAuthenticationContext.LocalizedFallbackTitle = Title;
349
350 if (this.localAuthenticationContext.RespondsToSelector(new Selector("localizedCancelTitle")))
351 this.localAuthenticationContext.LocalizedCancelTitle = Cancel;
352
353 Registration = CancellationToken?.Register(this.DisposeLocalAuthenticationContext);
354
355 (bool Success, NSError _) = await this.localAuthenticationContext.EvaluatePolicyAsync(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, Description);
356
357 this.DisposeLocalAuthenticationContext();
358
359 return Success;
360 }
361 catch (Exception ex)
362 {
363 ServiceRef.LogService.LogException(ex);
364 return false;
365 }
366 finally
367 {
368 if (Registration.HasValue)
369 Registration.Value.Dispose();
370 }
371 }
372
377 public async Task<TokenInformation> GetPushNotificationToken()
378 {
379 string Token = string.Empty;
380
381 try
382 {
383 //Token = Firebase.CloudMessaging.Messaging.SharedInstance.FcmToken ?? string.Empty;
384 }
385 catch (Exception ex)
386 {
387 Log.Exception(ex);
388 }
389
391 {
392 Token = Token,
394 Service = PushMessagingService.Firebase
395 };
396
397 return await Task.FromResult(TokenInformation);
398 }
399
400 #region Keyboard
401 public event EventHandler<KeyboardSizeMessage>? KeyboardShown;
402 public event EventHandler<KeyboardSizeMessage>? KeyboardHidden;
403 public event EventHandler<KeyboardSizeMessage>? KeyboardSizeChanged;
404
408 public void HideKeyboard()
409 {
410 AppDelegate.GetKeyWindow()?.EndEditing(true);
411 }
412
413 private void OnKeyboardWillShow(NSNotification notification)
414 {
415 CoreGraphics.CGRect keyboardFrame = UIKeyboard.FrameEndFromNotification(notification);
416 float keyboardHeight = (float)keyboardFrame.Height;
417 KeyboardShown?.Invoke(this, new KeyboardSizeMessage(keyboardHeight));
418 KeyboardSizeChanged?.Invoke(this, new KeyboardSizeMessage(keyboardHeight));
419 WeakReferenceMessenger.Default.Send(new KeyboardSizeMessage(keyboardHeight));
420 }
421
422 private void OnKeyboardWillHide(NSNotification notification)
423 {
424 float keyboardHeight = 0;
425 KeyboardShown?.Invoke(this, new KeyboardSizeMessage(keyboardHeight));
426 KeyboardSizeChanged?.Invoke(this, new KeyboardSizeMessage(keyboardHeight));
427 WeakReferenceMessenger.Default.Send(new KeyboardSizeMessage(keyboardHeight));
428 }
429
430
431 #endregion
432
433 }
434}
The Application class, representing an instance of the Neuro-Access app.
Definition: App.xaml.cs:69
Android implementation of platform-specific features.
Task< byte[]> CaptureScreen(int blurRadius=25)
Make a blurred screenshot TODO: Just make a screen shot. Use the portable CV library to blur it.
EventHandler< KeyboardSizeMessage >? KeyboardShown
bool CanProhibitScreenCapture
If screen capture prohibition is supported
void ShareImage(byte[] PngFile, string Message, string Title, string FileName)
Shares an image in PNG format.
PlatformSpecific()
iOS implementation of platform-specific features.
void HideKeyboard()
Force hide the keyboard
bool SupportsFingerprintAuthentication
If the device supports authenticating the user using fingerprints.
BiometricMethod GetBiometricMethod()
Gets the biometric method supported by the device. Can return Face, Fingerprint, Unknown,...
async Task< bool > AuthenticateUserFingerprint(string Title, string? Subtitle, string Description, string Cancel, CancellationToken? CancellationToken)
Authenticates the user using the fingerprint sensor.
virtual void Dispose(bool Disposing)
IDisposable.Dispose
EventHandler< KeyboardSizeMessage >? KeyboardSizeChanged
Fired when the keyboard size changes.
string? GetDeviceId()
Gets A persistent ID of the device Fetches the device ID from the keychain, or creates a new one if i...
async Task< TokenInformation > GetPushNotificationToken()
Gets a Push Notification token for the device.
EventHandler< KeyboardSizeMessage >? KeyboardHidden
bool ProhibitScreenCapture
If screen capture is prohibited or not.
Task CloseApplication()
Closes the application
Contains information about a push notification token.
Base class that references services in the app.
Definition: ServiceRef.cs:31
static ILogService LogService
Log service.
Definition: ServiceRef.cs:91
Static class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:13
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.
Definition: Log.cs:1647
Interface for platform-specific functions.
BiometricMethod
Enum representing the device biometric method for authentication.
class KeyboardSizeMessage(float KeyboardSize)
Keyboard size change message
Definition: Messages.cs:19
ClientType
Type of client requesting notification.
Definition: ClientType.cs:7