Skip to content
This repository has been archived by the owner on May 1, 2024. It is now read-only.

Commit

Permalink
GH-8: Add Connectivity API (#6)
Browse files Browse the repository at this point in the history
* Add base connectivity

* Update naming and put in Caboodle folder

* Add: NetworkAccess and Profiles instead of simple bools

* update sample and test device runner

* Implement Connection profiles and connection change events.

* Additional cleanup for compile

* Add tests and samples

* Fix saving list to not be reference type.

Ensures that when profiles change an event is triggered.

* Android dont' show non-connected profiles

* Refactor some code

Local functions!!!!!!!!

* Update exception names for compile

* Add connectivity documentation

* Add docs

* Fix build

* Delete unit test 1 from build

* Fix tests! woops

* Cleanup connectivity API checks and add remarks for exceptions on Android
  • Loading branch information
jamesmontemagno authored and Redth committed Mar 26, 2018
1 parent f64844c commit b6cffff
Show file tree
Hide file tree
Showing 24 changed files with 1,240 additions and 9 deletions.
24 changes: 24 additions & 0 deletions Caboodle.Tests/Connectivity_Tests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using Microsoft.Caboodle;
using Xunit;

namespace Caboodle.Tests
{
public class Connectivity_Tests
{
[Fact]
public void Network_Access_On_NetStandard() =>
Assert.Throws<NotImplementedInReferenceAssemblyException>(() => Connectivity.NetworkAccess);

[Fact]
public void Profiles_On_NetStandard() =>
Assert.Throws<NotImplementedInReferenceAssemblyException>(() => Connectivity.Profiles);

[Fact]
public void Connectivity_Changed_Event_On_NetStandard() =>
Assert.Throws<NotImplementedInReferenceAssemblyException>(() => Connectivity.ConnectivityChanged += Connectivity_ConnectivityChanged);

void Connectivity_ConnectivityChanged(ConnectivityChangedEventArgs e)
{
}
}
}
4 changes: 4 additions & 0 deletions Caboodle/Caboodle.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -47,20 +47,24 @@
</ItemGroup>
<ItemGroup Condition=" $(TargetFramework.StartsWith('netstandard')) ">
<Compile Include="**\*.netstandard.cs" />
<Compile Include="**\*.netstandard.*.cs" />
</ItemGroup>
<ItemGroup Condition=" $(TargetFramework.StartsWith('uap10.0')) ">
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform" Version="6.0.6" />
<SDKReference Include="WindowsMobile, Version=10.0.16299.0">
<Name>Windows Mobile Extensions for the UWP</Name>
</SDKReference>
<Compile Include="**\*.uwp.cs" />
<Compile Include="**\*.uwp.*.cs" />
</ItemGroup>
<ItemGroup Condition=" $(TargetFramework.StartsWith('MonoAndroid')) ">
<PackageReference Include="Xamarin.Android.Support.CustomTabs" Version="26.1.0.1" />
<Compile Include="**\*.android.cs" />
<Compile Include="**\*.android.*.cs" />
</ItemGroup>
<ItemGroup Condition=" $(TargetFramework.StartsWith('Xamarin.iOS')) ">
<Compile Include="**\*.ios.cs" />
<Compile Include="**\*.ios.*.cs" />
</ItemGroup>
<Import Project="$(MSBuildSDKExtrasTargets)" Condition="Exists('$(MSBuildSDKExtrasTargets)')" />
<Import Project="$(MSBuildThisFileDirectory)mdoc.targets" />
Expand Down
9 changes: 3 additions & 6 deletions Caboodle/Clipboard/Clipboard.android.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,13 @@ namespace Microsoft.Caboodle
{
public static partial class Clipboard
{
static ClipboardManager ClipboardManager
=> (ClipboardManager)Application.Context.GetSystemService(Context.ClipboardService);

public static void SetText(string text)
=> ClipboardManager.PrimaryClip = ClipData.NewPlainText("Text", text);
=> Platform.ClipboardManager.PrimaryClip = ClipData.NewPlainText("Text", text);

public static bool HasText
=> ClipboardManager.HasPrimaryClip;
=> Platform.ClipboardManager.HasPrimaryClip;

public static Task<string> GetTextAsync()
=> Task.FromResult(ClipboardManager.PrimaryClip?.GetItemAt(0)?.Text);
=> Task.FromResult(Platform.ClipboardManager.PrimaryClip?.GetItemAt(0)?.Text);
}
}
226 changes: 226 additions & 0 deletions Caboodle/Connectivity/Connectivity.android.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Android;
using Android.App;
using Android.Content;
using Android.Net;
using Android.Net.Wifi;
using Android.OS;

namespace Microsoft.Caboodle
{
public partial class Connectivity
{
static ConnectivityBroadcastReceiver conectivityReceiver;
static bool hasPermission;

static void ValidatePermission()
{
if (hasPermission)
return;

var permission = Manifest.Permission.AccessNetworkState;
if (!Platform.HasPermissionInManifest(permission))
throw new PermissionException(permission);

hasPermission = true;
}

static void StartListeners()
{
ValidatePermission();
conectivityReceiver = new ConnectivityBroadcastReceiver(OnConnectivityChanged);
Platform.CurrentContext.RegisterReceiver(conectivityReceiver, new IntentFilter(ConnectivityManager.ConnectivityAction));
}

static void StopListeners()
{
Platform.CurrentContext.UnregisterReceiver(conectivityReceiver);
conectivityReceiver?.Dispose();
conectivityReceiver = null;
}

static NetworkAccess IsBetterAccess(NetworkAccess currentAccess, NetworkAccess newAccess) =>
newAccess > currentAccess ? newAccess : currentAccess;

public static NetworkAccess NetworkAccess
{
get
{
ValidatePermission();
try
{
var currentAccess = NetworkAccess.None;
var manager = Platform.ConnectivityManager;

if (Platform.HasApiLevel(BuildVersionCodes.Lollipop))
{
foreach (var network in manager.GetAllNetworks())
{
try
{
var capabilities = manager.GetNetworkCapabilities(network);

if (capabilities == null)
continue;

// Check to see if it has the internet capability
if (!capabilities.HasCapability(NetCapability.Internet))
{
// Doesn't have internet, but local is possible
currentAccess = IsBetterAccess(currentAccess, NetworkAccess.Local);
continue;
}

var info = manager.GetNetworkInfo(network);

ProcessNetworkInfo(info);
}
catch
{
// there is a possibility, but don't worry
}
}
}
else
{
#pragma warning disable CS0618 // Type or member is obsolete
foreach (var info in manager.GetAllNetworkInfo())
#pragma warning restore CS0618 // Type or member is obsolete
{
ProcessNetworkInfo(info);
}
}

void ProcessNetworkInfo(NetworkInfo info)
{
if (info == null || !info.IsAvailable)
return;

if (info.IsConnected)
currentAccess = IsBetterAccess(currentAccess, NetworkAccess.Internet);
else if (info.IsConnectedOrConnecting)
currentAccess = IsBetterAccess(currentAccess, NetworkAccess.ConstrainedInternet);
}

return currentAccess;
}
catch (Exception e)
{
Console.WriteLine("Unable to get connected state - do you have ACCESS_NETWORK_STATE permission? - error: {0}", e);
return NetworkAccess.Unknown;
}
}
}

public static IEnumerable<ConnectionProfile> Profiles
{
get
{
ValidatePermission();
var manager = Platform.ConnectivityManager;
if (Platform.HasApiLevel(BuildVersionCodes.Lollipop))
{
foreach (var network in manager.GetAllNetworks())
{
NetworkInfo info = null;
try
{
info = manager.GetNetworkInfo(network);
}
catch
{
// there is a possibility, but don't worry about it
}

var p = ProcessNetworkInfo(info);
if (p.HasValue)
yield return p.Value;
}
}
else
{
#pragma warning disable CS0618 // Type or member is obsolete
foreach (var info in manager.GetAllNetworkInfo())
#pragma warning restore CS0618 // Type or member is obsolete
{
var p = ProcessNetworkInfo(info);
if (p.HasValue)
yield return p.Value;
}
}

ConnectionProfile? ProcessNetworkInfo(NetworkInfo info)
{
if (info == null || !info.IsAvailable || !info.IsConnectedOrConnecting)
return null;

return GetConnectionType(info.Type, info.TypeName);
}
}
}

internal static ConnectionProfile GetConnectionType(ConnectivityType connectivityType, string typeName)
{
switch (connectivityType)
{
case ConnectivityType.Ethernet:
return ConnectionProfile.Ethernet;
case ConnectivityType.Wimax:
return ConnectionProfile.WiMAX;
case ConnectivityType.Wifi:
return ConnectionProfile.WiFi;
case ConnectivityType.Bluetooth:
return ConnectionProfile.Bluetooth;
case ConnectivityType.Mobile:
case ConnectivityType.MobileDun:
case ConnectivityType.MobileHipri:
case ConnectivityType.MobileMms:
return ConnectionProfile.Cellular;
case ConnectivityType.Dummy:
return ConnectionProfile.Other;
default:
if (string.IsNullOrWhiteSpace(typeName))
return ConnectionProfile.Other;

var typeNameLower = typeName.ToLowerInvariant();
if (typeNameLower.Contains("mobile"))
return ConnectionProfile.Cellular;

if (typeNameLower.Contains("wifi"))
return ConnectionProfile.WiFi;

if (typeNameLower.Contains("wimax"))
return ConnectionProfile.WiMAX;

if (typeNameLower.Contains("ethernet"))
return ConnectionProfile.Ethernet;

if (typeNameLower.Contains("bluetooth"))
return ConnectionProfile.Bluetooth;

return ConnectionProfile.Other;
}
}
}

class ConnectivityBroadcastReceiver : BroadcastReceiver
{
Action onChanged;

public ConnectivityBroadcastReceiver(Action onChanged) =>
this.onChanged = onChanged;

public override async void OnReceive(Context context, Intent intent)
{
if (intent.Action != ConnectivityManager.ConnectivityAction)
return;

// await 500ms to ensure that the the connection manager updates
await Task.Delay(500);
onChanged?.Invoke();
}
}
}
61 changes: 61 additions & 0 deletions Caboodle/Connectivity/Connectivity.ios.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;

namespace Microsoft.Caboodle
{
public static partial class Connectivity
{
static void StartListeners() =>
Reachability.ReachabilityChanged += ReachabilityChanged;

static async void ReachabilityChanged(object sender, EventArgs e)
{
await Task.Delay(100);
OnConnectivityChanged();
}

static void StopListeners() =>
Reachability.ReachabilityChanged -= ReachabilityChanged;

public static NetworkAccess NetworkAccess
{
get
{
var remoteHostStatus = Reachability.RemoteHostStatus();
var internetStatus = Reachability.InternetConnectionStatus();

var isConnected = (internetStatus == NetworkStatus.ReachableViaCarrierDataNetwork ||
internetStatus == NetworkStatus.ReachableViaWiFiNetwork) ||
(remoteHostStatus == NetworkStatus.ReachableViaCarrierDataNetwork ||
remoteHostStatus == NetworkStatus.ReachableViaWiFiNetwork);

return isConnected ? NetworkAccess.Internet : NetworkAccess.None;
}
}

public static IEnumerable<ConnectionProfile> Profiles
{
get
{
var statuses = Reachability.GetActiveConnectionType();
foreach (var status in statuses)
{
switch (status)
{
case NetworkStatus.ReachableViaCarrierDataNetwork:
yield return ConnectionProfile.Cellular;
break;
case NetworkStatus.ReachableViaWiFiNetwork:
yield return ConnectionProfile.WiFi;
break;
default:
yield return ConnectionProfile.Other;
break;
}
}
}
}
}
}
Loading

0 comments on commit b6cffff

Please sign in to comment.