13using System.Collections.Concurrent;
14using System.Collections.ObjectModel;
15using System.Diagnostics;
16using System.Diagnostics.CodeAnalysis;
31 private readonly Dictionary<string, NavigationArgs> navigationArgsMap = [];
32 private readonly ConcurrentQueue<UiTask> taskQueue =
new();
33 private readonly Stack<PopupPage?> popupStack =
new();
35 private bool isExecutingUiTasks =
false;
36 private bool isNavigating =
false;
47 private void AddTask(
UiTask Task)
49 this.taskQueue.Enqueue(Task);
51 if (!this.isExecutingUiTasks)
53 this.isExecutingUiTasks =
true;
55 MainThread.BeginInvokeOnMainThread(async () =>
57 await this.ProcessAllTasks();
62 private async Task ProcessAllTasks()
68 if (this.taskQueue.TryDequeue(out
UiTask? Task))
71 while (!this.taskQueue.IsEmpty);
75 this.isExecutingUiTasks =
false;
84 public Task<bool>
DisplayAlert(
string Title,
string Message,
string? Accept =
null,
string? Cancel =
null)
88 return Task.CompletionSource.Task;
96 StringBuilder sb =
new();
98 if (Exception is not
null)
100 sb.AppendLine(Exception.Message);
102 while (Exception.InnerException is not
null)
104 Exception = Exception.InnerException;
105 sb.AppendLine(Exception.Message);
118 #region DisplayPrompt
121 public Task<string?>
DisplayPrompt(
string Title,
string Message,
string? Accept =
null,
string? Cancel =
null)
125 return Task.CompletionSource.Task;
146 IScreenshotResult? Screen = await Screenshot.CaptureAsync();
154 using Stream PngStream = await Screen.OpenReadAsync(ScreenshotFormat.Png, 20);
160 SKBitmap OriginalBitmap = SKBitmap.FromImage(SKImage.FromEncodedData(PngStream));
166 int DesiredWidth = OriginalBitmap.Width / 4;
167 int DesiredHeight = OriginalBitmap.Height / 4;
170 SKImageInfo resizedInfo =
new(DesiredWidth, DesiredHeight, SKColorType.Gray8);
173 SKBitmap resizedBitmap = OriginalBitmap.Resize(resizedInfo, SKFilterQuality.Medium);
180 IMatrix GreyChannelMatrix = RezisedMatrix.GrayScale();
197 ImageSource BlurredScreen = ImageSource.FromStream(() =>
new MemoryStream(Blurred));
204 await
App.SendAlert(
"```uml\r\n" + TimingUml +
"\r\n```",
"text/markdown");
206 return BlurredScreen;
223 IScreenshotResult? Result = await Screenshot.CaptureAsync();
228 using Stream Stream = await Result.OpenReadAsync();
229 MemoryStream MemoryStream =
new();
230 await Stream.CopyToAsync(MemoryStream);
231 byte[] Bytes = MemoryStream.ToArray();
234 return ImageSource.FromStream(() =>
new MemoryStream(Bytes));
255 if (this.
BeginLoad(IsResuming, CancellationToken))
259 Application? Application = Application.Current;
261 if (Application is not
null)
263 Application.PropertyChanging += this.OnApplicationPropertyChanging;
264 Application.PropertyChanged += this.OnApplicationPropertyChanged;
267 this.SubscribeToShellNavigatingIfNecessary(Application);
279 return Task.CompletedTask;
289 Application? Application = Application.Current;
290 if (Application is not
null)
292 this.UnsubscribeFromShellNavigatingIfNecessary(Application);
293 Application.PropertyChanged -= this.OnApplicationPropertyChanged;
294 Application.PropertyChanging -= this.OnApplicationPropertyChanging;
305 return Task.CompletedTask;
313 return this.GoToAsync<NavigationArgs>(Route,
null,
BackMethod, UniqueId);
319 await MainThread.InvokeOnMainThreadAsync(async () =>
332 if (!
string.IsNullOrEmpty(UniqueId))
333 Route +=
"?UniqueId=" + UniqueId;
337 this.isNavigating =
true;
344 string ExtraInfo = Environment.NewLine + ex.Message;
352 this.isNavigating =
false;
369 this.isNavigating =
true;
370 await Shell.Current.GoToAsync(BackRoute, Animate);
374 ShellNavigationState State = Shell.Current.CurrentState;
375 if (Uri.TryCreate(State.Location,
"..", out Uri? BackLocation))
376 await Shell.Current.GoToAsync(BackLocation);
391 this.isNavigating =
false;
403 if (this.latestArguments is TArgs Result)
405 this.latestArguments =
null;
420 if (this.TryGetArgs(out TArgs? Result, UniqueId))
436 if (this.CurrentPage is Page Page)
439 string Route = Routing.GetRoute(Page);
445 return this.TryGetArgs(out Args, BasePage.UniqueId);
454 return (Args is not
null);
463 private void OnApplicationPropertyChanged(
object? Sender, System.ComponentModel.PropertyChangedEventArgs Args)
465 if (Args.PropertyName == nameof(Application.MainPage))
466 this.SubscribeToShellNavigatingIfNecessary((Application?)Sender);
469 private void OnApplicationPropertyChanging(
object? Sender, PropertyChangingEventArgs Args)
471 if (Args.PropertyName == nameof(Application.MainPage))
472 this.UnsubscribeFromShellNavigatingIfNecessary((Application?)Sender);
475 private void SubscribeToShellNavigatingIfNecessary(Application? Application)
477 if (Application?.MainPage is Shell Shell)
478 Shell.Navigating += this.Shell_Navigating;
481 private void UnsubscribeFromShellNavigatingIfNecessary(Application? Application)
483 if (Application?.MainPage is Shell Shell)
484 Shell.Navigating -= this.Shell_Navigating;
487 private void Shell_Navigating(
object? Sender, ShellNavigatingEventArgs e)
491 if ((e.Source == ShellNavigationSource.Pop) && e.CanCancel && !
this.isNavigating)
495 MainThread.BeginInvokeOnMainThread(async () =>
507 private static bool TryGetPageName(
string Route, [NotNullWhen(
true)] out
string? PageName)
511 if (!
string.IsNullOrWhiteSpace(Route))
513 PageName = Route.TrimStart(
'.',
'/');
514 return !
string.IsNullOrWhiteSpace(PageName);
522 this.latestArguments = Args;
524 if (TryGetPageName(Route, out
string? PageName))
526 if (Args is not
null)
530 if (!
string.IsNullOrEmpty(UniqueId))
531 PageName +=
"?UniqueId=" + UniqueId;
533 this.navigationArgsMap[PageName] = Args;
536 this.navigationArgsMap.Remove(PageName);
540 private NavigationArgs? TryGetArgs(
string Route,
string? UniqueId)
542 if (!
string.IsNullOrEmpty(UniqueId))
543 Route +=
"?UniqueId=" + UniqueId;
545 if (TryGetPageName(Route, out
string? PageName) &&
546 this.navigationArgsMap.TryGetValue(PageName, out
NavigationArgs? Args))
561 public ReadOnlyCollection<PopupPage?>
PopupStack =>
new(this.popupStack.ToList());
567 TViewModel viewModel =
new();
568 page.ViewModel = viewModel;
570 this.popupStack.Push(page);
571 return MopupService.Instance.PushAsync(page);
577 page.ViewModel = viewModel;
579 this.popupStack.Push(page);
580 return MopupService.Instance.PushAsync(page);
586 TViewModel viewModel =
new();
587 page.ViewModel = viewModel;
589 this.popupStack.Push(page);
590 return MopupService.Instance.PushAsync(page);
598 ViewModel = viewModel
601 this.popupStack.Push(page);
602 return MopupService.Instance.PushAsync(page);
609 this.popupStack.Push(page);
610 return MopupService.Instance.PushAsync(page);
617 this.popupStack.Push(page);
618 return MopupService.Instance.PushAsync(page);
625 TViewModel viewModel =
new();
626 page.ViewModel = viewModel;
628 this.popupStack.Push(page);
629 await MopupService.Instance.PushAsync(page);
630 return await viewModel.Result;
636 page.ViewModel = viewModel;
638 this.popupStack.Push(page);
639 await MopupService.Instance.PushAsync(page);
640 return await viewModel.Result;
647 TViewModel viewModel =
new();
648 page.ViewModel = viewModel;
650 this.popupStack.Push(page);
651 await MopupService.Instance.PushAsync(page);
652 return await viewModel.Result;
660 ViewModel = viewModel
663 this.popupStack.Push(page);
664 await MopupService.Instance.PushAsync(page);
666 return await viewModel.Result;
672 if (this.popupStack.Count == 0)
676 object? vm = this.popupStack.Pop()?.BindingContext;
680 await MopupService.Instance.PopAsync();
697 using HttpClient httpClient =
new();
698 using HttpResponseMessage response = await httpClient.GetAsync(svgUri);
699 if (!response.IsSuccessStatusCode)
703 byte[] contentBytes = await response.Content.ReadAsByteArrayAsync();
705 using (MemoryStream stream =
new(contentBytes))
711 if (svg.Picture is
null)
714 using (MemoryStream stream =
new())
716 if (svg.Picture.ToImage(stream, SKColor.Parse(
"#00FFFFFF"), SKEncodedImageFormat.Png, 100, 1, 1, SKColorType.Rgba8888, SKAlphaType.Premul, SKColorSpace.CreateSrgb()))
717 return ImageSource.FromStream(() =>
new MemoryStream(stream.ToArray()));
Static methods managing conversion to and from bitmap representations.
static byte[] EncodeAsPng(IMatrix M)
Encodes an image in a matrix using PNG.
static IMatrix FromBitmap(SKBitmap Bmp)
Craetes a matrix from a bitmap.
The Application class, representing an instance of the Neuro-Access app.
Absolute paths to important pages.
const string MainPage
Path to main page.
A set of never changing property constants and helpful values.
bool BeginLoad(bool IsResuming, CancellationToken CancellationToken)
Sets the IsLoading flag if the service isn't already loading.
void EndLoad(bool isLoaded)
Sets the IsLoading and IsLoaded flags and fires an event representing the current load state of the s...
bool IsResuming
If App is resuming service.
bool BeginUnload()
Sets the IsLoading flag if the service isn't already unloading.
void EndUnload()
Sets the IsLoading and IsLoaded flags and fires an event representing the current load state of the s...
Base class that references services in the app.
static ILogService LogService
Log service.
static IUiService UiService
Service serializing and managing UI-related tasks.
static IStringLocalizer Localizer
Localization service
An base class holding page specific navigation parameters.
string? UniqueId
An unique view identifier used to search the args of similar view types.
readonly TaskCompletionSource< bool > NavigationCompletionSource
The completion source for the navigation task. Will return true when the navigation and transitions a...
void SetBackArguments(NavigationArgs? ParentArgs, BackMethod BackMethod=BackMethod.Inherited, string? UniqueId=null)
Sets the reference to the main parent's NavigationArgs.
string GetBackRoute()
Get the route used for the IUiService.GoBackAsync method.
bool Animated
Is the navigation animated
Abstract base class for UI tasks.
abstract Task Execute()
Executes the task.
UiService()
Creates a new instance of the UiService class.
Task PushAsync< TPage >()
Pushes a popup, without any viewmodel binding, onto the current view
override Task Unload()
Unloads the specified service.
async Task GoToAsync< TArgs >(string Route, TArgs? Args, BackMethod BackMethod=BackMethod.Inherited, string? UniqueId=null)
Navigates the AppShell to the specified route, with page arguments to match.
TArgs? TryGetArgs< TArgs >(string? UniqueId=null)
Returns the page's arguments from the (one-level) deep navigation stack.
Task< bool > DisplayAlert(string Title, string Message, string? Accept=null, string? Cancel=null)
Displays an alert/message box to the user. If Accept or Cancel was pressed
TArgs? PopLatestArgs< TArgs >()
Pops the latests navigation arguments. Can only be used once to get the navigation arguments....
Task DisplayException(Exception Exception, string? Title=null)
Displays an alert/message box to the user.
override Task Load(bool IsResuming, CancellationToken CancellationToken)
Loads the specified service.
Task GoToAsync(string Route, BackMethod BackMethod=BackMethod.Inherited, string? UniqueId=null)
Navigates the AppShell to the specified route, with page arguments to match.
Page CurrentPage
Current page
async Task< ImageSource?> ConvertSvgUriToImageSource(string svgUri)
Fetches a SVG and converts it to a PNG image source. An image Source representing the SVG file or nul...
async Task GoBackAsync(bool Animate=true)
Returns to the previous page/route.
ReadOnlyCollection< PopupPage?> PopupStack
The current stack of popup pages.
async Task< ImageSource?> TakeScreenshotAsync()
Takes a screen-shot.
async Task< ImageSource?> TakeBlurredScreenshotAsync()
Takes a blurred screen shot
Task PushAsync< TPage, TViewModel >()
Pushes a popup onto the current view
Task< string?> DisplayPrompt(string Title, string Message, string? Accept=null, string? Cancel=null)
Prompts the user for some input. User input
async Task< TReturn?> PushAsync< TPage, TViewModel, TReturn >()
Pushes a popup onto the current view Return value, or null if cancelled.
async Task PopAsync()
Closes the current popup.
Static class managing the application event log. Applications and services log events on this static ...
static Exception UnnestException(Exception Exception)
Unnests an exception, to extract the relevant inner exception.
Class that keeps track of events and timing.
void Stop()
Stops measuring time.
void NewState(string State)
Main Thread changes state.
string ExportPlantUml(TimeUnit TimeUnit)
Exports events to PlantUML.
void Start()
Starts measuring time.
Service serializing and managing UI-related tasks.
BackMethod
Navigation Back Method
TimeUnit
Options for presenting time in reports.
ProfilerThreadType
Type of profiler thread.