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 Android;
2using Android.App;
3using Android.Content;
4using Android.Content.PM;
5//using Android.Gms.Extensions;
6using Android.Graphics;
7using Android.OS;
8using Android.Renderscripts;
9using Android.Runtime;
10using Android.Views;
11using Android.Views.InputMethods;
12using AndroidX.Biometric;
13using AndroidX.Fragment.App;
14using AndroidX.Lifecycle;
15using CommunityToolkit.Mvvm.Messaging;
16
17//using Firebase.Messaging; // TODO: Firebase
18using Java.Util.Concurrent;
20using Waher.Events;
22using Debug = System.Diagnostics.Debug;
23using Rect = Android.Graphics.Rect;
24
26{
31 {
32 private bool isDisposed;
33
38 {
39 this.InitializeKeyboard();
40 }
41
45 public void Dispose()
46 {
47 this.Dispose(true);
48 GC.SuppressFinalize(this);
49 }
50
54 protected virtual void Dispose(bool Disposing)
55 {
56 if (this.isDisposed)
57 return;
58
59 if (Disposing)
60 {
61 protectionTimer?.Dispose();
62 protectionTimer = null;
63 }
64
65 this.isDisposed = true;
66 }
67
68 private static bool screenProtected = true; // App started with screen protected.
69 private static Timer? protectionTimer = null;
70
74 public bool CanProhibitScreenCapture => true;
75
80 {
81 get => screenProtected;
82 set
83 {
84 try
85 {
86 protectionTimer?.Dispose();
87 protectionTimer = null;
88
89 if (screenProtected != value)
90 {
91 this.SetScreenSecurityProtection(value);
92 screenProtected = value;
93 }
94 }
95 catch (Exception ex)
96 {
97 Log.Exception(ex);
98 }
99 }
100 }
101
102 private void SetScreenSecurityProtection(bool Enabled)
103 {
104 MainThread.BeginInvokeOnMainThread(() =>
105 {
106 try
107 {
108 Activity? Activity = Platform.CurrentActivity;
109
110 if (Activity is not null)
111 {
112 if (Build.VERSION.SdkInt >= BuildVersionCodes.Tiramisu)
113 {
114#pragma warning disable CA1416
115 Activity.SetRecentsScreenshotEnabled(!Enabled);
116#pragma warning restore CA1416
117 }
118
119 if (Enabled)
120 {
121 Activity.Window?.SetFlags(WindowManagerFlags.Secure, WindowManagerFlags.Secure);
122 protectionTimer = new Timer(this.ProtectionTimerElapsed, null,
123 Constants.Security.MaxScreenRecordingTimeSeconds * 1000, Timeout.Infinite);
124 }
125 else
126 Activity.Window?.ClearFlags(WindowManagerFlags.Secure);
127 }
128 }
129 catch (Exception ex)
130 {
131 Log.Exception(ex);
132 }
133 });
134 }
135
136 private void ProtectionTimerElapsed(object? P)
137 {
138 MainThread.BeginInvokeOnMainThread(() => this.ProhibitScreenCapture = false);
139 }
140
144 public string? GetDeviceId()
145 {
146 ContentResolver? ContentResolver = Android.App.Application.Context.ContentResolver;
147
148 if (ContentResolver is not null)
149 {
150 return Android.Provider.Settings.Secure.GetString(ContentResolver, Android.Provider.Settings.Secure.AndroidId);
151 }
152 try
153 {
154 App.SendAlert("Unable to get AndroidID, ContentResolver was null", "text/plain").Wait();
155 this.CloseApplication().Wait();
156 }
157 catch (Exception)
158 {
159 System.Environment.Exit(0);
160 }
161
162 return null;
163 }
164
165
169 public Task CloseApplication()
170 {
171 Activity? Activity = Platform.CurrentActivity;
172 Activity?.FinishAffinity();
173
174 Java.Lang.JavaSystem.Exit(0);
175
176 return Task.CompletedTask;
177 }
178
186 public void ShareImage(byte[] PngFile, string Message, string Title, string FileName)
187 {
188 Context Context = Android.App.Application.Context;
189 Java.IO.File? ExternalFilesDir = Context.GetExternalFilesDir("");
190
191 if (ExternalFilesDir is null)
192 return;
193
194 if (!Directory.Exists(ExternalFilesDir.Path))
195 Directory.CreateDirectory(ExternalFilesDir.Path);
196
197 Java.IO.File fileDir = new(ExternalFilesDir.AbsolutePath + (Java.IO.File.Separator + FileName));
198
199 File.WriteAllBytes(fileDir.Path, PngFile);
200
201 Intent Intent = new(Intent.ActionSend);
202 Intent.PutExtra(Intent.ExtraText, Message);
203 Intent.SetType(Constants.MimeTypes.Png);
204
205 Intent.AddFlags(ActivityFlags.GrantReadUriPermission);
206 Intent.AddFlags(ActivityFlags.GrantWriteUriPermission);
207 Intent.PutExtra(Intent.ExtraStream, FileProvider.GetUriForFile(Context, "com.tag.IdApp.fileprovider", fileDir));
208
209 Intent? MyIntent = Intent.CreateChooser(Intent, Title);
210
211 if (MyIntent is not null)
212 {
213 MyIntent.AddFlags(ActivityFlags.NewTask);
214 Context.StartActivity(MyIntent);
215 }
216 }
217
218
223 public Task<byte[]> CaptureScreen(int blurRadius)
224 {
225 blurRadius = Math.Min(25, Math.Max(blurRadius, 0));
226
227 Activity? activity = Platform.CurrentActivity;
228 Android.Views.View? rootView = activity?.Window?.DecorView.RootView;
229
230 if (rootView is null)
231 return Task.FromResult<byte[]>([]);
232
233 using Bitmap screenshot = Bitmap.CreateBitmap(rootView.Width, rootView.Height, Bitmap.Config.Argb8888!);
234 Canvas canvas = new(screenshot);
235 rootView.Draw(canvas);
236
237 Bitmap? Blurred = null;
238
239 if (activity != null && (int)Android.OS.Build.VERSION.SdkInt >= 17)
240 Blurred = ToBlurred(screenshot, activity, blurRadius);
241 else
242 Blurred = ToLegacyBlurred(screenshot, blurRadius);
243
244 MemoryStream Stream = new();
245 Blurred.Compress(Bitmap.CompressFormat.Jpeg!, 80, Stream);
246 Stream.Seek(0, SeekOrigin.Begin);
247
248 return Task.FromResult(Stream.ToArray());
249 }
250
251 private static Bitmap ToBlurred(Bitmap originalBitmap, Activity? Activity, int radius)
252 {
253 // Create another bitmap that will hold the results of the filter.
254 Bitmap blurredBitmap = Bitmap.CreateBitmap(originalBitmap);
255 RenderScript? renderScript = RenderScript.Create(Activity);
256
257 // Load up an instance of the specific script that we want to use.
258 // An Element is similar to a C type. The second parameter, Element.U8_4,
259 // tells the Allocation is made up of 4 fields of 8 unsigned bits.
260 ScriptIntrinsicBlur? script = ScriptIntrinsicBlur.Create(renderScript, Android.Renderscripts.Element.U8_4(renderScript));
261
262 // Create an Allocation for the kernel inputs.
263 Allocation? input = Allocation.CreateFromBitmap(renderScript, originalBitmap, Allocation.MipmapControl.MipmapFull,
264 AllocationUsage.Script);
265
266 // Assign the input Allocation to the script.
267 script?.SetInput(input);
268
269 // Set the blur radius
270 script?.SetRadius(radius);
271
272 // Finally we need to create an output allocation to hold the output of the Renderscript.
273 Allocation? output = Allocation.CreateTyped(renderScript, input?.Type);
274
275 // Next, run the script. This will run the script over each Element in the Allocation, and copy it's
276 // output to the allocation we just created for this purpose.
277 script?.ForEach(output);
278
279 // Copy the output to the blurred bitmap
280 output?.CopyTo(blurredBitmap);
281
282 // Cleanup.
283 output?.Destroy();
284 input?.Destroy();
285 script?.Destroy();
286 renderScript?.Destroy();
287
288 return blurredBitmap;
289 }
290
291 // Source: http://incubator.quasimondo.com/processing/superfast_blur.php
292 public static Bitmap ToLegacyBlurred(Bitmap source, int radius)
293 {
294 Bitmap.Config? config = source.GetConfig();
295 config ??= Bitmap.Config.Argb8888; // This will support transparency
296
297 Bitmap? img = source.Copy(config!, true);
298
299 int w = img!.Width;
300 int h = img.Height;
301 int wm = w - 1;
302 int hm = h - 1;
303 int wh = w * h;
304 int div = radius + radius + 1;
305 int[] r = new int[wh];
306 int[] g = new int[wh];
307 int[] b = new int[wh];
308 int rsum, gsum, bsum, x, y, i, p, p1, p2, yp, yi, yw;
309 int[] vmin = new int[Math.Max(w, h)];
310 int[] vmax = new int[Math.Max(w, h)];
311 int[] pix = new int[w * h];
312
313 img.GetPixels(pix, 0, w, 0, 0, w, h);
314
315 int[] dv = new int[256 * div];
316 for (i = 0; i < 256 * div; i++)
317 dv[i] = (i / div);
318
319 yw = yi = 0;
320
321 for (y = 0; y < h; y++)
322 {
323 rsum = gsum = bsum = 0;
324 for (i = -radius; i <= radius; i++)
325 {
326 p = pix[yi + Math.Min(wm, Math.Max(i, 0))];
327 rsum += (p & 0xff0000) >> 16;
328 gsum += (p & 0x00ff00) >> 8;
329 bsum += p & 0x0000ff;
330 }
331 for (x = 0; x < w; x++)
332 {
333
334 r[yi] = dv[rsum];
335 g[yi] = dv[gsum];
336 b[yi] = dv[bsum];
337
338 if (y == 0)
339 {
340 vmin[x] = Math.Min(x + radius + 1, wm);
341 vmax[x] = Math.Max(x - radius, 0);
342 }
343
344 p1 = pix[yw + vmin[x]];
345 p2 = pix[yw + vmax[x]];
346
347 rsum += ((p1 & 0xff0000) - (p2 & 0xff0000)) >> 16;
348 gsum += ((p1 & 0x00ff00) - (p2 & 0x00ff00)) >> 8;
349 bsum += (p1 & 0x0000ff) - (p2 & 0x0000ff);
350 yi++;
351 }
352 yw += w;
353 }
354
355 for (x = 0; x < w; x++)
356 {
357 rsum = gsum = bsum = 0;
358 yp = -radius * w;
359 for (i = -radius; i <= radius; i++)
360 {
361 yi = Math.Max(0, yp) + x;
362 rsum += r[yi];
363 gsum += g[yi];
364 bsum += b[yi];
365 yp += w;
366 }
367 yi = x;
368 for (y = 0; y < h; y++)
369 {
370 // Preserve alpha channel: ( 0xff000000 & pix[yi] )
371 int rgb = (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum];
372 pix[yi] = ((int)(0xff000000 & pix[yi]) | rgb);
373 if (x == 0)
374 {
375 vmin[y] = Math.Min(y + radius + 1, hm) * w;
376 vmax[y] = Math.Max(y - radius, 0) * w;
377 }
378 p1 = x + vmin[y];
379 p2 = x + vmax[y];
380
381 rsum += r[p1] - r[p2];
382 gsum += g[p1] - g[p2];
383 bsum += b[p1] - b[p2];
384
385 yi += w;
386 }
387 }
388
389 img.SetPixels(pix, 0, w, 0, 0, w, h);
390 return img;
391 }
392
397 {
398 get
399 {
400 try
401 {
402 if (Build.VERSION.SdkInt < BuildVersionCodes.M)
403 return false;
404
405 Context Context = Android.App.Application.Context;
406
407 if (Context.CheckCallingOrSelfPermission(Manifest.Permission.UseBiometric) != Permission.Granted &&
408 Context.CheckCallingOrSelfPermission(Manifest.Permission.UseFingerprint) != Permission.Granted)
409 {
410 return false;
411 }
412
413 BiometricManager Manager = BiometricManager.From(Android.App.Application.Context);
414 int Level = BiometricManager.Authenticators.BiometricWeak;
415
416 return Manager.CanAuthenticate(Level) == BiometricManager.BiometricSuccess;
417
418 // TODO: AndroidX package conflicts arose between Maui & Xamarin.AndroidX.Biometrics package, that the
419 // Plugin.Fingerprint seems to have resolved. Using this library while Maui fixes the problem, even
420 // though the library is not used in code.
421
422 // TODO: Consider alternative levels:
423 //
424 // public interface Authenticators {
425 // /**
426 // * Any biometric (e.g. fingerprint, iris, or face) on the device that meets or exceeds the
427 // * requirements for <strong>Class 3</strong> (formerly <strong>Strong</strong>), as defined
428 // * by the Android CDD.
429 // */
430 // int BIOMETRIC_STRONG = 0x000F;
431 //
432 // /**
433 // * Any biometric (e.g. fingerprint, iris, or face) on the device that meets or exceeds the
434 // * requirements for <strong>Class 2</strong> (formerly <strong>Weak</strong>), as defined by
435 // * the Android CDD.
436 // *
437 // * <p>Note that this is a superset of {@link #BIOMETRIC_STRONG} and is defined such that
438 // * {@code BIOMETRIC_STRONG | BIOMETRIC_WEAK == BIOMETRIC_WEAK}.
439 // */
440 // int BIOMETRIC_WEAK = 0x00FF;
441 //
442 // /**
443 // * The non-biometric credential used to secure the device (i.e. PIN, pattern, or password).
444 // * This should typically only be used in combination with a biometric auth type, such as
445 // * {@link #BIOMETRIC_WEAK}.
446 // */
447 // int DEVICE_CREDENTIAL = 1 << 15;
448 // }
449 }
450 catch (Exception)
451 {
452 return false;
453 }
454 }
455 }
456
464 {
465 if (Build.VERSION.SdkInt < BuildVersionCodes.M)
466 return BiometricMethod.None;
467
468 Context Context = Android.App.Application.Context;
469
470 if (Context.CheckCallingOrSelfPermission(Manifest.Permission.UseBiometric) != Permission.Granted &&
471 Context.CheckCallingOrSelfPermission(Manifest.Permission.UseFingerprint) != Permission.Granted)
472 {
473 return BiometricMethod.None;
474 }
475
476 BiometricManager Manager = BiometricManager.From(Context);
477 const int Level = BiometricManager.Authenticators.BiometricWeak;
478 return Manager.CanAuthenticate(Level) == BiometricManager.BiometricSuccess ? BiometricMethod.Unknown : BiometricMethod.None;
479 }
480
490 public async Task<bool> AuthenticateUserFingerprint(string Title, string? Subtitle, string Description, string Cancel,
491 CancellationToken? CancellationToken)
492 {
494 return false;
495
496 if (string.IsNullOrWhiteSpace(Title))
497 throw new ArgumentException("Title cannot be empty.", nameof(Title));
498
499 if (Platform.CurrentActivity is not FragmentActivity Activity)
500 return false;
501
502 try
503 {
504 BiometricPrompt.PromptInfo.Builder Builder = new();
505
506 Builder.SetDeviceCredentialAllowed(false);
507 Builder.SetAllowedAuthenticators((int)Android.Hardware.Biometrics.BiometricManagerAuthenticators.BiometricWeak);
508 Builder.SetConfirmationRequired(false);
509 Builder.SetTitle(Title);
510 Builder.SetDescription(Description);
511 Builder.SetNegativeButtonText(Cancel);
512
513 if (!string.IsNullOrEmpty(Subtitle))
514 Builder.SetSubtitle(Subtitle);
515
516 BiometricPrompt.PromptInfo Prompt = Builder.Build();
517 IExecutorService? Executor = Executors.NewSingleThreadExecutor();
518 CallbackHandler Handler = new();
519 CancellationTokenRegistration? Registration = null;
520
521 BiometricPrompt Dialog = new(Activity, Executor, Handler);
522 try
523 {
524 Registration = CancellationToken?.Register(Dialog.CancelAuthentication);
525 Dialog.Authenticate(Prompt);
526
527 return await Handler.Result;
528 }
529 finally
530 {
531 if (Registration.HasValue)
532 Registration.Value.Dispose();
533
534 // Remove the lifecycle observer that is set by the BiometricPrompt.
535 // Reference: https://stackoverflow.com/a/59637670/1489968
536 // Review after referenced nugets (or Maui) has been updated.
537
538 Java.Lang.Class Class = Java.Lang.Class.FromType(Dialog.GetType());
539 Java.Lang.Reflect.Field[] Fields = Class.GetDeclaredFields();
540 Java.Lang.Reflect.Field? LifecycleObserver = Fields?.FirstOrDefault(f => f.Name == "mLifecycleObserver");
541
542 if (LifecycleObserver is not null)
543 {
544 LifecycleObserver.Accessible = true;
545 ILifecycleObserver? LastLifecycleObserver = LifecycleObserver.Get(Dialog).JavaCast<ILifecycleObserver>();
546 Lifecycle? Lifecycle = Activity.Lifecycle;
547
548 if (LastLifecycleObserver is not null && Lifecycle is not null)
549 Lifecycle.RemoveObserver(LastLifecycleObserver);
550 }
551
552 Dialog?.Dispose();
553 }
554 }
555 catch (Exception ex)
556 {
557 ServiceRef.LogService.LogException(ex);
558 return false;
559 }
560 }
561
562 private class CallbackHandler : BiometricPrompt.AuthenticationCallback, IDialogInterfaceOnClickListener
563 {
564 private readonly TaskCompletionSource<bool> result = new();
565
566 public Task<bool> Result => this.result.Task;
567
568 public override void OnAuthenticationSucceeded(BiometricPrompt.AuthenticationResult Result)
569 {
570 base.OnAuthenticationSucceeded(Result);
571 this.result.TrySetResult(true);
572 }
573
574 public override void OnAuthenticationError(int ErrorCode, Java.Lang.ICharSequence ErrorString)
575 {
576 base.OnAuthenticationError(ErrorCode, ErrorString);
577 this.result.TrySetResult(false);
578 }
579
580 public override void OnAuthenticationFailed()
581 {
582 base.OnAuthenticationFailed();
583 this.result.TrySetResult(false);
584 }
585
586 public void OnClick(IDialogInterface? Dialog, int Which)
587 {
588 this.result.TrySetResult(false);
589 }
590 }
591
596 public Task<TokenInformation> GetPushNotificationToken()
597 {
598 Java.Lang.Object? Token = string.Empty;
599
600 try
601 {
602 Token = string.Empty; // await FirebaseMessaging.Instance.GetToken().AsAsync<Java.Lang.Object>(); // TODO: Firebase
603 }
604 catch (Exception ex)
605 {
606 Log.Exception(ex);
607 }
608
610 {
611 Token = Token?.ToString(),
612 ClientType = ClientType.Android,
613 Service = PushMessagingService.Firebase
614 };
615
616 return Task.FromResult(TokenInformation);
617 }
618
619 #region Keyboard
620
622 public event EventHandler<KeyboardSizeMessage>? KeyboardShown;
624 public event EventHandler<KeyboardSizeMessage>? KeyboardHidden;
631 public event EventHandler<KeyboardSizeMessage>? KeyboardSizeChanged;
632
633 private Activity? activity;
634 private Android.Views.View? rootView;
635 private int lastKeyboardHeight = 0;
636 private Handler? initializeKeyboardHandler;
637
639 public void HideKeyboard()
640 {
641 if (this.activity is null || this.rootView is null)
642 return;
643 InputMethodManager? inputMethodManager = this.activity.GetSystemService(Context.InputMethodService) as InputMethodManager;
644 inputMethodManager?.HideSoftInputFromWindow(this.rootView.WindowToken, HideSoftInputFlags.None);
645 this.activity.Window?.DecorView.ClearFocus();
646 }
647
648 private void InitializeKeyboard()
649 {
650 this.activity = Platform.CurrentActivity;
651 try
652 {
653 this.initializeKeyboardHandler = new Handler(Looper.MainLooper!);
654 this.CheckRootView();
655 }
656 catch (Exception ex)
657 {
658 ServiceRef.LogService.LogException(ex);
659 }
660 }
661
662 private void CheckRootView()
663 {
664 this.activity = Platform.CurrentActivity;
665
666 if (this.activity?.Window?.DecorView.RootView?.ViewTreeObserver is null)
667 {
668 this.initializeKeyboardHandler?.PostDelayed(this.CheckRootView, 100);
669 return;
670 }
671 this.activity = Platform.CurrentActivity;
672 this.rootView = this.activity!.Window!.DecorView.RootView;
673 this.rootView!.ViewTreeObserver!.GlobalLayout += this.OnGlobalLayout;
674 }
675
676
677
678 private void OnGlobalLayout(object? sender, EventArgs e)
679 {
680 Rect r = new();
681 this.rootView!.GetWindowVisibleDisplayFrame(r);
682
683 int screenHeight = this.rootView.RootView!.Height;
684 int statusBarHeight = 0;
685 int actionBarHeight = 0;
686
687 // if this succeeds, we can calculate an exact keyboard height
688 Android.Views.View? contentView = this.rootView.FindViewById(Android.Views.Window.IdAndroidContent);
689 if (contentView is not null)
690 {
691 statusBarHeight = r.Top - contentView.Top;
692 actionBarHeight = r.Bottom - contentView.Bottom;
693 }
694
695 // Calculate the height of the keyboard (if the above fails, the keyboardsize will include the size of the action and status bar)
696 int availableScreenHeight = screenHeight - statusBarHeight - actionBarHeight;
697 int visibleHeight = r.Height();
698 int keypadHeight = availableScreenHeight - visibleHeight; // height of the keyboard, but is not garanteed to be the actual keyboard height it might include other things such as the action bar.
699
700 // Assume keyboard is shown if more than 15% of the available screen height is used.
701 // This is a heuristic, and may need to be adjusted.
702 // I really don't like this solution, but android doesn't provide a better way to detect the keyboard at the time of writing.
703 // Checking keyboardheight > 0 is not enough, because the keyboardheight is not garanteed to be accurate on all devices and circumstances
704
705 if (keypadHeight > availableScreenHeight * 0.15)
706 {
707 this.lastKeyboardHeight = keypadHeight;
708 this.KeyboardSizeChanged?.Invoke(this, new KeyboardSizeMessage(keypadHeight));
709 WeakReferenceMessenger.Default.Send(new KeyboardSizeMessage(keypadHeight));
710 this.KeyboardShown?.Invoke(this, new KeyboardSizeMessage(keypadHeight));
711 }
712 else
713 {
714 if (this.lastKeyboardHeight == 0)
715 return;
716
717 this.lastKeyboardHeight = 0;
718 this.KeyboardSizeChanged?.Invoke(this, new KeyboardSizeMessage(0));
719 WeakReferenceMessenger.Default.Send(new KeyboardSizeMessage(0));
720 this.KeyboardHidden?.Invoke(this, new KeyboardSizeMessage(0));
721 }
722 }
723 #endregion
724
725 }
726
727
728}
The Application class, representing an instance of the Neuro-Access app.
Definition: App.xaml.cs:69
const string Png
The PNG MIME type.
Definition: Constants.cs:242
Authentication constants
Definition: Constants.cs:33
const int MaxScreenRecordingTimeSeconds
Maximum number of seconds screen recording is allowed.
Definition: Constants.cs:72
A set of never changing property constants and helpful values.
Definition: Constants.cs:7
Android implementation of platform-specific features.
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()
Android 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. Currently on android, you cannot determine if the ...
async Task< bool > AuthenticateUserFingerprint(string Title, string? Subtitle, string Description, string Cancel, CancellationToken? CancellationToken)
Authenticates the user using the fingerprint sensor.
Task< byte[]> CaptureScreen(int blurRadius)
Make a blurred screenshot TODO: Just make a screen shot. Use the portable CV library to blur it.
virtual void Dispose(bool Disposing)
IDisposable.Dispose
EventHandler< KeyboardSizeMessage >? KeyboardSizeChanged
Fired when the keyboard size changes.
string? GetDeviceId()
Gets the ID of the device
EventHandler< KeyboardSizeMessage >? KeyboardHidden
bool ProhibitScreenCapture
If screen capture is prohibited or not.
Task CloseApplication()
Closes the application
Task< TokenInformation > GetPushNotificationToken()
Gets a Push Notification token for the device.
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