4using Android.Content.PM;
8using Android.Renderscripts;
11using Android.Views.InputMethods;
12using AndroidX.Biometric;
13using AndroidX.Fragment.App;
14using AndroidX.Lifecycle;
15using CommunityToolkit.Mvvm.Messaging;
18using Java.Util.Concurrent;
22using Debug = System.Diagnostics.Debug;
23using Rect = Android.Graphics.Rect;
32 private bool isDisposed;
39 this.InitializeKeyboard();
48 GC.SuppressFinalize(
this);
54 protected virtual void Dispose(
bool Disposing)
61 protectionTimer?.Dispose();
62 protectionTimer =
null;
65 this.isDisposed =
true;
68 private static bool screenProtected =
true;
69 private static Timer? protectionTimer =
null;
81 get => screenProtected;
86 protectionTimer?.Dispose();
87 protectionTimer =
null;
89 if (screenProtected != value)
91 this.SetScreenSecurityProtection(value);
92 screenProtected = value;
102 private void SetScreenSecurityProtection(
bool Enabled)
104 MainThread.BeginInvokeOnMainThread(() =>
108 Activity? Activity = Platform.CurrentActivity;
110 if (Activity is not
null)
112 if (Build.VERSION.SdkInt >= BuildVersionCodes.Tiramisu)
114#pragma warning disable CA1416
115 Activity.SetRecentsScreenshotEnabled(!Enabled);
116#pragma warning restore CA1416
121 Activity.Window?.SetFlags(WindowManagerFlags.Secure, WindowManagerFlags.Secure);
122 protectionTimer =
new Timer(this.ProtectionTimerElapsed,
null,
126 Activity.Window?.ClearFlags(WindowManagerFlags.Secure);
136 private void ProtectionTimerElapsed(
object? P)
146 ContentResolver? ContentResolver = Android.App.Application.Context.ContentResolver;
148 if (ContentResolver is not
null)
150 return Android.Provider.Settings.Secure.GetString(ContentResolver, Android.Provider.Settings.Secure.AndroidId);
154 App.SendAlert(
"Unable to get AndroidID, ContentResolver was null",
"text/plain").Wait();
159 System.Environment.Exit(0);
171 Activity? Activity = Platform.CurrentActivity;
172 Activity?.FinishAffinity();
174 Java.Lang.JavaSystem.Exit(0);
176 return Task.CompletedTask;
186 public void ShareImage(
byte[] PngFile,
string Message,
string Title,
string FileName)
188 Context Context = Android.App.Application.Context;
189 Java.IO.File? ExternalFilesDir = Context.GetExternalFilesDir(
"");
191 if (ExternalFilesDir is
null)
194 if (!Directory.Exists(ExternalFilesDir.Path))
195 Directory.CreateDirectory(ExternalFilesDir.Path);
197 Java.IO.File fileDir =
new(ExternalFilesDir.AbsolutePath + (Java.IO.File.Separator + FileName));
199 File.WriteAllBytes(fileDir.Path, PngFile);
201 Intent Intent =
new(Intent.ActionSend);
202 Intent.PutExtra(Intent.ExtraText, Message);
205 Intent.AddFlags(ActivityFlags.GrantReadUriPermission);
206 Intent.AddFlags(ActivityFlags.GrantWriteUriPermission);
207 Intent.PutExtra(Intent.ExtraStream, FileProvider.GetUriForFile(Context,
"com.tag.IdApp.fileprovider", fileDir));
209 Intent? MyIntent = Intent.CreateChooser(Intent, Title);
211 if (MyIntent is not
null)
213 MyIntent.AddFlags(ActivityFlags.NewTask);
214 Context.StartActivity(MyIntent);
225 blurRadius = Math.Min(25, Math.Max(blurRadius, 0));
227 Activity? activity = Platform.CurrentActivity;
228 Android.Views.View? rootView = activity?.Window?.DecorView.RootView;
230 if (rootView is
null)
231 return Task.FromResult<
byte[]>([]);
233 using Bitmap screenshot = Bitmap.CreateBitmap(rootView.Width, rootView.Height, Bitmap.Config.Argb8888!);
234 Canvas canvas =
new(screenshot);
235 rootView.Draw(canvas);
237 Bitmap? Blurred =
null;
239 if (activity !=
null && (
int)Android.OS.Build.VERSION.SdkInt >= 17)
240 Blurred = ToBlurred(screenshot, activity, blurRadius);
242 Blurred = ToLegacyBlurred(screenshot, blurRadius);
244 MemoryStream Stream =
new();
245 Blurred.Compress(Bitmap.CompressFormat.Jpeg!, 80, Stream);
246 Stream.Seek(0, SeekOrigin.Begin);
248 return Task.FromResult(Stream.ToArray());
251 private static Bitmap ToBlurred(Bitmap originalBitmap, Activity? Activity,
int radius)
254 Bitmap blurredBitmap = Bitmap.CreateBitmap(originalBitmap);
255 RenderScript? renderScript = RenderScript.Create(Activity);
260 ScriptIntrinsicBlur? script = ScriptIntrinsicBlur.Create(renderScript, Android.Renderscripts.Element.U8_4(renderScript));
263 Allocation? input = Allocation.CreateFromBitmap(renderScript, originalBitmap, Allocation.MipmapControl.MipmapFull,
264 AllocationUsage.Script);
267 script?.SetInput(input);
270 script?.SetRadius(radius);
273 Allocation? output = Allocation.CreateTyped(renderScript, input?.Type);
277 script?.ForEach(output);
280 output?.CopyTo(blurredBitmap);
286 renderScript?.Destroy();
288 return blurredBitmap;
292 public static Bitmap ToLegacyBlurred(Bitmap source,
int radius)
294 Bitmap.Config? config = source.GetConfig();
295 config ??= Bitmap.Config.Argb8888;
297 Bitmap? img = source.Copy(config!,
true);
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];
313 img.GetPixels(pix, 0, w, 0, 0, w, h);
315 int[] dv =
new int[256 * div];
316 for (i = 0; i < 256 * div; i++)
321 for (y = 0; y < h; y++)
323 rsum = gsum = bsum = 0;
324 for (i = -radius; i <= radius; i++)
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;
331 for (x = 0; x < w; x++)
340 vmin[x] = Math.Min(x + radius + 1, wm);
341 vmax[x] = Math.Max(x - radius, 0);
344 p1 = pix[yw + vmin[x]];
345 p2 = pix[yw + vmax[x]];
347 rsum += ((p1 & 0xff0000) - (p2 & 0xff0000)) >> 16;
348 gsum += ((p1 & 0x00ff00) - (p2 & 0x00ff00)) >> 8;
349 bsum += (p1 & 0x0000ff) - (p2 & 0x0000ff);
355 for (x = 0; x < w; x++)
357 rsum = gsum = bsum = 0;
359 for (i = -radius; i <= radius; i++)
361 yi = Math.Max(0, yp) + x;
368 for (y = 0; y < h; y++)
371 int rgb = (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum];
372 pix[yi] = ((int)(0xff000000 & pix[yi]) | rgb);
375 vmin[y] = Math.Min(y + radius + 1, hm) * w;
376 vmax[y] = Math.Max(y - radius, 0) * w;
381 rsum += r[p1] - r[p2];
382 gsum += g[p1] - g[p2];
383 bsum += b[p1] - b[p2];
389 img.SetPixels(pix, 0, w, 0, 0, w, h);
402 if (Build.VERSION.SdkInt < BuildVersionCodes.M)
405 Context Context = Android.App.Application.Context;
407 if (Context.CheckCallingOrSelfPermission(Manifest.Permission.UseBiometric) != Permission.Granted &&
408 Context.CheckCallingOrSelfPermission(Manifest.Permission.UseFingerprint) != Permission.Granted)
413 BiometricManager Manager = BiometricManager.From(Android.App.Application.Context);
414 int Level = BiometricManager.Authenticators.BiometricWeak;
416 return Manager.CanAuthenticate(Level) == BiometricManager.BiometricSuccess;
465 if (Build.VERSION.SdkInt < BuildVersionCodes.M)
468 Context Context = Android.App.Application.Context;
470 if (Context.CheckCallingOrSelfPermission(Manifest.Permission.UseBiometric) != Permission.Granted &&
471 Context.CheckCallingOrSelfPermission(Manifest.Permission.UseFingerprint) != Permission.Granted)
476 BiometricManager Manager = BiometricManager.From(Context);
477 const int Level = BiometricManager.Authenticators.BiometricWeak;
478 return Manager.CanAuthenticate(Level) == BiometricManager.BiometricSuccess ? BiometricMethod.Unknown :
BiometricMethod.None;
491 CancellationToken? CancellationToken)
496 if (
string.IsNullOrWhiteSpace(Title))
497 throw new ArgumentException(
"Title cannot be empty.", nameof(Title));
499 if (Platform.CurrentActivity is not FragmentActivity Activity)
504 BiometricPrompt.PromptInfo.Builder Builder =
new();
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);
513 if (!
string.IsNullOrEmpty(Subtitle))
514 Builder.SetSubtitle(Subtitle);
516 BiometricPrompt.PromptInfo Prompt = Builder.Build();
517 IExecutorService? Executor = Executors.NewSingleThreadExecutor();
518 CallbackHandler Handler =
new();
519 CancellationTokenRegistration? Registration =
null;
521 BiometricPrompt Dialog =
new(Activity, Executor, Handler);
524 Registration = CancellationToken?.Register(Dialog.CancelAuthentication);
525 Dialog.Authenticate(Prompt);
527 return await Handler.Result;
531 if (Registration.HasValue)
532 Registration.Value.Dispose();
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");
542 if (LifecycleObserver is not
null)
544 LifecycleObserver.Accessible =
true;
545 ILifecycleObserver? LastLifecycleObserver = LifecycleObserver.Get(Dialog).JavaCast<ILifecycleObserver>();
546 Lifecycle? Lifecycle = Activity.Lifecycle;
548 if (LastLifecycleObserver is not
null && Lifecycle is not
null)
549 Lifecycle.RemoveObserver(LastLifecycleObserver);
562 private class CallbackHandler : BiometricPrompt.AuthenticationCallback, IDialogInterfaceOnClickListener
564 private readonly TaskCompletionSource<bool> result =
new();
566 public Task<bool> Result => this.result.Task;
568 public override void OnAuthenticationSucceeded(BiometricPrompt.AuthenticationResult Result)
570 base.OnAuthenticationSucceeded(Result);
571 this.result.TrySetResult(
true);
574 public override void OnAuthenticationError(
int ErrorCode, Java.Lang.ICharSequence ErrorString)
576 base.OnAuthenticationError(ErrorCode, ErrorString);
577 this.result.TrySetResult(
false);
580 public override void OnAuthenticationFailed()
582 base.OnAuthenticationFailed();
583 this.result.TrySetResult(
false);
586 public void OnClick(IDialogInterface? Dialog,
int Which)
588 this.result.TrySetResult(
false);
598 Java.Lang.Object? Token =
string.Empty;
602 Token =
string.Empty;
611 Token = Token?.ToString(),
613 Service = PushMessagingService.Firebase
633 private Activity? activity;
634 private Android.Views.View? rootView;
635 private int lastKeyboardHeight = 0;
636 private Handler? initializeKeyboardHandler;
641 if (this.activity is
null || this.rootView is
null)
643 InputMethodManager? inputMethodManager = this.activity.GetSystemService(Context.InputMethodService) as InputMethodManager;
644 inputMethodManager?.HideSoftInputFromWindow(this.rootView.WindowToken, HideSoftInputFlags.None);
645 this.activity.Window?.DecorView.ClearFocus();
648 private void InitializeKeyboard()
650 this.activity = Platform.CurrentActivity;
653 this.initializeKeyboardHandler =
new Handler(Looper.MainLooper!);
654 this.CheckRootView();
662 private void CheckRootView()
664 this.activity = Platform.CurrentActivity;
666 if (this.activity?.Window?.DecorView.RootView?.ViewTreeObserver is
null)
668 this.initializeKeyboardHandler?.PostDelayed(this.CheckRootView, 100);
671 this.activity = Platform.CurrentActivity;
672 this.rootView = this.activity!.Window!.DecorView.RootView;
673 this.rootView!.ViewTreeObserver!.GlobalLayout += this.OnGlobalLayout;
678 private void OnGlobalLayout(
object? sender, EventArgs e)
681 this.rootView!.GetWindowVisibleDisplayFrame(r);
683 int screenHeight = this.rootView.RootView!.Height;
684 int statusBarHeight = 0;
685 int actionBarHeight = 0;
688 Android.Views.View? contentView = this.rootView.FindViewById(Android.Views.Window.IdAndroidContent);
689 if (contentView is not
null)
691 statusBarHeight = r.Top - contentView.Top;
692 actionBarHeight = r.Bottom - contentView.Bottom;
696 int availableScreenHeight = screenHeight - statusBarHeight - actionBarHeight;
697 int visibleHeight = r.Height();
698 int keypadHeight = availableScreenHeight - visibleHeight;
705 if (keypadHeight > availableScreenHeight * 0.15)
707 this.lastKeyboardHeight = keypadHeight;
714 if (this.lastKeyboardHeight == 0)
717 this.lastKeyboardHeight = 0;
The Application class, representing an instance of the Neuro-Access app.
const string Png
The PNG MIME type.
const int MaxScreenRecordingTimeSeconds
Maximum number of seconds screen recording is allowed.
A set of never changing property constants and helpful values.
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.