2using System.Runtime.CompilerServices;
3using System.Runtime.ExceptionServices;
15 internal class NetworkService : LoadableService, INetworkService
17 private const int defaultXmppPortNumber = 5222;
19 public event EventHandler<ConnectivityChangedEventArgs>? ConnectivityChanged;
21 public NetworkService()
26 public override Task Load(
bool IsResuming, CancellationToken CancellationToken)
28 if (this.
BeginLoad(IsResuming, CancellationToken))
30 if (DeviceInfo.Platform != DevicePlatform.Unknown && !DesignMode.IsDesignModeEnabled)
31 Connectivity.ConnectivityChanged += this.Connectivity_ConnectivityChanged;
36 return Task.CompletedTask;
40 public override Task Unload()
44 if (DeviceInfo.Platform != DevicePlatform.Unknown)
45 Connectivity.ConnectivityChanged -= this.Connectivity_ConnectivityChanged;
50 return Task.CompletedTask;
53 private void Connectivity_ConnectivityChanged(
object? Sender, ConnectivityChangedEventArgs e)
55 this.ConnectivityChanged?.Invoke(
this, e);
58 public virtual bool IsOnline =>
59 Connectivity.NetworkAccess == NetworkAccess.Internet ||
60 Connectivity.NetworkAccess == NetworkAccess.ConstrainedInternet;
62 public async Task<(
string HostName,
int Port,
bool IsIpAddress)> LookupXmppHostnameAndPort(
string DomainName)
64 if (IPAddress.TryParse(DomainName, out IPAddress? _))
65 return (DomainName, defaultXmppPortNumber,
true);
71 if (endpoint is not
null && !
string.IsNullOrWhiteSpace(endpoint.
TargetHost) && endpoint.Port > 0)
79 return (DomainName, defaultXmppPortNumber,
false);
82 public async Task<bool> TryRequest(Func<Task> func,
bool rethrowException =
false,
bool displayAlert =
true,
83 [CallerMemberName]
string memberName =
"")
85 (
bool succeeded,
bool _) = await this.PerformRequestInner(async () =>
89 }, memberName, rethrowException, displayAlert);
94 public Task<(
bool Succeeded, TReturn? ReturnValue)> TryRequest<TReturn>(Func<Task<TReturn>> func,
bool rethrowException =
false,
bool displayAlert =
true, [CallerMemberName]
string memberName =
"")
96 return this.PerformRequestInner(async () => await func(), memberName, rethrowException, displayAlert);
99 private async Task<(
bool Succeeded, TReturn? ReturnValue)> PerformRequestInner<TReturn>(Func<Task<TReturn>> func,
string memberName,
bool rethrowException =
false,
bool displayAlert =
true)
101 Exception thrownException;
107 ServiceRef.LogService.LogException(thrownException, GetParameter(memberName));
111 await ServiceRef.UiService.DisplayAlert(
112 ServiceRef.Localizer[nameof(AppResources.ErrorTitle)],
113 ServiceRef.Localizer[nameof(AppResources.ThereIsNoNetwork)]);
122 catch (AggregateException ae)
124 thrownException = ae;
126 if (ae.InnerException is TimeoutException te)
128 ServiceRef.LogService.LogException(te, GetParameter(memberName));
132 await ServiceRef.UiService.DisplayAlert(
133 ServiceRef.Localizer[nameof(AppResources.ErrorTitle)],
134 ServiceRef.Localizer[nameof(AppResources.RequestTimedOut)]);
137 else if (ae.InnerException is TaskCanceledException tce)
139 ServiceRef.LogService.LogException(tce, GetParameter(memberName));
143 await ServiceRef.UiService.DisplayAlert(
144 ServiceRef.Localizer[nameof(AppResources.ErrorTitle)],
145 ServiceRef.Localizer[nameof(AppResources.RequestWasCancelled)]);
148 else if (ae.InnerException is not
null)
150 ServiceRef.LogService.LogException(ae.InnerException, GetParameter(memberName));
154 await ServiceRef.UiService.DisplayAlert(
155 ServiceRef.Localizer[nameof(AppResources.ErrorTitle)],
156 ae.InnerException.Message);
161 ServiceRef.LogService.LogException(ae, GetParameter(memberName));
165 await ServiceRef.UiService.DisplayAlert(
166 ServiceRef.Localizer[nameof(AppResources.ErrorTitle)],
171 catch (TimeoutException te)
173 thrownException = te;
174 ServiceRef.LogService.LogException(te, GetParameter(memberName));
178 await ServiceRef.UiService.DisplayAlert(
179 ServiceRef.Localizer[nameof(AppResources.ErrorTitle)],
180 ServiceRef.Localizer[nameof(AppResources.RequestTimedOut)]);
183 catch (TaskCanceledException tce)
185 thrownException = tce;
186 ServiceRef.LogService.LogException(tce, GetParameter(memberName));
190 await ServiceRef.UiService.DisplayAlert(
191 ServiceRef.Localizer[nameof(AppResources.ErrorTitle)],
192 ServiceRef.Localizer[nameof(AppResources.RequestWasCancelled)]);
199 thrownException = ex;
202 message = xe.Stanza.InnerText;
204 message = ex.Message;
206 ServiceRef.LogService.LogException(ex, GetParameter(memberName));
210 await ServiceRef.UiService.DisplayAlert(
211 ServiceRef.Localizer[nameof(AppResources.ErrorTitle)],
216 if (rethrowException)
217 ExceptionDispatchInfo.Capture(thrownException).Throw();
219 return (
false,
default);
222 private static KeyValuePair<string, object?>[] GetParameter(
string MemberName)
224 if (!
string.IsNullOrWhiteSpace(MemberName))
228 new KeyValuePair<string, object?>(
"Caller", MemberName)
static readonly TimeSpan GenericRequest
Generic request timeout
A set of never changing property constants and helpful values.
Represents network errors.
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...
DNS resolver, as defined in:
static Task< SRV > LookupServiceEndpoint(string DomainName, string ServiceName, string Protocol)
Looks up a service endpoint for a domain. If multiple are available, an appropriate one is selected a...
string TargetHost
Target Host
Base class of XMPP exceptions
XmlElement Stanza
Stanza causing exception.