diff --git a/powered-up.sln b/powered-up.sln index a773733..a10b2da 100644 --- a/powered-up.sln +++ b/powered-up.sln @@ -1,103 +1,121 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26124.0 -MinimumVisualStudioVersion = 15.0.26124.0 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{62C31C3D-8ACF-4ED3-A3D8-225536F3AC6D}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpBrick.PoweredUp", "src\SharpBrick.PoweredUp\SharpBrick.PoweredUp.csproj", "{5A2B0183-09CE-46BD-A9F4-7C8F1E809B67}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{39B30145-497F-4AEB-A014-BBF27DA0651A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpBrick.PoweredUp.Test", "test\SharpBrick.PoweredUp.Test\SharpBrick.PoweredUp.Test.csproj", "{99DD765B-CEF4-49CA-B613-A44805C63D6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpBrick.PoweredUp.WinRT", "src\SharpBrick.PoweredUp.WinRT\SharpBrick.PoweredUp.WinRT.csproj", "{B43B306D-A1AE-40AD-B912-D15DBDB83BAA}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{F4D84196-5E85-41FE-9C39-38BD8F78E84D}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpBrick.PoweredUp.Examples", "examples\SharpBrick.PoweredUp.Examples\SharpBrick.PoweredUp.Examples.csproj", "{D82C23F2-86B4-4847-B2BD-7F194279FD43}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpBrick.PoweredUp.Cli", "src\SharpBrick.PoweredUp.Cli\SharpBrick.PoweredUp.Cli.csproj", "{E2D7D98D-9F20-4761-B507-D379A530E77D}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {5A2B0183-09CE-46BD-A9F4-7C8F1E809B67}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5A2B0183-09CE-46BD-A9F4-7C8F1E809B67}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5A2B0183-09CE-46BD-A9F4-7C8F1E809B67}.Debug|x64.ActiveCfg = Debug|Any CPU - {5A2B0183-09CE-46BD-A9F4-7C8F1E809B67}.Debug|x64.Build.0 = Debug|Any CPU - {5A2B0183-09CE-46BD-A9F4-7C8F1E809B67}.Debug|x86.ActiveCfg = Debug|Any CPU - {5A2B0183-09CE-46BD-A9F4-7C8F1E809B67}.Debug|x86.Build.0 = Debug|Any CPU - {5A2B0183-09CE-46BD-A9F4-7C8F1E809B67}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5A2B0183-09CE-46BD-A9F4-7C8F1E809B67}.Release|Any CPU.Build.0 = Release|Any CPU - {5A2B0183-09CE-46BD-A9F4-7C8F1E809B67}.Release|x64.ActiveCfg = Release|Any CPU - {5A2B0183-09CE-46BD-A9F4-7C8F1E809B67}.Release|x64.Build.0 = Release|Any CPU - {5A2B0183-09CE-46BD-A9F4-7C8F1E809B67}.Release|x86.ActiveCfg = Release|Any CPU - {5A2B0183-09CE-46BD-A9F4-7C8F1E809B67}.Release|x86.Build.0 = Release|Any CPU - {99DD765B-CEF4-49CA-B613-A44805C63D6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {99DD765B-CEF4-49CA-B613-A44805C63D6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {99DD765B-CEF4-49CA-B613-A44805C63D6E}.Debug|x64.ActiveCfg = Debug|Any CPU - {99DD765B-CEF4-49CA-B613-A44805C63D6E}.Debug|x64.Build.0 = Debug|Any CPU - {99DD765B-CEF4-49CA-B613-A44805C63D6E}.Debug|x86.ActiveCfg = Debug|Any CPU - {99DD765B-CEF4-49CA-B613-A44805C63D6E}.Debug|x86.Build.0 = Debug|Any CPU - {99DD765B-CEF4-49CA-B613-A44805C63D6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {99DD765B-CEF4-49CA-B613-A44805C63D6E}.Release|Any CPU.Build.0 = Release|Any CPU - {99DD765B-CEF4-49CA-B613-A44805C63D6E}.Release|x64.ActiveCfg = Release|Any CPU - {99DD765B-CEF4-49CA-B613-A44805C63D6E}.Release|x64.Build.0 = Release|Any CPU - {99DD765B-CEF4-49CA-B613-A44805C63D6E}.Release|x86.ActiveCfg = Release|Any CPU - {99DD765B-CEF4-49CA-B613-A44805C63D6E}.Release|x86.Build.0 = Release|Any CPU - {B43B306D-A1AE-40AD-B912-D15DBDB83BAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B43B306D-A1AE-40AD-B912-D15DBDB83BAA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B43B306D-A1AE-40AD-B912-D15DBDB83BAA}.Debug|x64.ActiveCfg = Debug|Any CPU - {B43B306D-A1AE-40AD-B912-D15DBDB83BAA}.Debug|x64.Build.0 = Debug|Any CPU - {B43B306D-A1AE-40AD-B912-D15DBDB83BAA}.Debug|x86.ActiveCfg = Debug|Any CPU - {B43B306D-A1AE-40AD-B912-D15DBDB83BAA}.Debug|x86.Build.0 = Debug|Any CPU - {B43B306D-A1AE-40AD-B912-D15DBDB83BAA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B43B306D-A1AE-40AD-B912-D15DBDB83BAA}.Release|Any CPU.Build.0 = Release|Any CPU - {B43B306D-A1AE-40AD-B912-D15DBDB83BAA}.Release|x64.ActiveCfg = Release|Any CPU - {B43B306D-A1AE-40AD-B912-D15DBDB83BAA}.Release|x64.Build.0 = Release|Any CPU - {B43B306D-A1AE-40AD-B912-D15DBDB83BAA}.Release|x86.ActiveCfg = Release|Any CPU - {B43B306D-A1AE-40AD-B912-D15DBDB83BAA}.Release|x86.Build.0 = Release|Any CPU - {D82C23F2-86B4-4847-B2BD-7F194279FD43}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D82C23F2-86B4-4847-B2BD-7F194279FD43}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D82C23F2-86B4-4847-B2BD-7F194279FD43}.Debug|x64.ActiveCfg = Debug|Any CPU - {D82C23F2-86B4-4847-B2BD-7F194279FD43}.Debug|x64.Build.0 = Debug|Any CPU - {D82C23F2-86B4-4847-B2BD-7F194279FD43}.Debug|x86.ActiveCfg = Debug|Any CPU - {D82C23F2-86B4-4847-B2BD-7F194279FD43}.Debug|x86.Build.0 = Debug|Any CPU - {D82C23F2-86B4-4847-B2BD-7F194279FD43}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D82C23F2-86B4-4847-B2BD-7F194279FD43}.Release|Any CPU.Build.0 = Release|Any CPU - {D82C23F2-86B4-4847-B2BD-7F194279FD43}.Release|x64.ActiveCfg = Release|Any CPU - {D82C23F2-86B4-4847-B2BD-7F194279FD43}.Release|x64.Build.0 = Release|Any CPU - {D82C23F2-86B4-4847-B2BD-7F194279FD43}.Release|x86.ActiveCfg = Release|Any CPU - {D82C23F2-86B4-4847-B2BD-7F194279FD43}.Release|x86.Build.0 = Release|Any CPU - {E2D7D98D-9F20-4761-B507-D379A530E77D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E2D7D98D-9F20-4761-B507-D379A530E77D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E2D7D98D-9F20-4761-B507-D379A530E77D}.Debug|x64.ActiveCfg = Debug|Any CPU - {E2D7D98D-9F20-4761-B507-D379A530E77D}.Debug|x64.Build.0 = Debug|Any CPU - {E2D7D98D-9F20-4761-B507-D379A530E77D}.Debug|x86.ActiveCfg = Debug|Any CPU - {E2D7D98D-9F20-4761-B507-D379A530E77D}.Debug|x86.Build.0 = Debug|Any CPU - {E2D7D98D-9F20-4761-B507-D379A530E77D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E2D7D98D-9F20-4761-B507-D379A530E77D}.Release|Any CPU.Build.0 = Release|Any CPU - {E2D7D98D-9F20-4761-B507-D379A530E77D}.Release|x64.ActiveCfg = Release|Any CPU - {E2D7D98D-9F20-4761-B507-D379A530E77D}.Release|x64.Build.0 = Release|Any CPU - {E2D7D98D-9F20-4761-B507-D379A530E77D}.Release|x86.ActiveCfg = Release|Any CPU - {E2D7D98D-9F20-4761-B507-D379A530E77D}.Release|x86.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {5A2B0183-09CE-46BD-A9F4-7C8F1E809B67} = {62C31C3D-8ACF-4ED3-A3D8-225536F3AC6D} - {99DD765B-CEF4-49CA-B613-A44805C63D6E} = {39B30145-497F-4AEB-A014-BBF27DA0651A} - {B43B306D-A1AE-40AD-B912-D15DBDB83BAA} = {62C31C3D-8ACF-4ED3-A3D8-225536F3AC6D} - {D82C23F2-86B4-4847-B2BD-7F194279FD43} = {F4D84196-5E85-41FE-9C39-38BD8F78E84D} - {E2D7D98D-9F20-4761-B507-D379A530E77D} = {62C31C3D-8ACF-4ED3-A3D8-225536F3AC6D} - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31112.23 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{62C31C3D-8ACF-4ED3-A3D8-225536F3AC6D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharpBrick.PoweredUp", "src\SharpBrick.PoweredUp\SharpBrick.PoweredUp.csproj", "{5A2B0183-09CE-46BD-A9F4-7C8F1E809B67}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{39B30145-497F-4AEB-A014-BBF27DA0651A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharpBrick.PoweredUp.Test", "test\SharpBrick.PoweredUp.Test\SharpBrick.PoweredUp.Test.csproj", "{99DD765B-CEF4-49CA-B613-A44805C63D6E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharpBrick.PoweredUp.WinRT", "src\SharpBrick.PoweredUp.WinRT\SharpBrick.PoweredUp.WinRT.csproj", "{B43B306D-A1AE-40AD-B912-D15DBDB83BAA}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{F4D84196-5E85-41FE-9C39-38BD8F78E84D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharpBrick.PoweredUp.Examples", "examples\SharpBrick.PoweredUp.Examples\SharpBrick.PoweredUp.Examples.csproj", "{D82C23F2-86B4-4847-B2BD-7F194279FD43}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharpBrick.PoweredUp.Cli", "src\SharpBrick.PoweredUp.Cli\SharpBrick.PoweredUp.Cli.csproj", "{E2D7D98D-9F20-4761-B507-D379A530E77D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharpBrick.PoweredUp.Mobile", "src\SharpBrick.PoweredUp.Mobile\SharpBrick.PoweredUp.Mobile.csproj", "{F66A2B09-84B6-477D-9B15-926E771C7D80}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {5A2B0183-09CE-46BD-A9F4-7C8F1E809B67}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5A2B0183-09CE-46BD-A9F4-7C8F1E809B67}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5A2B0183-09CE-46BD-A9F4-7C8F1E809B67}.Debug|x64.ActiveCfg = Debug|Any CPU + {5A2B0183-09CE-46BD-A9F4-7C8F1E809B67}.Debug|x64.Build.0 = Debug|Any CPU + {5A2B0183-09CE-46BD-A9F4-7C8F1E809B67}.Debug|x86.ActiveCfg = Debug|Any CPU + {5A2B0183-09CE-46BD-A9F4-7C8F1E809B67}.Debug|x86.Build.0 = Debug|Any CPU + {5A2B0183-09CE-46BD-A9F4-7C8F1E809B67}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5A2B0183-09CE-46BD-A9F4-7C8F1E809B67}.Release|Any CPU.Build.0 = Release|Any CPU + {5A2B0183-09CE-46BD-A9F4-7C8F1E809B67}.Release|x64.ActiveCfg = Release|Any CPU + {5A2B0183-09CE-46BD-A9F4-7C8F1E809B67}.Release|x64.Build.0 = Release|Any CPU + {5A2B0183-09CE-46BD-A9F4-7C8F1E809B67}.Release|x86.ActiveCfg = Release|Any CPU + {5A2B0183-09CE-46BD-A9F4-7C8F1E809B67}.Release|x86.Build.0 = Release|Any CPU + {99DD765B-CEF4-49CA-B613-A44805C63D6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {99DD765B-CEF4-49CA-B613-A44805C63D6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {99DD765B-CEF4-49CA-B613-A44805C63D6E}.Debug|x64.ActiveCfg = Debug|Any CPU + {99DD765B-CEF4-49CA-B613-A44805C63D6E}.Debug|x64.Build.0 = Debug|Any CPU + {99DD765B-CEF4-49CA-B613-A44805C63D6E}.Debug|x86.ActiveCfg = Debug|Any CPU + {99DD765B-CEF4-49CA-B613-A44805C63D6E}.Debug|x86.Build.0 = Debug|Any CPU + {99DD765B-CEF4-49CA-B613-A44805C63D6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {99DD765B-CEF4-49CA-B613-A44805C63D6E}.Release|Any CPU.Build.0 = Release|Any CPU + {99DD765B-CEF4-49CA-B613-A44805C63D6E}.Release|x64.ActiveCfg = Release|Any CPU + {99DD765B-CEF4-49CA-B613-A44805C63D6E}.Release|x64.Build.0 = Release|Any CPU + {99DD765B-CEF4-49CA-B613-A44805C63D6E}.Release|x86.ActiveCfg = Release|Any CPU + {99DD765B-CEF4-49CA-B613-A44805C63D6E}.Release|x86.Build.0 = Release|Any CPU + {B43B306D-A1AE-40AD-B912-D15DBDB83BAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B43B306D-A1AE-40AD-B912-D15DBDB83BAA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B43B306D-A1AE-40AD-B912-D15DBDB83BAA}.Debug|x64.ActiveCfg = Debug|Any CPU + {B43B306D-A1AE-40AD-B912-D15DBDB83BAA}.Debug|x64.Build.0 = Debug|Any CPU + {B43B306D-A1AE-40AD-B912-D15DBDB83BAA}.Debug|x86.ActiveCfg = Debug|Any CPU + {B43B306D-A1AE-40AD-B912-D15DBDB83BAA}.Debug|x86.Build.0 = Debug|Any CPU + {B43B306D-A1AE-40AD-B912-D15DBDB83BAA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B43B306D-A1AE-40AD-B912-D15DBDB83BAA}.Release|Any CPU.Build.0 = Release|Any CPU + {B43B306D-A1AE-40AD-B912-D15DBDB83BAA}.Release|x64.ActiveCfg = Release|Any CPU + {B43B306D-A1AE-40AD-B912-D15DBDB83BAA}.Release|x64.Build.0 = Release|Any CPU + {B43B306D-A1AE-40AD-B912-D15DBDB83BAA}.Release|x86.ActiveCfg = Release|Any CPU + {B43B306D-A1AE-40AD-B912-D15DBDB83BAA}.Release|x86.Build.0 = Release|Any CPU + {D82C23F2-86B4-4847-B2BD-7F194279FD43}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D82C23F2-86B4-4847-B2BD-7F194279FD43}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D82C23F2-86B4-4847-B2BD-7F194279FD43}.Debug|x64.ActiveCfg = Debug|Any CPU + {D82C23F2-86B4-4847-B2BD-7F194279FD43}.Debug|x64.Build.0 = Debug|Any CPU + {D82C23F2-86B4-4847-B2BD-7F194279FD43}.Debug|x86.ActiveCfg = Debug|Any CPU + {D82C23F2-86B4-4847-B2BD-7F194279FD43}.Debug|x86.Build.0 = Debug|Any CPU + {D82C23F2-86B4-4847-B2BD-7F194279FD43}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D82C23F2-86B4-4847-B2BD-7F194279FD43}.Release|Any CPU.Build.0 = Release|Any CPU + {D82C23F2-86B4-4847-B2BD-7F194279FD43}.Release|x64.ActiveCfg = Release|Any CPU + {D82C23F2-86B4-4847-B2BD-7F194279FD43}.Release|x64.Build.0 = Release|Any CPU + {D82C23F2-86B4-4847-B2BD-7F194279FD43}.Release|x86.ActiveCfg = Release|Any CPU + {D82C23F2-86B4-4847-B2BD-7F194279FD43}.Release|x86.Build.0 = Release|Any CPU + {E2D7D98D-9F20-4761-B507-D379A530E77D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E2D7D98D-9F20-4761-B507-D379A530E77D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E2D7D98D-9F20-4761-B507-D379A530E77D}.Debug|x64.ActiveCfg = Debug|Any CPU + {E2D7D98D-9F20-4761-B507-D379A530E77D}.Debug|x64.Build.0 = Debug|Any CPU + {E2D7D98D-9F20-4761-B507-D379A530E77D}.Debug|x86.ActiveCfg = Debug|Any CPU + {E2D7D98D-9F20-4761-B507-D379A530E77D}.Debug|x86.Build.0 = Debug|Any CPU + {E2D7D98D-9F20-4761-B507-D379A530E77D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E2D7D98D-9F20-4761-B507-D379A530E77D}.Release|Any CPU.Build.0 = Release|Any CPU + {E2D7D98D-9F20-4761-B507-D379A530E77D}.Release|x64.ActiveCfg = Release|Any CPU + {E2D7D98D-9F20-4761-B507-D379A530E77D}.Release|x64.Build.0 = Release|Any CPU + {E2D7D98D-9F20-4761-B507-D379A530E77D}.Release|x86.ActiveCfg = Release|Any CPU + {E2D7D98D-9F20-4761-B507-D379A530E77D}.Release|x86.Build.0 = Release|Any CPU + {F66A2B09-84B6-477D-9B15-926E771C7D80}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F66A2B09-84B6-477D-9B15-926E771C7D80}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F66A2B09-84B6-477D-9B15-926E771C7D80}.Debug|x64.ActiveCfg = Debug|Any CPU + {F66A2B09-84B6-477D-9B15-926E771C7D80}.Debug|x64.Build.0 = Debug|Any CPU + {F66A2B09-84B6-477D-9B15-926E771C7D80}.Debug|x86.ActiveCfg = Debug|Any CPU + {F66A2B09-84B6-477D-9B15-926E771C7D80}.Debug|x86.Build.0 = Debug|Any CPU + {F66A2B09-84B6-477D-9B15-926E771C7D80}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F66A2B09-84B6-477D-9B15-926E771C7D80}.Release|Any CPU.Build.0 = Release|Any CPU + {F66A2B09-84B6-477D-9B15-926E771C7D80}.Release|x64.ActiveCfg = Release|Any CPU + {F66A2B09-84B6-477D-9B15-926E771C7D80}.Release|x64.Build.0 = Release|Any CPU + {F66A2B09-84B6-477D-9B15-926E771C7D80}.Release|x86.ActiveCfg = Release|Any CPU + {F66A2B09-84B6-477D-9B15-926E771C7D80}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {5A2B0183-09CE-46BD-A9F4-7C8F1E809B67} = {62C31C3D-8ACF-4ED3-A3D8-225536F3AC6D} + {99DD765B-CEF4-49CA-B613-A44805C63D6E} = {39B30145-497F-4AEB-A014-BBF27DA0651A} + {B43B306D-A1AE-40AD-B912-D15DBDB83BAA} = {62C31C3D-8ACF-4ED3-A3D8-225536F3AC6D} + {D82C23F2-86B4-4847-B2BD-7F194279FD43} = {F4D84196-5E85-41FE-9C39-38BD8F78E84D} + {E2D7D98D-9F20-4761-B507-D379A530E77D} = {62C31C3D-8ACF-4ED3-A3D8-225536F3AC6D} + {F66A2B09-84B6-477D-9B15-926E771C7D80} = {62C31C3D-8ACF-4ED3-A3D8-225536F3AC6D} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {34AA1641-ACED-43ED-A0DD-BA88E43A67A8} + EndGlobalSection +EndGlobal diff --git a/src/SharpBrick.PoweredUp.Mobile/INativeDeviceInfoProvider.cs b/src/SharpBrick.PoweredUp.Mobile/INativeDeviceInfoProvider.cs new file mode 100644 index 0000000..d0c1442 --- /dev/null +++ b/src/SharpBrick.PoweredUp.Mobile/INativeDeviceInfoProvider.cs @@ -0,0 +1,7 @@ +namespace SharpBrick.PoweredUp.Mobile +{ + public interface INativeDeviceInfoProvider + { + NativeDeviceInfo GetNativeDeviceInfo(object device); + } +} \ No newline at end of file diff --git a/src/SharpBrick.PoweredUp.Mobile/IServiceCollectionExtensionsForXamarin.cs b/src/SharpBrick.PoweredUp.Mobile/IServiceCollectionExtensionsForXamarin.cs new file mode 100644 index 0000000..7ba75a6 --- /dev/null +++ b/src/SharpBrick.PoweredUp.Mobile/IServiceCollectionExtensionsForXamarin.cs @@ -0,0 +1,16 @@ +using Microsoft.Extensions.DependencyInjection; +using Plugin.BLE; +using Plugin.BLE.Abstractions.Contracts; +using SharpBrick.PoweredUp.Bluetooth; + +namespace SharpBrick.PoweredUp.Mobile +{ + public static class IServiceCollectionExtensionsForXamarin + { + public static IServiceCollection AddXamarinBluetooth(this IServiceCollection self, INativeDeviceInfoProvider deviceInfoProvider) + => self + .AddSingleton(CrossBluetoothLE.Current) + .AddSingleton(deviceInfoProvider) + .AddSingleton(); + } +} diff --git a/src/SharpBrick.PoweredUp.Mobile/NativeDeviceInfo.cs b/src/SharpBrick.PoweredUp.Mobile/NativeDeviceInfo.cs new file mode 100644 index 0000000..48bd404 --- /dev/null +++ b/src/SharpBrick.PoweredUp.Mobile/NativeDeviceInfo.cs @@ -0,0 +1,9 @@ +namespace SharpBrick.PoweredUp.Mobile +{ + public class NativeDeviceInfo + { + public string MacAddress { get; set; } + + public ulong MacAddressNumeric { get; set; } + } +} \ No newline at end of file diff --git a/src/SharpBrick.PoweredUp.Mobile/SharpBrick.PoweredUp.Mobile.csproj b/src/SharpBrick.PoweredUp.Mobile/SharpBrick.PoweredUp.Mobile.csproj new file mode 100644 index 0000000..02b02c0 --- /dev/null +++ b/src/SharpBrick.PoweredUp.Mobile/SharpBrick.PoweredUp.Mobile.csproj @@ -0,0 +1,15 @@ + + + + netstandard2.1 + + + + + + + + + + + diff --git a/src/SharpBrick.PoweredUp.Mobile/XamarinPoweredUpBluetoothAdapter.cs b/src/SharpBrick.PoweredUp.Mobile/XamarinPoweredUpBluetoothAdapter.cs new file mode 100644 index 0000000..5bb28ae --- /dev/null +++ b/src/SharpBrick.PoweredUp.Mobile/XamarinPoweredUpBluetoothAdapter.cs @@ -0,0 +1,122 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Plugin.BLE.Abstractions; +using Plugin.BLE.Abstractions.Contracts; +using Plugin.BLE.Abstractions.EventArgs; +using SharpBrick.PoweredUp.Bluetooth; + +namespace SharpBrick.PoweredUp.Mobile +{ + public class XamarinPoweredUpBluetoothAdapter : IPoweredUpBluetoothAdapter + { + private readonly IAdapter _bluetoothAdapter; + private readonly INativeDeviceInfoProvider _deviceInfoProvider; + private readonly Dictionary _discoveredDevices = new Dictionary(); + + public XamarinPoweredUpBluetoothAdapter(IBluetoothLE bluetooth, INativeDeviceInfoProvider deviceInfoProvider) + { + _bluetoothAdapter = bluetooth.Adapter; + _deviceInfoProvider = deviceInfoProvider; + } + + public void Discover(Func discoveryHandler, CancellationToken cancellationToken = default) + { + _bluetoothAdapter.ScanMode = ScanMode.Balanced; + + _bluetoothAdapter.DeviceDiscovered += ReceivedHandler; + + cancellationToken.Register(async () => + { + await _bluetoothAdapter.StopScanningForDevicesAsync().ConfigureAwait(false); + _bluetoothAdapter.DeviceDiscovered -= ReceivedHandler; + }); + + _bluetoothAdapter.StartScanningForDevicesAsync(new Guid[] { new Guid(PoweredUpBluetoothConstants.LegoHubService) }, DeviceFilter, false).ConfigureAwait(false); + + async void ReceivedHandler(object sender, DeviceEventArgs args) + { + var info = new PoweredUpBluetoothDeviceInfo(); + + var advertisementRecord = args.Device.AdvertisementRecords.FirstOrDefault(x => x.Type == AdvertisementRecordType.ManufacturerSpecificData); + + if (advertisementRecord?.Data?.Length > 0) + { + var data = advertisementRecord.Data.ToList(); + data.RemoveRange(0, 2); + info.ManufacturerData = data.ToArray(); + + info.Name = args.Device.Name; + info.BluetoothAddress = _deviceInfoProvider.GetNativeDeviceInfo(args.Device.NativeDevice).MacAddressNumeric; + + AddInternalDevice(args.Device, info); + await discoveryHandler(info).ConfigureAwait(false); + } + } + } + + private void AddInternalDevice(IDevice device, PoweredUpBluetoothDeviceInfo info) + { + if (!_discoveredDevices.ContainsKey(info.BluetoothAddress)) + { + _discoveredDevices.Add(info.BluetoothAddress, device); + } + else + { + _discoveredDevices[info.BluetoothAddress] = device; + } + } + + private bool DeviceFilter(IDevice arg) + { + if (arg == null) + { + return false; + } + + var manufacturerData = arg.AdvertisementRecords.FirstOrDefault(x => x.Type == AdvertisementRecordType.ManufacturerSpecificData); + + if (manufacturerData?.Data == null || manufacturerData.Data.Length < 8) + { + return false; + } + + // https://lego.github.io/lego-ble-wireless-protocol-docs/index.html#advertising + // Length and Data Type Name seems to be already trimmed away + // Manufacturer ID should be 0x0397 but seems in little endian encoding. I found no notice for this in the documentation except in version number encoding + + return manufacturerData.Data[0] == 0x97 && manufacturerData.Data[1] == 0x03; + } + + public async Task GetDeviceAsync(ulong bluetoothAddress) + { + if (!_discoveredDevices.ContainsKey(bluetoothAddress)) + { + CancellationTokenSource cts = new CancellationTokenSource(10000); + + // trigger scan for 10 seconds + Discover((deviceInfo) => + { + return Task.Run(() => + { + cts.Cancel(false); + }); + + }, cts.Token); + + // 60 seconds will be ignored here, because the cancelation will happen after 10 seconds + await Task.Delay(60000, cts.Token).ContinueWith(task => { }); + + if (!_discoveredDevices.ContainsKey(bluetoothAddress)) + { + throw new NotSupportedException("Given bt address does not belong to a discovered device"); + } + } + + return new XamarinPoweredUpBluetoothDevice(_discoveredDevices[bluetoothAddress], _bluetoothAdapter); + } + } + +} diff --git a/src/SharpBrick.PoweredUp.Mobile/XamarinPoweredUpBluetoothCharacteristic.cs b/src/SharpBrick.PoweredUp.Mobile/XamarinPoweredUpBluetoothCharacteristic.cs new file mode 100644 index 0000000..d41078c --- /dev/null +++ b/src/SharpBrick.PoweredUp.Mobile/XamarinPoweredUpBluetoothCharacteristic.cs @@ -0,0 +1,51 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Plugin.BLE.Abstractions; +using Plugin.BLE.Abstractions.Contracts; +using Plugin.BLE.Abstractions.EventArgs; +using SharpBrick.PoweredUp.Bluetooth; + +namespace SharpBrick.PoweredUp.Mobile +{ + public class XamarinPoweredUpBluetoothCharacteristic : IPoweredUpBluetoothCharacteristic + { + private ICharacteristic _characteristic; + + public Guid Uuid => _characteristic.Id; + + public XamarinPoweredUpBluetoothCharacteristic(ICharacteristic characteristic) + { + this._characteristic = characteristic; + } + + public async Task NotifyValueChangeAsync(Func notificationHandler) + { + if (notificationHandler is null) + { + throw new ArgumentNullException(nameof(notificationHandler)); + } + + _characteristic.ValueUpdated += ValueUpdatedHandler; + + void ValueUpdatedHandler(object sender, CharacteristicUpdatedEventArgs e) + { + notificationHandler(e.Characteristic.Value); + } + + await _characteristic.StartUpdatesAsync(); + + return true; + } + + public async Task WriteValueAsync(byte[] data) + { + if (data is null) + { + throw new ArgumentNullException(nameof(data)); + } + + return await _characteristic.WriteAsync(data); + } + } +} diff --git a/src/SharpBrick.PoweredUp.Mobile/XamarinPoweredUpBluetoothDevice.cs b/src/SharpBrick.PoweredUp.Mobile/XamarinPoweredUpBluetoothDevice.cs new file mode 100644 index 0000000..e479dfe --- /dev/null +++ b/src/SharpBrick.PoweredUp.Mobile/XamarinPoweredUpBluetoothDevice.cs @@ -0,0 +1,49 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Plugin.BLE.Abstractions; +using Plugin.BLE.Abstractions.Contracts; +using SharpBrick.PoweredUp.Bluetooth; + +namespace SharpBrick.PoweredUp.Mobile +{ + public class XamarinPoweredUpBluetoothDevice : IPoweredUpBluetoothDevice + { + private IDevice _device; + private IAdapter _adapter; + + public string Name => this._device.Name; + + public XamarinPoweredUpBluetoothDevice(IDevice device, IAdapter bluetoothAdapter) + { + this._device = device; + this._adapter = bluetoothAdapter; + } + + #region IDisposible + + ~XamarinPoweredUpBluetoothDevice() => Dispose(false); + + public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } + + protected virtual void Dispose(bool disposing) + { + _device?.Dispose(); + _device = null; + _adapter = null; + } + + #endregion + + public async Task GetServiceAsync(Guid serviceId) + { + await _adapter.ConnectToDeviceAsync(_device, new ConnectParameters(true, true)).ConfigureAwait(false); + + if (!_adapter.ConnectedDevices.Contains(_device)) return null; + + var service = await _device.GetServiceAsync(serviceId).ConfigureAwait(false); + + return new XamarinPoweredUpBluetoothService(service); + } + } +} diff --git a/src/SharpBrick.PoweredUp.Mobile/XamarinPoweredUpBluetoothService.cs b/src/SharpBrick.PoweredUp.Mobile/XamarinPoweredUpBluetoothService.cs new file mode 100644 index 0000000..1275625 --- /dev/null +++ b/src/SharpBrick.PoweredUp.Mobile/XamarinPoweredUpBluetoothService.cs @@ -0,0 +1,44 @@ +using System; +using System.Threading.Tasks; +using Plugin.BLE.Abstractions.Contracts; +using SharpBrick.PoweredUp.Bluetooth; + +namespace SharpBrick.PoweredUp.Mobile +{ + public class XamarinPoweredUpBluetoothService : IPoweredUpBluetoothService + { + private IService _service; + + public Guid Uuid => _service.Id; + + public XamarinPoweredUpBluetoothService(IService service) + { + _service = service ?? throw new ArgumentNullException(nameof(service)); + } + + #region IDisposable + + ~XamarinPoweredUpBluetoothService() => Dispose(false); + + public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } + + protected virtual void Dispose(bool disposing) + { + _service.Dispose(); + _service = null; + } + + #endregion + + public async Task GetCharacteristicAsync(Guid guid) + { + var characteristic = await _service.GetCharacteristicAsync(guid); + + if (characteristic == null) return null; + + // await characteristic.StartUpdatesAsync(); + return new XamarinPoweredUpBluetoothCharacteristic(characteristic); + + } + } +} diff --git a/src/SharpBrick.PoweredUp/CompilerService.cs b/src/SharpBrick.PoweredUp/CompilerService.cs new file mode 100644 index 0000000..8172780 --- /dev/null +++ b/src/SharpBrick.PoweredUp/CompilerService.cs @@ -0,0 +1,10 @@ +namespace System.Runtime.CompilerServices +{ +#if NETSTANDARD2_1 + /// + /// Dummy compilerServices which is only included in .NET5 (upwards). To be compatible with .NetStandard 2.1 a dummy is required. + /// + /// + internal static class IsExternalInit { } +#endif +} \ No newline at end of file diff --git a/src/SharpBrick.PoweredUp/SharpBrick.PoweredUp.csproj b/src/SharpBrick.PoweredUp/SharpBrick.PoweredUp.csproj index 1276a28..2ef8d10 100644 --- a/src/SharpBrick.PoweredUp/SharpBrick.PoweredUp.csproj +++ b/src/SharpBrick.PoweredUp/SharpBrick.PoweredUp.csproj @@ -1,7 +1,8 @@ - net5.0 + netstandard2.1;net5.0 + 9.0