Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
ViewThingViewModel.cs
1using CommunityToolkit.Mvvm.ComponentModel;
2using CommunityToolkit.Mvvm.Input;
14using System.Collections.ObjectModel;
15using System.ComponentModel;
16using System.Globalization;
17using System.Text;
26using Waher.Things;
27
29{
33 public partial class ViewThingViewModel : QrXmppViewModel
34 {
35 private readonly Dictionary<string, PresenceEventArgs> presences = new(StringComparer.InvariantCultureIgnoreCase);
36 private readonly ViewThingNavigationArgs? navigationArguments;
37 private readonly ContactInfo? thing;
38
44 : base()
45 {
46 this.navigationArguments = Args;
47 this.thing = Args?.Thing;
48
49 this.Tags = [];
50 this.Notifications = [];
51
52 if (this.thing?.MetaData is not null)
53 {
54 foreach (Property Tag in this.thing.MetaData)
55 this.Tags.Add(new HumanReadableTag(Tag));
56 }
57
58 this.InContacts = !string.IsNullOrEmpty(this.thing?.ObjectId);
59 this.IsOwner = this.thing?.Owner ?? false;
60 this.IsSensor = this.thing?.IsSensor ?? false;
61 this.IsActuator = this.thing?.IsActuator ?? false;
62 this.IsConcentrator = this.thing?.IsConcentrator ?? false;
63 this.IsNodeInConcentrator = !string.IsNullOrEmpty(this.thing?.NodeId) || !string.IsNullOrEmpty(this.thing?.SourceId) || !string.IsNullOrEmpty(this.thing?.Partition);
64 this.SupportsSensorEvents = this.thing?.SupportsSensorEvents ?? false;
65
66 this.InContactsAndNotOwner = this.InContacts && !this.IsOwner;
67 this.NotInContacts = !this.InContacts;
68 this.IsConnectedAndOwner = this.IsConnected && this.IsOwner;
69 this.IsConnectedAndSensor = this.IsConnected && this.IsSensor;
70 this.IsConnectedAndActuator = this.IsConnected && this.IsActuator;
71 this.IsConnectedAndNotConcentrator = this.IsConnected && !this.IsConcentrator;
72 }
73
75 protected override async Task OnInitialize()
76 {
77 await base.OnInitialize();
78
79 if (this.navigationArguments?.Events is not null)
80 {
81 this.Notifications.Clear();
82
83 int c = 0;
84
85 foreach (NotificationEvent Event in this.navigationArguments.Events)
86 {
87 this.Notifications.Add(new EventModel(Event.Received,
88 await Event.GetCategoryIcon(),
89 await Event.GetDescription(),
90 Event));
91
92 if (Event.Type == NotificationEventType.Contacts)
93 c++;
94 }
95
96 this.NrPendingChatMessages = c;
97 this.HasPendingChatMessages = c > 0;
98 }
99
100 this.HasNotifications = this.Notifications.Count > 0;
101
102 await this.CalcThingIsOnline();
103
104 ServiceRef.XmppService.OnPresence += this.Xmpp_OnPresence;
105 ServiceRef.XmppService.OnRosterItemAdded += this.Xmpp_OnRosterItemAdded;
106 ServiceRef.XmppService.OnRosterItemUpdated += this.Xmpp_OnRosterItemUpdated;
107 ServiceRef.XmppService.OnRosterItemRemoved += this.Xmpp_OnRosterItemRemoved;
108 ServiceRef.TagProfile.Changed += this.TagProfile_Changed;
109 ServiceRef.NotificationService.OnNewNotification += this.NotificationService_OnNewNotification;
110 ServiceRef.NotificationService.OnNotificationsDeleted += this.NotificationService_OnNotificationsDeleted;
111
112 if (this.IsConnected && this.IsThingOnline)
113 await this.CheckCapabilities();
114
115 this.GenerateQrCode(this.Link);
116 }
117
118
119 protected override Task XmppService_ConnectionStateChanged(object? _, XmppState NewState)
120 {
121 base.XmppService_ConnectionStateChanged(_, NewState);
122
123 MainThread.BeginInvokeOnMainThread(async () => await this.CalcThingIsOnline());
124
125 return Task.CompletedTask;
126 }
127
128 private async Task CheckCapabilities()
129 {
130 if (this.InContacts &&
131 this.thing is not null &&
132 (!this.thing.IsSensor.HasValue ||
133 !this.thing.IsActuator.HasValue ||
134 !this.thing.IsConcentrator.HasValue ||
135 !this.thing.SupportsSensorEvents.HasValue))
136 {
137 string? FullJid = this.GetFullJid();
138
139 if (!string.IsNullOrEmpty(FullJid))
140 {
141 try
142 {
143 ServiceDiscoveryEventArgs e = await ServiceRef.XmppService.SendServiceDiscoveryRequest(FullJid);
144 if (!this.InContacts)
145 return;
146
147 this.thing.IsSensor = e.HasAnyFeature(SensorClient.NamespacesSensorData);
148 this.thing.SupportsSensorEvents = e.HasAnyFeature(SensorClient.NamespacesSensorEvents);
149 this.thing.IsActuator = e.HasAnyFeature(ControlClient.NamespacesControl);
150 this.thing.IsConcentrator = e.HasAnyFeature(ConcentratorServer.NamespacesConcentrator);
151
152 if (this.InContacts && !string.IsNullOrEmpty(this.thing.ObjectId))
153 await Database.Update(this.thing);
154
155 MainThread.BeginInvokeOnMainThread(() =>
156 {
157 this.IsSensor = this.thing.IsSensor ?? false;
158 this.IsActuator = this.thing.IsActuator ?? false;
159 this.IsConcentrator = this.thing.IsConcentrator ?? false;
160 this.SupportsSensorEvents = this.thing.SupportsSensorEvents ?? false;
161 });
162 }
163 catch (Exception ex)
164 {
165 ServiceRef.LogService.LogException(ex);
166 }
167 }
168 }
169 }
170
172 protected override async Task OnDispose()
173 {
174 ServiceRef.XmppService.OnPresence -= this.Xmpp_OnPresence;
175 ServiceRef.XmppService.OnRosterItemAdded -= this.Xmpp_OnRosterItemAdded;
176 ServiceRef.XmppService.OnRosterItemUpdated -= this.Xmpp_OnRosterItemUpdated;
177 ServiceRef.XmppService.OnRosterItemRemoved -= this.Xmpp_OnRosterItemRemoved;
178 ServiceRef.TagProfile.Changed -= this.TagProfile_Changed;
179 ServiceRef.NotificationService.OnNewNotification -= this.NotificationService_OnNewNotification;
180 ServiceRef.NotificationService.OnNotificationsDeleted -= this.NotificationService_OnNotificationsDeleted;
181
182 await base.OnDispose();
183 }
184
185 private async Task Xmpp_OnPresence(object? Sender, PresenceEventArgs e)
186 {
187 switch (e.Type)
188 {
189 case PresenceType.Available:
190 this.presences[e.FromBareJID] = e;
191
192 if (!this.InContacts && string.Equals(e.FromBareJID, this.thing?.BareJid, StringComparison.OrdinalIgnoreCase))
193 {
194 if (string.IsNullOrEmpty(this.thing?.ObjectId))
195 await Database.Insert(this.thing);
196
197 MainThread.BeginInvokeOnMainThread(async () =>
198 {
199 this.InContacts = true;
200 });
201 }
202
203 await this.CalcThingIsOnline();
204
205 break;
206
207 case PresenceType.Unavailable:
208 this.presences.Remove(e.FromBareJID);
209 break;
210 }
211 }
212
213 private async Task CalcThingIsOnline()
214 {
215
216 if (this.thing is null)
217 MainThread.BeginInvokeOnMainThread(() => this.IsThingOnline = false);
218 else
219 {
220 try
221 {
222 await MainThread.InvokeOnMainThreadAsync(() => this.IsThingOnline = this.IsOnline(this.thing.BareJid));
223 if (this.IsThingOnline)
224 await this.CheckCapabilities();
225 }
226 catch (Exception ex)
227 {
228 ServiceRef.LogService.LogException(ex);
230 }
231
232 }
233 }
234
235 async partial void OnInContactsChanged(bool value)
236 {
237 await this.CalcThingIsOnline();
238 }
239 private bool IsOnline(string BareJid)
240 {
241 if (this.presences.TryGetValue(BareJid, out PresenceEventArgs? e))
242 return e.IsOnline;
243 try
244 {
245 RosterItem? Item = ServiceRef.XmppService?.GetRosterItem(BareJid);
246 if (Item is not null && Item.HasLastPresence)
247 return Item.LastPresence.IsOnline;
248 }
249 catch (Exception)
250 {
251 //ignore
252 }
253
254 return false;
255 }
256
257 private string? GetFullJid()
258 {
259 if (this.thing is null)
260 return null;
261 else
262 {
263 if (this.presences.TryGetValue(this.thing.BareJid, out PresenceEventArgs? e))
264 return (e?.IsOnline == true) ? e.From : null;
265
266 try
267 {
268 RosterItem? Item = ServiceRef.XmppService.GetRosterItem(this.thing.BareJid);
269
270 if (Item is null || !Item.HasLastPresence || !Item.LastPresence.IsOnline)
271 return null;
272 else
273 return Item.LastPresenceFullJid;
274 }
275 catch (Exception)
276 {
277 return null;
278 }
279
280 }
281 }
282
283 private void TagProfile_Changed(object? Sender, PropertyChangedEventArgs e)
284 {
285 MainThread.BeginInvokeOnMainThread(async () => await this.CalcThingIsOnline());
286 }
287
288 #region Properties
289
293 public ObservableCollection<HumanReadableTag> Tags { get; }
294
298 public ObservableCollection<EventModel> Notifications { get; }
299
303 [ObservableProperty]
304 [NotifyCanExecuteChangedFor(nameof(RemoveFromListCommand))]
305 [NotifyCanExecuteChangedFor(nameof(AddToListCommand))]
306 private bool inContacts;
307
311 [ObservableProperty]
312 [NotifyCanExecuteChangedFor(nameof(RemoveFromListCommand))]
313 [NotifyCanExecuteChangedFor(nameof(AddToListCommand))]
314 private bool notInContacts;
315
316
317 protected override void OnPropertyChanged(PropertyChangedEventArgs e)
318 {
319 base.OnPropertyChanged(e);
320 MainThread.BeginInvokeOnMainThread(() =>
321 {
322
323 switch (e.PropertyName)
324 {
325 case nameof(this.IsBusy):
326 this.ReadSensorCommand.NotifyCanExecuteChanged();
327 this.ControlActuatorCommand.NotifyCanExecuteChanged();
328 this.ChatCommand.NotifyCanExecuteChanged();
329 break;
330 }
331
332 //This looks a bit cursed
333 switch (e.PropertyName)
334 {
335 case nameof(this.InContacts):
336 case nameof(this.IsOwner):
337 this.InContactsAndNotOwner = this.InContacts && !this.IsOwner;
338 this.NotInContacts = !this.InContacts;
339 break;
340 }
341
342 switch (e.PropertyName)
343 {
344 case nameof(this.IsConnected):
345 case nameof(this.IsOwner):
346 this.IsConnectedAndOwner = this.IsConnected && this.IsOwner;
347 break;
348 }
349
350 switch (e.PropertyName)
351 {
352 case nameof(this.IsConnected):
353 case nameof(this.IsSensor):
354 this.IsConnectedAndSensor = this.IsConnected && this.IsSensor;
355 break;
356 }
357
358 switch (e.PropertyName)
359 {
360 case nameof(this.IsConnected):
361 case nameof(this.IsActuator):
362 this.IsConnectedAndActuator = this.IsConnected && this.IsActuator;
363 break;
364 }
365
366 switch (e.PropertyName)
367 {
368 case nameof(this.IsConnected):
369 case nameof(this.IsConcentrator):
370 this.IsConnectedAndNotConcentrator = this.IsConnected && !this.IsConcentrator;
371 break;
372 }
373 });
374 }
375
376 public string? FriendlyName => this.thing?.FriendlyName ?? this.thing?.BareJid ?? string.Empty;
377
381 [ObservableProperty]
382 private bool isOwner;
383
387 [ObservableProperty]
388 [NotifyCanExecuteChangedFor(nameof(RemoveFromListCommand))]
389 [NotifyCanExecuteChangedFor(nameof(AddToListCommand))]
390 private bool inContactsAndNotOwner;
391
395 [ObservableProperty]
396 [NotifyCanExecuteChangedFor(nameof(DeleteRulesCommand))]
397 [NotifyCanExecuteChangedFor(nameof(DisownThingCommand))]
398 private bool isConnectedAndOwner;
399
403 [ObservableProperty]
404 [NotifyCanExecuteChangedFor(nameof(ReadSensorCommand))]
405 private bool isConnectedAndSensor;
406
410 [ObservableProperty]
411 [NotifyCanExecuteChangedFor(nameof(ControlActuatorCommand))]
412 private bool isConnectedAndActuator;
413
417 [ObservableProperty]
418 private bool isConnectedAndNotConcentrator;
419
423 [ObservableProperty]
424 private bool isThingOnline;
425
429 [ObservableProperty]
430 [NotifyCanExecuteChangedFor(nameof(ReadSensorCommand))]
431 private bool isSensor;
432
436 [ObservableProperty]
437 [NotifyCanExecuteChangedFor(nameof(ControlActuatorCommand))]
438 private bool isActuator;
439
443 [ObservableProperty]
444 private bool isConcentrator;
445
449 [ObservableProperty]
450 private bool isNodeInConcentrator;
451
455 [ObservableProperty]
456 private bool supportsSensorEvents;
460 [ObservableProperty]
461 private bool hasNotifications;
462
466 [ObservableProperty]
467 private bool hasPendingChatMessages;
468
472 [ObservableProperty]
473 private int nrPendingChatMessages;
474
475 #endregion
476
480 [RelayCommand]
481 private static Task Click(object obj)
482 {
483 if (obj is HumanReadableTag Tag)
484 return ViewClaimThing.ViewClaimThingViewModel.LabelClicked(Tag.Name, Tag.Value, Tag.LocalizedValue);
485 else if (obj is string s)
486 return ViewClaimThing.ViewClaimThingViewModel.LabelClicked(string.Empty, s, s);
487 else
488 return Task.CompletedTask;
489 }
490
494 [RelayCommand]
495 private async Task CopyQr(object Item)
496 {
497 try
498 {
499 this.SetIsBusy(true);
500
501 await Clipboard.SetTextAsync(this.Link);
503 ServiceRef.Localizer[nameof(AppResources.SuccessTitle)],
504 ServiceRef.Localizer[nameof(AppResources.SuccessTitle)]);
505 }
506 catch (Exception ex)
507 {
508 ServiceRef.LogService.LogException(ex);
510 }
511 finally
512 {
513 this.SetIsBusy(false);
514 }
515 }
516
520 [RelayCommand(CanExecute = nameof(IsConnectedAndOwner))]
521 private async Task DeleteRules()
522 {
523 if (this.thing is null)
524 return;
525
526 try
527 {
529 ServiceRef.Localizer[nameof(AppResources.Question)], ServiceRef.Localizer[nameof(AppResources.DeleteRulesQuestion)],
530 ServiceRef.Localizer[nameof(AppResources.Yes)], ServiceRef.Localizer[nameof(AppResources.Cancel)]))
531 {
532 return;
533 }
534
535 if (!await App.AuthenticateUser(AuthenticationPurpose.DeleteRules, true))
536 return;
537
538 TaskCompletionSource<bool> Result = new();
539
540 ServiceRef.XmppService.DeleteDeviceRules(this.thing.RegistryJid, this.thing.BareJid, this.thing.NodeId,
541 this.thing.SourceId, this.thing.Partition, (sender, e) =>
542 {
543 if (e.Ok)
544 Result.TrySetResult(true);
545 else if (e.StanzaError is not null)
546 Result.TrySetException(e.StanzaError);
547 else
548 Result.TrySetResult(false);
549
550 return Task.CompletedTask;
551 }, null);
552
553
554 if (!await Result.Task)
555 return;
556
557 await ServiceRef.UiService.DisplayAlert(ServiceRef.Localizer[nameof(AppResources.SuccessTitle)],
558 ServiceRef.Localizer[nameof(AppResources.RulesDeleted)]);
559 }
560 catch (Exception ex)
561 {
562 ServiceRef.LogService.LogException(ex);
564 }
565 }
566
570 [RelayCommand(CanExecute = nameof(IsConnectedAndOwner))]
571 private async Task DisownThing()
572 {
573 if (this.thing is null)
574 return;
575
576 try
577 {
579 ServiceRef.Localizer[nameof(AppResources.Question)], ServiceRef.Localizer[nameof(AppResources.DisownThingQuestion)],
580 ServiceRef.Localizer[nameof(AppResources.Yes)], ServiceRef.Localizer[nameof(AppResources.Cancel)]))
581 {
582 return;
583 }
584
585 if (!await App.AuthenticateUser(AuthenticationPurpose.DisownThing, true))
586 return;
587
588 (bool Succeeded, bool Done) = await ServiceRef.NetworkService.TryRequest(() =>
589 ServiceRef.XmppService.Disown(this.thing.RegistryJid, this.thing.BareJid, this.thing.SourceId, this.thing.Partition, this.thing.NodeId));
590
591 if (!Succeeded)
592 return;
593
594 if (this.InContacts)
595 {
596 if (!string.IsNullOrEmpty(this.thing?.ObjectId))
597 {
598 await Database.Delete(this.thing);
599 await Database.Provider.Flush();
600
601 this.thing.ObjectId = null;
602 }
603
604 if (this.thing is not null)
605 this.thing.ObjectId = null;
606
607 this.InContacts = false;
608 }
609
610 await ServiceRef.UiService.DisplayAlert(ServiceRef.Localizer[nameof(AppResources.SuccessTitle)],
611 ServiceRef.Localizer[nameof(AppResources.ThingDisowned)]);
612 await this.GoBack();
613 }
614 catch (Exception ex)
615 {
616 ServiceRef.LogService.LogException(ex);
618 }
619 }
620
624 [RelayCommand(CanExecute = nameof(NotInContacts))]
625 private async Task AddToList()
626 {
627 if (this.thing is null)
628 return;
629 try
630 {
631 if (!await App.AuthenticateUser(AuthenticationPurpose.AddToListOfThings))
632 return;
633
634 RosterItem? Item = ServiceRef.XmppService.GetRosterItem(this.thing.BareJid);
635 if (Item is null || Item.State == SubscriptionState.None || Item.State == SubscriptionState.From)
636 {
637 string IdXml;
638
639 if (ServiceRef.TagProfile.LegalIdentity is null)
640 IdXml = string.Empty;
641 else
642 {
643 StringBuilder Xml = new();
644 ServiceRef.TagProfile.LegalIdentity.Serialize(Xml, true, true, true, true, true, true, true);
645 IdXml = Xml.ToString();
646 }
647 ServiceRef.XmppService.RequestPresenceSubscription(this.thing.BareJid);
648 await ServiceRef.UiService.DisplayAlert(ServiceRef.Localizer[nameof(AppResources.SuccessTitle)], ServiceRef.Localizer[nameof(AppResources.ARequestHasBeenSentToTheOwner)]);
649 MainThread.BeginInvokeOnMainThread(() => this.NotInContacts = false);
650 }
651 await MainThread.InvokeOnMainThreadAsync(async () =>
652 {
653 if (!this.InContacts)
654 {
655 if (string.IsNullOrEmpty(this.thing.ObjectId))
656 await Database.Insert(this.thing);
657
658 this.InContacts = true;
659 }
660
661 await this.CalcThingIsOnline();
662 });
663
664 }
665 catch (Exception ex)
666 {
667 ServiceRef.LogService.LogException(ex);
669 }
670 }
671
675 [RelayCommand(CanExecute = nameof(InContactsAndNotOwner))]
676 private async Task RemoveFromList()
677 {
678 if (this.thing is null)
679 return;
680
681 try
682 {
683 if (!await App.AuthenticateUser(AuthenticationPurpose.RemoveFromListOfThings))
684 return;
685
686 if (this.InContacts)
687 {
688 if (!string.IsNullOrEmpty(this.thing.ObjectId))
689 {
690 await Database.Delete(this.thing);
691 this.thing.ObjectId = null;
692 }
693
694 ServiceRef.XmppService.RequestPresenceUnsubscription(this.thing.BareJid);
695
696 if (ServiceRef.XmppService.GetRosterItem(this.thing.BareJid) is not null)
697 ServiceRef.XmppService.RemoveRosterItem(this.thing.BareJid);
698
699 await this.GoBack();
700 }
701 }
702 catch (Exception ex)
703 {
704 ServiceRef.LogService.LogException(ex);
706 }
707 }
708 private bool CanReadSensor => !this.IsBusy && this.IsConnectedAndSensor;
709
713 [RelayCommand(CanExecute = nameof(CanReadSensor))]
714 private async Task ReadSensor()
715 {
716 await MainThread.InvokeOnMainThreadAsync(() => this.SetIsBusy(true));
717
718 if (this.thing is null)
719 return;
720
721 ViewThingNavigationArgs Args = new(this.thing, MyThingsViewModel.GetNotificationEvents(this.thing) ?? []);
722
723 await ServiceRef.UiService.GoToAsync(nameof(ReadSensorPage), Args, BackMethod.Pop);
724
725 await MainThread.InvokeOnMainThreadAsync(() => this.SetIsBusy(false));
726
727 }
728
729 private bool CanControlActuator => !this.IsBusy && this.IsConnectedAndActuator;
730
734 [RelayCommand(CanExecute = nameof(CanControlActuator))]
735 private async Task ControlActuator()
736 {
737 await MainThread.InvokeOnMainThreadAsync(() => this.SetIsBusy(true));
738
739 if (this.thing is null)
740 return;
741
742 try
743 {
744 string? FullJid = this.GetFullJid();
745 if (string.IsNullOrEmpty(FullJid))
746 return;
747
748 LanguageInfo SelectedLanguage = App.SelectedLanguage;
749
750 if (string.IsNullOrEmpty(this.thing.NodeId) && string.IsNullOrEmpty(this.thing.SourceId) && string.IsNullOrEmpty(this.thing.Partition))
751 ServiceRef.XmppService.GetControlForm(FullJid, SelectedLanguage.Name, this.ControlFormCallback, null);
752 else
753 {
754 ThingReference ThingRef = new(this.thing.NodeId, this.thing.SourceId, this.thing.Partition);
755 ServiceRef.XmppService.GetControlForm(FullJid, SelectedLanguage.Name, this.ControlFormCallback, null, ThingRef);
756 }
757 }
758 catch (Exception ex)
759 {
761 }
762 }
763
764 private Task ControlFormCallback(object? Sender, DataFormEventArgs e)
765 {
766 if (e.Ok)
767 {
768 MainThread.BeginInvokeOnMainThread(async () =>
769 {
770 await ServiceRef.UiService.GoToAsync(nameof(XmppFormPage), new XmppFormNavigationArgs(e.Form));
771 });
772 }
773 else
774 {
775 ServiceRef.UiService.DisplayException(e.StanzaError ?? new Exception("Unable to get control form."));
776 }
777 MainThread.BeginInvokeOnMainThread(() => this.SetIsBusy(false));
778 return Task.CompletedTask;
779 }
780
781
782 private bool CanChat => !this.IsBusy && this.IsConnectedAndNotConcentrator;
783
787 [RelayCommand(CanExecute = nameof(CanChat))]
788 private async Task Chat()
789 {
790 await MainThread.InvokeOnMainThreadAsync(() => this.SetIsBusy(true));
791
792 if (this.thing is null)
793 return;
794
795 try
796 {
797 string LegalId = this.thing.LegalId;
798 string FriendlyName = this.thing.FriendlyName;
799 ChatNavigationArgs Args = new(LegalId, this.thing.BareJid, FriendlyName);
800
801 await ServiceRef.UiService.GoToAsync(nameof(ChatPage), Args, BackMethod.Inherited, this.thing.BareJid);
802 }
803 catch (Exception ex)
804 {
806 }
807 finally
808 {
809 await MainThread.InvokeOnMainThreadAsync(() => this.SetIsBusy(false));
810
811 }
812 }
813
814 private void NotificationService_OnNotificationsDeleted(object? Sender, NotificationEventsArgs e)
815 {
816 if (this.thing is null)
817 return;
818
819 MainThread.BeginInvokeOnMainThread(() =>
820 {
821 bool IsNode = this.IsNodeInConcentrator;
822 string Key = this.thing.ThingNotificationCategoryKey;
823 int NrChatMessagesRemoved = 0;
824
825 foreach (NotificationEvent Event in e.Events)
826 {
827 switch (Event.Type)
828 {
829 case NotificationEventType.Contacts:
830 if (IsNode)
831 continue;
832
833 if (Event.Category != this.thing.BareJid)
834 continue;
835 break;
836
837 case NotificationEventType.Things:
838 if (Event.Category != Key)
839 continue;
840 break;
841
842 default:
843 continue;
844 }
845
846 int i = 0;
847
848 foreach (EventModel Model in this.Notifications)
849 {
850 if (Model.Event.ObjectId == Event.ObjectId)
851 {
852 this.Notifications.RemoveAt(i);
853
854 if (Event.Type == NotificationEventType.Contacts)
855 NrChatMessagesRemoved++;
856
857 break;
858 }
859
860 i++;
861 }
862 }
863
864 this.NrPendingChatMessages -= NrChatMessagesRemoved;
865 this.HasNotifications = this.Notifications.Count > 0;
866 this.HasPendingChatMessages = this.NrPendingChatMessages > 0;
867 });
868 }
869
870 private void NotificationService_OnNewNotification(object? Sender, NotificationEventArgs e)
871 {
872 MainThread.BeginInvokeOnMainThread(async () =>
873 {
874 try
875 {
876 await this.CalcThingIsOnline();
877
878 switch (e.Event.Type)
879 {
880 case NotificationEventType.Contacts:
881 if (this.IsNodeInConcentrator)
882 return;
883
884 if (e.Event.Category != this.thing?.BareJid)
885 return;
886 break;
887
888 case NotificationEventType.Things:
889 if (e.Event.Category != this.thing?.ThingNotificationCategoryKey)
890 return;
891 break;
892
893 default:
894 return;
895 }
896
897 this.Notifications.Add(new EventModel(e.Event.Received,
898 await e.Event.GetCategoryIcon(),
899 await e.Event.GetDescription(),
900 e.Event));
901
902 this.HasNotifications = true;
903
904 if (e.Event.Type == NotificationEventType.Contacts)
905 {
906 this.NrPendingChatMessages++;
907 this.HasPendingChatMessages = true;
908 }
909 }
910 catch (Exception ex)
911 {
912 ServiceRef.LogService.LogException(ex);
913 }
914 });
915 }
916
917 private Task Xmpp_OnRosterItemRemoved(object? Sender, RosterItem Item)
918 {
919 this.presences.Remove(Item.BareJid);
920 MainThread.BeginInvokeOnMainThread(async () => await this.CalcThingIsOnline());
921 return Task.CompletedTask;
922 }
923
924 private Task Xmpp_OnRosterItemUpdated(object? Sender, RosterItem Item)
925 {
926 MainThread.BeginInvokeOnMainThread(async () => await this.CalcThingIsOnline());
927 return Task.CompletedTask;
928 }
929
930 private Task Xmpp_OnRosterItemAdded(object? Sender, RosterItem Item)
931 {
932 MainThread.BeginInvokeOnMainThread(async () => await this.CalcThingIsOnline());
933 return Task.CompletedTask;
934 }
935
936 #region ILinkableView
937
941 public override bool IsLinkable => true;
942
946 public override bool EncodeAppLinks => true;
947
951 public override string Link
952 {
953 get
954 {
955 StringBuilder sb = new();
956 bool HasJid = false;
957 bool HasSourceId = false;
958 bool HasPartition = false;
959 bool HasNodeId = false;
960 bool HasRegistry = false;
961
962 sb.Append("iotdisco:");
963
964 if (this.thing is not null)
965 {
966 if (this.thing.MetaData is not null)
967 {
968 foreach (Property P in this.thing.MetaData)
969 {
970
971 switch (P.Name.ToUpper(CultureInfo.InvariantCulture))
972 {
977 sb.Append('#');
978 break;
979
981 HasJid = true;
982 break;
983
985 HasSourceId = true;
986 break;
987
989 HasPartition = true;
990 break;
991
993 HasNodeId = true;
994 break;
995
997 HasRegistry = true;
998 break;
999 }
1000 sb.Append(Uri.EscapeDataString(P.Name));
1001 sb.Append('=');
1002 sb.Append(Uri.EscapeDataString(P.Value));
1003 sb.Append(';');
1004
1005 }
1006 }
1007
1008 if (!HasJid)
1009 {
1010 sb.Append("JID=");
1011 sb.Append(Uri.EscapeDataString(this.thing.BareJid));
1012 }
1013
1014 if (!HasSourceId && !string.IsNullOrEmpty(this.thing.SourceId))
1015 {
1016 sb.Append(";SID=");
1017 sb.Append(Uri.EscapeDataString(this.thing.SourceId));
1018 }
1019
1020 if (!HasPartition && !string.IsNullOrEmpty(this.thing.Partition))
1021 {
1022 sb.Append(";PT=");
1023 sb.Append(Uri.EscapeDataString(this.thing.Partition));
1024 }
1025
1026 if (!HasNodeId && !string.IsNullOrEmpty(this.thing.NodeId))
1027 {
1028 sb.Append(";NID=");
1029 sb.Append(Uri.EscapeDataString(this.thing.NodeId));
1030 }
1031
1032 if (!HasRegistry && !string.IsNullOrEmpty(this.thing.RegistryJid))
1033 {
1034 sb.Append(";R=");
1035 sb.Append(Uri.EscapeDataString(this.thing.RegistryJid));
1036 }
1037 }
1038
1039 return sb.ToString();
1040 }
1041 }
1042
1046 public override Task<string> Title => Task.FromResult(this.thing?.FriendlyName ?? string.Empty);
1047
1051 public override bool HasMedia => false;
1052
1056 public override byte[]? Media => null;
1057
1061 public override string? MediaContentType => null;
1062
1063 #endregion
1064 }
1065}
The Application class, representing an instance of the Neuro-Access app.
Definition: App.xaml.cs:69
static LanguageInfo SelectedLanguage
Selected language.
Definition: App.xaml.cs:196
static Task< bool > AuthenticateUser(AuthenticationPurpose Purpose, bool Force=false)
Authenticates the user using the configured authentication method.
Definition: App.xaml.cs:981
A set of never changing property constants and helpful values.
Definition: Constants.cs:7
Contains information about a contact.
Definition: ContactInfo.cs:21
Base class that references services in the app.
Definition: ServiceRef.cs:31
static ILogService LogService
Log service.
Definition: ServiceRef.cs:91
static INetworkService NetworkService
Network service.
Definition: ServiceRef.cs:103
static IUiService UiService
Service serializing and managing UI-related tasks.
Definition: ServiceRef.cs:55
static ITagProfile TagProfile
TAG Profile service.
Definition: ServiceRef.cs:79
static IStringLocalizer Localizer
Localization service
Definition: ServiceRef.cs:235
static IXmppService XmppService
The XMPP service for XMPP communication.
Definition: ServiceRef.cs:67
virtual async Task GoBack()
Method called when user wants to navigate to the previous screen.
virtual void SetIsBusy(bool IsBusy)
Sets the IsBusy property.
Holds navigation parameters specific to views displaying a list of contacts.
A page that displays a list of the current user's contacts.
A page that displays an XMPP Form to the user.
A view model that holds the XMPP state.
void GenerateQrCode(string Uri)
Generates a QR-code
Class used to present a meta-data tag in a human interface.
The view model to bind to when displaying the list of things.
static ? NotificationEvent[] GetNotificationEvents(ContactInfo Thing)
Gets available notification events related to a thing.
A page that displays sensor data from a sensor.
Holds navigation parameters specific to viewing things.
The view model to bind to when displaying a thing.
ViewThingViewModel(ViewThingNavigationArgs? Args)
Creates an instance of the ViewThingViewModel class.
override async Task OnDispose()
Method called when the view is disposed, and will not be used more. Use this method to unregister eve...
override Task XmppService_ConnectionStateChanged(object? _, XmppState NewState)
Listens to connection state changes from the XMPP server.
override async Task OnInitialize()
Method called when view is initialized for the first time. Use this method to implement registration ...
ObservableCollection< EventModel > Notifications
Holds a list of notifications.
ObservableCollection< HumanReadableTag > Tags
Holds a list of meta-data tags associated with a thing.
Implements an XMPP concentrator server interface.
static readonly string[] NamespacesConcentrator
Supported concentrator namespaces.
Implements an XMPP control client interface.
static readonly string[] NamespacesControl
Supported control namespaces
Event arguments for data form results.
Class containing information about media content in a data form.
Definition: Media.cs:18
bool Ok
If the response is an OK result response (true), or an error response (false).
XmppException StanzaError
Any stanza error returned.
Maintains information about an item in the roster.
Definition: RosterItem.cs:75
Implements an XMPP sensor client interface.
Definition: SensorClient.cs:21
static readonly string[] NamespacesSensorEvents
Supported sensor event namespaces.
Definition: SensorClient.cs:64
static readonly string[] NamespacesSensorData
Supported sensor-data namespaces.
Definition: SensorClient.cs:40
Contains information about an item of an entity.
Definition: Item.cs:11
bool HasAnyFeature(params string[] Features)
Checks if the remote entity supports any of a set of features.
Static interface for database persistence. In order to work, a database provider has to be assigned t...
Definition: Database.cs:19
static IDatabaseProvider Provider
Registered database provider.
Definition: Database.cs:57
static async Task Update(object Object)
Updates an object in the database.
Definition: Database.cs:626
static async Task Delete(object Object)
Deletes an object in the database.
Definition: Database.cs:717
static async Task Insert(object Object)
Inserts an object into the default collection of the database.
Definition: Database.cs:95
Contains a reference to a thing
Task DisplayException(Exception Exception, string? Title=null)
Displays an alert/message box to the user.
Task GoToAsync(string Route, BackMethod BackMethod=BackMethod.Inherited, string? UniqueId=null)
Navigates the AppShell to the specified route, with page arguments to match.
Task< bool > DisplayAlert(string Title, string Message, string? Accept=null, string? Cancel=null)
Displays an alert/message box to the user.
Task Flush()
Persists any pending changes.
abstract class NotificationEvent()
Abstract base class of notification events.
class NotificationEventsArgs(NotificationEvent[] Events)
Event argument for notification events.
class NotificationEventArgs(NotificationEvent Event)
Event argument for notification events.
NotificationEventType
Button on which event is to be displayed.
BackMethod
Navigation Back Method
Definition: BackMethod.cs:7
AuthenticationPurpose
Purpose for requesting the user to authenticate itself.
Notifications
Determines if the resource is observable, and how notifications are sent.
Definition: CoapResource.cs:15
PresenceType
Type of presence received.
Definition: PresenceType.cs:7
SubscriptionState
State of a presence subscription.
Definition: RosterItem.cs:16
XmppState
State of XMPP connection.
Definition: XmppState.cs:7