Bluetooth LE Explorer first commit to GitHub
|
@ -0,0 +1,125 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.26430.16
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GattServicesLibrary", "..\GattServicesLibrary\GattServicesLibrary\GattServicesLibrary.csproj", "{644F40AD-EABB-4570-B9B7-E3F8DDAA80F3}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GattHelper", "GattHelper\GattHelper.csproj", "{37C63FDD-D995-4CC2-B014-4FB323194001}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SortedObservableCollection", "SortedObservableCollection", "{92EE95F6-40B6-4E5A-97C9-E4234D17C022}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SortedObservableCollection", "..\SortedObservableCollection\SortedObservableCollection\SortedObservableCollection.csproj", "{BE79FC52-6041-4913-B0D4-66C100944904}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BluetoothLEExplorer", "BluetoothLEExplorer\BluetoothLEExplorer.csproj", "{AD1CBE3C-A68A-4E02-8001-E02B815560EE}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BluetoothLEExplorerUnitTests", "BluetoothLEExplorerUnitTests\BluetoothLEExplorerUnitTests.csproj", "{6F503DF9-71C9-4340-90BB-D9AA14ADB686}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|ARM = Debug|ARM
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|ARM = Release|ARM
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{644F40AD-EABB-4570-B9B7-E3F8DDAA80F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{644F40AD-EABB-4570-B9B7-E3F8DDAA80F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{644F40AD-EABB-4570-B9B7-E3F8DDAA80F3}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||
{644F40AD-EABB-4570-B9B7-E3F8DDAA80F3}.Debug|ARM.Build.0 = Debug|ARM
|
||||
{644F40AD-EABB-4570-B9B7-E3F8DDAA80F3}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{644F40AD-EABB-4570-B9B7-E3F8DDAA80F3}.Debug|x64.Build.0 = Debug|x64
|
||||
{644F40AD-EABB-4570-B9B7-E3F8DDAA80F3}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{644F40AD-EABB-4570-B9B7-E3F8DDAA80F3}.Debug|x86.Build.0 = Debug|x86
|
||||
{644F40AD-EABB-4570-B9B7-E3F8DDAA80F3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{644F40AD-EABB-4570-B9B7-E3F8DDAA80F3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{644F40AD-EABB-4570-B9B7-E3F8DDAA80F3}.Release|ARM.ActiveCfg = Release|ARM
|
||||
{644F40AD-EABB-4570-B9B7-E3F8DDAA80F3}.Release|ARM.Build.0 = Release|ARM
|
||||
{644F40AD-EABB-4570-B9B7-E3F8DDAA80F3}.Release|x64.ActiveCfg = Release|x64
|
||||
{644F40AD-EABB-4570-B9B7-E3F8DDAA80F3}.Release|x64.Build.0 = Release|x64
|
||||
{644F40AD-EABB-4570-B9B7-E3F8DDAA80F3}.Release|x86.ActiveCfg = Release|x86
|
||||
{644F40AD-EABB-4570-B9B7-E3F8DDAA80F3}.Release|x86.Build.0 = Release|x86
|
||||
{37C63FDD-D995-4CC2-B014-4FB323194001}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{37C63FDD-D995-4CC2-B014-4FB323194001}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{37C63FDD-D995-4CC2-B014-4FB323194001}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||
{37C63FDD-D995-4CC2-B014-4FB323194001}.Debug|ARM.Build.0 = Debug|ARM
|
||||
{37C63FDD-D995-4CC2-B014-4FB323194001}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{37C63FDD-D995-4CC2-B014-4FB323194001}.Debug|x64.Build.0 = Debug|x64
|
||||
{37C63FDD-D995-4CC2-B014-4FB323194001}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{37C63FDD-D995-4CC2-B014-4FB323194001}.Debug|x86.Build.0 = Debug|x86
|
||||
{37C63FDD-D995-4CC2-B014-4FB323194001}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{37C63FDD-D995-4CC2-B014-4FB323194001}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{37C63FDD-D995-4CC2-B014-4FB323194001}.Release|ARM.ActiveCfg = Release|ARM
|
||||
{37C63FDD-D995-4CC2-B014-4FB323194001}.Release|ARM.Build.0 = Release|ARM
|
||||
{37C63FDD-D995-4CC2-B014-4FB323194001}.Release|x64.ActiveCfg = Release|x64
|
||||
{37C63FDD-D995-4CC2-B014-4FB323194001}.Release|x64.Build.0 = Release|x64
|
||||
{37C63FDD-D995-4CC2-B014-4FB323194001}.Release|x86.ActiveCfg = Release|x86
|
||||
{37C63FDD-D995-4CC2-B014-4FB323194001}.Release|x86.Build.0 = Release|x86
|
||||
{BE79FC52-6041-4913-B0D4-66C100944904}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{BE79FC52-6041-4913-B0D4-66C100944904}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{BE79FC52-6041-4913-B0D4-66C100944904}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||
{BE79FC52-6041-4913-B0D4-66C100944904}.Debug|ARM.Build.0 = Debug|ARM
|
||||
{BE79FC52-6041-4913-B0D4-66C100944904}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{BE79FC52-6041-4913-B0D4-66C100944904}.Debug|x64.Build.0 = Debug|x64
|
||||
{BE79FC52-6041-4913-B0D4-66C100944904}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{BE79FC52-6041-4913-B0D4-66C100944904}.Debug|x86.Build.0 = Debug|x86
|
||||
{BE79FC52-6041-4913-B0D4-66C100944904}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{BE79FC52-6041-4913-B0D4-66C100944904}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{BE79FC52-6041-4913-B0D4-66C100944904}.Release|ARM.ActiveCfg = Release|ARM
|
||||
{BE79FC52-6041-4913-B0D4-66C100944904}.Release|ARM.Build.0 = Release|ARM
|
||||
{BE79FC52-6041-4913-B0D4-66C100944904}.Release|x64.ActiveCfg = Release|x64
|
||||
{BE79FC52-6041-4913-B0D4-66C100944904}.Release|x64.Build.0 = Release|x64
|
||||
{BE79FC52-6041-4913-B0D4-66C100944904}.Release|x86.ActiveCfg = Release|x86
|
||||
{BE79FC52-6041-4913-B0D4-66C100944904}.Release|x86.Build.0 = Release|x86
|
||||
{AD1CBE3C-A68A-4E02-8001-E02B815560EE}.Debug|Any CPU.ActiveCfg = Debug|x86
|
||||
{AD1CBE3C-A68A-4E02-8001-E02B815560EE}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||
{AD1CBE3C-A68A-4E02-8001-E02B815560EE}.Debug|ARM.Build.0 = Debug|ARM
|
||||
{AD1CBE3C-A68A-4E02-8001-E02B815560EE}.Debug|ARM.Deploy.0 = Debug|ARM
|
||||
{AD1CBE3C-A68A-4E02-8001-E02B815560EE}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{AD1CBE3C-A68A-4E02-8001-E02B815560EE}.Debug|x64.Build.0 = Debug|x64
|
||||
{AD1CBE3C-A68A-4E02-8001-E02B815560EE}.Debug|x64.Deploy.0 = Debug|x64
|
||||
{AD1CBE3C-A68A-4E02-8001-E02B815560EE}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{AD1CBE3C-A68A-4E02-8001-E02B815560EE}.Debug|x86.Build.0 = Debug|x86
|
||||
{AD1CBE3C-A68A-4E02-8001-E02B815560EE}.Debug|x86.Deploy.0 = Debug|x86
|
||||
{AD1CBE3C-A68A-4E02-8001-E02B815560EE}.Release|Any CPU.ActiveCfg = Release|x86
|
||||
{AD1CBE3C-A68A-4E02-8001-E02B815560EE}.Release|ARM.ActiveCfg = Release|ARM
|
||||
{AD1CBE3C-A68A-4E02-8001-E02B815560EE}.Release|ARM.Build.0 = Release|ARM
|
||||
{AD1CBE3C-A68A-4E02-8001-E02B815560EE}.Release|ARM.Deploy.0 = Release|ARM
|
||||
{AD1CBE3C-A68A-4E02-8001-E02B815560EE}.Release|x64.ActiveCfg = Release|x64
|
||||
{AD1CBE3C-A68A-4E02-8001-E02B815560EE}.Release|x64.Build.0 = Release|x64
|
||||
{AD1CBE3C-A68A-4E02-8001-E02B815560EE}.Release|x64.Deploy.0 = Release|x64
|
||||
{AD1CBE3C-A68A-4E02-8001-E02B815560EE}.Release|x86.ActiveCfg = Release|x86
|
||||
{AD1CBE3C-A68A-4E02-8001-E02B815560EE}.Release|x86.Build.0 = Release|x86
|
||||
{AD1CBE3C-A68A-4E02-8001-E02B815560EE}.Release|x86.Deploy.0 = Release|x86
|
||||
{6F503DF9-71C9-4340-90BB-D9AA14ADB686}.Debug|Any CPU.ActiveCfg = Debug|x86
|
||||
{6F503DF9-71C9-4340-90BB-D9AA14ADB686}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||
{6F503DF9-71C9-4340-90BB-D9AA14ADB686}.Debug|ARM.Build.0 = Debug|ARM
|
||||
{6F503DF9-71C9-4340-90BB-D9AA14ADB686}.Debug|ARM.Deploy.0 = Debug|ARM
|
||||
{6F503DF9-71C9-4340-90BB-D9AA14ADB686}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{6F503DF9-71C9-4340-90BB-D9AA14ADB686}.Debug|x64.Build.0 = Debug|x64
|
||||
{6F503DF9-71C9-4340-90BB-D9AA14ADB686}.Debug|x64.Deploy.0 = Debug|x64
|
||||
{6F503DF9-71C9-4340-90BB-D9AA14ADB686}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{6F503DF9-71C9-4340-90BB-D9AA14ADB686}.Debug|x86.Build.0 = Debug|x86
|
||||
{6F503DF9-71C9-4340-90BB-D9AA14ADB686}.Debug|x86.Deploy.0 = Debug|x86
|
||||
{6F503DF9-71C9-4340-90BB-D9AA14ADB686}.Release|Any CPU.ActiveCfg = Release|x86
|
||||
{6F503DF9-71C9-4340-90BB-D9AA14ADB686}.Release|ARM.ActiveCfg = Release|ARM
|
||||
{6F503DF9-71C9-4340-90BB-D9AA14ADB686}.Release|ARM.Build.0 = Release|ARM
|
||||
{6F503DF9-71C9-4340-90BB-D9AA14ADB686}.Release|ARM.Deploy.0 = Release|ARM
|
||||
{6F503DF9-71C9-4340-90BB-D9AA14ADB686}.Release|x64.ActiveCfg = Release|x64
|
||||
{6F503DF9-71C9-4340-90BB-D9AA14ADB686}.Release|x64.Build.0 = Release|x64
|
||||
{6F503DF9-71C9-4340-90BB-D9AA14ADB686}.Release|x64.Deploy.0 = Release|x64
|
||||
{6F503DF9-71C9-4340-90BB-D9AA14ADB686}.Release|x86.ActiveCfg = Release|x86
|
||||
{6F503DF9-71C9-4340-90BB-D9AA14ADB686}.Release|x86.Build.0 = Release|x86
|
||||
{6F503DF9-71C9-4340-90BB-D9AA14ADB686}.Release|x86.Deploy.0 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{BE79FC52-6041-4913-B0D4-66C100944904} = {92EE95F6-40B6-4E5A-97C9-E4234D17C022}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,10 @@
|
|||
<common:BootStrapper x:Class="BluetoothLEExplorer.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:common="using:Template10.Common">
|
||||
|
||||
<Application.Resources>
|
||||
<ResourceDictionary Source="Styles\Custom.xaml" />
|
||||
</Application.Resources>
|
||||
|
||||
</common:BootStrapper>
|
|
@ -0,0 +1,153 @@
|
|||
// <copyright file="App.xaml.cs" company="Microsoft Corporation">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//----------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using BluetoothLEExplorer.Services.SettingsServices;
|
||||
using Template10.Controls;
|
||||
using Windows.ApplicationModel.Activation;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Data;
|
||||
using Windows.UI.Popups;
|
||||
using BluetoothLEExplorer.Models;
|
||||
using BluetoothLEExplorer.ViewModels;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace BluetoothLEExplorer
|
||||
{
|
||||
//// Documentation on APIs used in this page:
|
||||
//// https://github.com/Windows-XAML/Template10/wiki
|
||||
|
||||
/// <summary>
|
||||
/// The application
|
||||
/// </summary>
|
||||
[Bindable]
|
||||
public sealed partial class App : Template10.Common.BootStrapper
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="App" /> class.
|
||||
/// </summary>
|
||||
public App()
|
||||
{
|
||||
InitializeComponent();
|
||||
this.UnhandledException += App_UnhandledException;
|
||||
|
||||
this.Suspending += App_Suspending;
|
||||
this.Resuming += App_Resuming;
|
||||
|
||||
SplashFactory = (e) => new Views.Splash(e);
|
||||
|
||||
#region App settings
|
||||
|
||||
var settings = SettingsService.Instance;
|
||||
RequestedTheme = settings.AppTheme;
|
||||
CacheMaxDuration = settings.CacheMaxDuration;
|
||||
ShowShellBackButton = settings.UseShellBackButton;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
private void App_Suspending(object sender, Windows.ApplicationModel.SuspendingEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
var deferral = e.SuspendingOperation.GetDeferral();
|
||||
|
||||
foreach(GenericGattServiceViewModel service in GattSampleContext.Context.CreatedServices)
|
||||
{
|
||||
string key = "Service_"+ service.Service.ServiceProvider.Service.Uuid.ToString() + "_IsPublishing";
|
||||
bool value = service.IsPublishing;
|
||||
SettingsService.Instance.SettingsDictionary[key] = value;
|
||||
|
||||
if (service.IsPublishing)
|
||||
{
|
||||
service.Service.ServiceProvider.StopAdvertising();
|
||||
}
|
||||
}
|
||||
|
||||
deferral.Complete();
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
Debug.WriteLine("Suspending: " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private void App_Resuming(object sender, object e)
|
||||
{
|
||||
string[] keys = new string[SettingsService.Instance.SettingsDictionary.Keys.Count];
|
||||
|
||||
SettingsService.Instance.SettingsDictionary.Keys.CopyTo(keys, 0);
|
||||
|
||||
for (int i = 0; i < keys.Length; i++)
|
||||
{
|
||||
if (keys[i].Contains("Service_"))
|
||||
{
|
||||
string serviceUUID = keys[i].Split('_')[1];
|
||||
bool IsPublishing = (bool)SettingsService.Instance.SettingsDictionary[keys[i]];
|
||||
|
||||
if (IsPublishing)
|
||||
{
|
||||
foreach (GenericGattServiceViewModel service in GattSampleContext.Context.CreatedServices)
|
||||
{
|
||||
if (serviceUUID == service.Service.ServiceProvider.Service.Uuid.ToString())
|
||||
{
|
||||
service.Service.Start(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void App_UnhandledException(object sender, UnhandledExceptionEventArgs e)
|
||||
{
|
||||
showDialog(e.Exception.Message + "\n\n" + e.Exception.StackTrace);
|
||||
}
|
||||
|
||||
private async void showDialog(string content)
|
||||
{
|
||||
MessageDialog dialog = new MessageDialog(content, "Fatal Error");
|
||||
await dialog.ShowAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Application initialization
|
||||
/// </summary>
|
||||
/// <param name="args"></param>
|
||||
/// <returns>On initialization task</returns>
|
||||
public override async Task OnInitializeAsync(IActivatedEventArgs args)
|
||||
{
|
||||
if (Window.Current.Content as ModalDialog == null)
|
||||
{
|
||||
// create a new frame
|
||||
var nav = NavigationServiceFactory(BackButton.Attach, ExistingContent.Include);
|
||||
|
||||
// create modal root
|
||||
Window.Current.Content = new ModalDialog
|
||||
{
|
||||
DisableBackButtonWhenModal = true,
|
||||
Content = new Views.Shell(nav),
|
||||
ModalContent = new Views.Busy(),
|
||||
};
|
||||
}
|
||||
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// App initialization for long running tasks
|
||||
/// </summary>
|
||||
/// <param name="startKind"></param>
|
||||
/// <param name="args"></param>
|
||||
/// <returns>On start task</returns>
|
||||
public override async Task OnStartAsync(StartKind startKind, IActivatedEventArgs args)
|
||||
{
|
||||
//// long-running startup tasks go here
|
||||
|
||||
NavigationService.Navigate(typeof(Views.Discover));
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
После Ширина: | Высота: | Размер: 627 B |
После Ширина: | Высота: | Размер: 802 B |
После Ширина: | Высота: | Размер: 912 B |
После Ширина: | Высота: | Размер: 1.2 KiB |
После Ширина: | Высота: | Размер: 3.2 KiB |
После Ширина: | Высота: | Размер: 71 KiB |
Двоичные данные
BluetoothLEExplorer/BluetoothLEExplorer/Assets/SplashScreen.scale-200.png
Normal file
После Ширина: | Высота: | Размер: 185 KiB |
Двоичные данные
BluetoothLEExplorer/BluetoothLEExplorer/Assets/Square150x150Logo.scale-100.png
Normal file
После Ширина: | Высота: | Размер: 18 KiB |
Двоичные данные
BluetoothLEExplorer/BluetoothLEExplorer/Assets/Square150x150Logo.scale-200.png
Normal file
После Ширина: | Высота: | Размер: 62 KiB |
Двоичные данные
BluetoothLEExplorer/BluetoothLEExplorer/Assets/Square150x150Logo.scale-400.png
Normal file
После Ширина: | Высота: | Размер: 218 KiB |
Двоичные данные
BluetoothLEExplorer/BluetoothLEExplorer/Assets/Square310x310Logo.scale-100.png
Normal file
После Ширина: | Высота: | Размер: 11 KiB |
Двоичные данные
BluetoothLEExplorer/BluetoothLEExplorer/Assets/Square310x310Logo.scale-200.png
Normal file
После Ширина: | Высота: | Размер: 31 KiB |
Двоичные данные
BluetoothLEExplorer/BluetoothLEExplorer/Assets/Square310x310Logo.scale-400.png
Normal file
После Ширина: | Высота: | Размер: 88 KiB |
Двоичные данные
BluetoothLEExplorer/BluetoothLEExplorer/Assets/Square44x44Logo.scale-100.png
Normal file
После Ширина: | Высота: | Размер: 2.7 KiB |
Двоичные данные
BluetoothLEExplorer/BluetoothLEExplorer/Assets/Square44x44Logo.scale-200.png
Normal file
После Ширина: | Высота: | Размер: 7.6 KiB |
Двоичные данные
BluetoothLEExplorer/BluetoothLEExplorer/Assets/Square44x44Logo.scale-400.png
Normal file
После Ширина: | Высота: | Размер: 24 KiB |
Двоичные данные
BluetoothLEExplorer/BluetoothLEExplorer/Assets/Square44x44Logo.targetsize-24_altform-unplated.png
Normal file
После Ширина: | Высота: | Размер: 1.2 KiB |
Двоичные данные
BluetoothLEExplorer/BluetoothLEExplorer/Assets/Square71x71Logo.scale-100.png
Normal file
После Ширина: | Высота: | Размер: 5.5 KiB |
Двоичные данные
BluetoothLEExplorer/BluetoothLEExplorer/Assets/Square71x71Logo.scale-200.png
Normal file
После Ширина: | Высота: | Размер: 17 KiB |
Двоичные данные
BluetoothLEExplorer/BluetoothLEExplorer/Assets/Square71x71Logo.scale-400.png
Normal file
После Ширина: | Высота: | Размер: 57 KiB |
После Ширина: | Высота: | Размер: 4.3 KiB |
После Ширина: | Высота: | Размер: 5.2 KiB |
После Ширина: | Высота: | Размер: 7.5 KiB |
После Ширина: | Высота: | Размер: 25 KiB |
После Ширина: | Высота: | Размер: 3.2 KiB |
Двоичные данные
BluetoothLEExplorer/BluetoothLEExplorer/Assets/Wide310x150Logo.scale-100.png
Normal file
После Ширина: | Высота: | Размер: 24 KiB |
Двоичные данные
BluetoothLEExplorer/BluetoothLEExplorer/Assets/Wide310x150Logo.scale-200.png
Normal file
После Ширина: | Высота: | Размер: 73 KiB |
Двоичные данные
BluetoothLEExplorer/BluetoothLEExplorer/Assets/Wide310x150Logo.scale-400.png
Normal file
После Ширина: | Высота: | Размер: 238 KiB |
|
@ -0,0 +1,317 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
|
||||
<ProjectGuid>{AD1CBE3C-A68A-4E02-8001-E02B815560EE}</ProjectGuid>
|
||||
<OutputType>AppContainerExe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>BluetoothLEExplorer</RootNamespace>
|
||||
<AssemblyName>BluetoothLEExplorer</AssemblyName>
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
|
||||
<TargetPlatformVersion>10.0.15063.0</TargetPlatformVersion>
|
||||
<TargetPlatformMinVersion>10.0.15063.0</TargetPlatformMinVersion>
|
||||
<MinimumVisualStudioVersion>14</MinimumVisualStudioVersion>
|
||||
<EnableDotNetNativeCompatibleProfile>true</EnableDotNetNativeCompatibleProfile>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<PackageCertificateKeyFile>BluetoothExplorer_StoreKey.pfx</PackageCertificateKeyFile>
|
||||
<AppxAutoIncrementPackageRevision>True</AppxAutoIncrementPackageRevision>
|
||||
<AppxBundle>Always</AppxBundle>
|
||||
<AppxBundlePlatforms>x86|x64|arm</AppxBundlePlatforms>
|
||||
<PackageCertificateThumbprint>DCBC2F4C73DE671847F017A13666994B37C0EC04</PackageCertificateThumbprint>
|
||||
<RuntimeIdentifiers>win10-arm;win10-arm-aot;win10-x86;win10-x86-aot;win10-x64;win10-x64-aot</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\ARM\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>ARM</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|ARM'">
|
||||
<OutputPath>bin\ARM\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>ARM</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x64\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
<OutputPath>bin\x64\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x86\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||
<OutputPath>bin\x86\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="App.xaml.cs">
|
||||
<DependentUpon>App.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="CustomControls\GattLocalCharacteristicControl.xaml.cs">
|
||||
<DependentUpon>GattLocalCharacteristicControl.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Models\GattSampleContext.cs" />
|
||||
<Compile Include="Models\ObservableBluetoothLEDevice.cs" />
|
||||
<Compile Include="Models\ObservableGattCharacteristics.cs" />
|
||||
<Compile Include="Models\ObservableGattDeviceService.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Services\Converters\Converters.cs" />
|
||||
<Compile Include="Services\DispatcherService\DispatcherService.cs" />
|
||||
<Compile Include="Services\GattUuidsService\GattUuidsService.cs" />
|
||||
<Compile Include="Services\Other\BytePadder.cs" />
|
||||
<Compile Include="Services\Other\GattProtocolErrorParser.cs" />
|
||||
<Compile Include="Services\SettingsServices\SettingsService.cs" />
|
||||
<Compile Include="Services\ToastService\ToastService.cs" />
|
||||
<Compile Include="ViewModels\Services\BloodPressureServicePageViewModel.cs" />
|
||||
<Compile Include="ViewModels\Services\HeartRateServicePageViewModel.cs" />
|
||||
<Compile Include="ViewModels\ShellViewModel.cs" />
|
||||
<Compile Include="ViewModels\Services\BatteryServicePageViewModel.cs" />
|
||||
<Compile Include="ViewModels\BeaconViewModel.cs" />
|
||||
<Compile Include="ViewModels\CharacteristicPageViewModel.cs" />
|
||||
<Compile Include="ViewModels\DeviceServicesPageViewModel.cs" />
|
||||
<Compile Include="ViewModels\DiscoverViewModel.cs" />
|
||||
<Compile Include="ViewModels\SettingsPageViewModel.cs" />
|
||||
<Compile Include="ViewModels\Services\MicrosoftServicePageViewModel.cs" />
|
||||
<Compile Include="ViewModels\GenericGattServiceViewModel.cs" />
|
||||
<Compile Include="ViewModels\GenericGattCharacteristicViewModel.cs" />
|
||||
<Compile Include="ViewModels\VirtualPeripheralPageViewModel.cs" />
|
||||
<Compile Include="Views\Services\BloodPressureServicePage.xaml.cs">
|
||||
<DependentUpon>BloodPressureServicePage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\Services\HeartRateServicePage.xaml.cs">
|
||||
<DependentUpon>HeartRateServicePage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\Services\MicrosoftServicePage.xaml.cs">
|
||||
<DependentUpon>MicrosoftServicePage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\Services\BatteryServicePage.xaml.cs">
|
||||
<DependentUpon>BatteryServicePage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\Beacon.xaml.cs">
|
||||
<DependentUpon>Beacon.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\Busy.xaml.cs">
|
||||
<DependentUpon>Busy.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\CharacteristicPage.xaml.cs">
|
||||
<DependentUpon>CharacteristicPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\DeviceServicesPage.xaml.cs">
|
||||
<DependentUpon>DeviceServicesPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\Discover.xaml.cs">
|
||||
<DependentUpon>Discover.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\SettingsPage.xaml.cs">
|
||||
<DependentUpon>SettingsPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\Shell.xaml.cs">
|
||||
<DependentUpon>Shell.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\Splash.xaml.cs">
|
||||
<DependentUpon>Splash.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Views\VirtualPeripheralPage.xaml.cs">
|
||||
<DependentUpon>VirtualPeripheralPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AppxManifest Include="Package.appxmanifest">
|
||||
<SubType>Designer</SubType>
|
||||
</AppxManifest>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Assets\BadgeLogo.scale-100.png" />
|
||||
<Content Include="Assets\BadgeLogo.scale-125.png" />
|
||||
<Content Include="Assets\BadgeLogo.scale-150.png" />
|
||||
<Content Include="Assets\BadgeLogo.scale-200.png" />
|
||||
<Content Include="Assets\BadgeLogo.scale-400.png" />
|
||||
<Content Include="Assets\Square150x150Logo.scale-100.png" />
|
||||
<Content Include="Assets\Square310x310Logo.scale-100.png" />
|
||||
<Content Include="Assets\Square310x310Logo.scale-200.png" />
|
||||
<Content Include="Assets\Square310x310Logo.scale-400.png" />
|
||||
<Content Include="Assets\Square44x44Logo.scale-100.png" />
|
||||
<Content Include="Assets\Square44x44Logo.scale-400.png" />
|
||||
<Content Include="Assets\Square71x71Logo.scale-100.png" />
|
||||
<Content Include="Assets\Square71x71Logo.scale-200.png" />
|
||||
<Content Include="Assets\Square71x71Logo.scale-400.png" />
|
||||
<Content Include="Assets\Wide310x150Logo.scale-100.png" />
|
||||
<Content Include="Help.htm" />
|
||||
<None Include="Package.StoreAssociation.xml" />
|
||||
<Content Include="Properties\Default.rd.xml" />
|
||||
<Content Include="Assets\SplashScreen.scale-200.png" />
|
||||
<Content Include="Assets\Square150x150Logo.scale-200.png" />
|
||||
<Content Include="Assets\Square44x44Logo.scale-200.png" />
|
||||
<Content Include="Assets\Square44x44Logo.targetsize-24_altform-unplated.png" />
|
||||
<Content Include="Assets\StoreLogo.png" />
|
||||
<Content Include="Assets\Wide310x150Logo.scale-200.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ApplicationDefinition Include="App.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</ApplicationDefinition>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Include="CustomControls\GattLocalCharacteristicControl.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Styles\Custom.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Page>
|
||||
<Page Include="Views\Services\BloodPressureServicePage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Views\Services\HeartRateServicePage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Views\Services\MicrosoftServicePage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Views\Services\BatteryServicePage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Views\Beacon.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Views\Busy.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Views\CharacteristicPage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Views\DeviceServicesPage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Views\Discover.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Views\SettingsPage.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\Shell.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\Splash.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Views\VirtualPeripheralPage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Services\Enums\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\GattServicesLibrary\GattServicesLibrary\GattServicesLibrary.csproj">
|
||||
<Project>{644f40ad-eabb-4570-b9b7-e3f8ddaa80f3}</Project>
|
||||
<Name>GattServicesLibrary</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\SortedObservableCollection\SortedObservableCollection\SortedObservableCollection.csproj">
|
||||
<Project>{be79fc52-6041-4913-b0d4-66c100944904}</Project>
|
||||
<Name>SortedObservableCollection</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\GattHelper\GattHelper.csproj">
|
||||
<Project>{37c63fdd-d995-4cc2-b014-4fb323194001}</Project>
|
||||
<Name>GattHelper</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
|
||||
<Version>5.3.3</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Xaml.Behaviors.Uwp.Managed">
|
||||
<Version>2.0.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Newtonsoft.Json">
|
||||
<Version>10.0.2</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="NuGet.CommandLine">
|
||||
<Version>3.5.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Template10">
|
||||
<Version>1.1.12</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' < '14.0' ">
|
||||
<VisualStudioVersion>14.0</VisualStudioVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PreBuildEvent>
|
||||
</PreBuildEvent>
|
||||
</PropertyGroup>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
|
@ -0,0 +1,49 @@
|
|||
<UserControl
|
||||
x:Class="BluetoothLEExplorer.CustomControls.GattLocalCharacteristicControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:BluetoothLEExplorer.CustomControls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
ScrollViewer.VerticalScrollBarVisibility="Disabled">
|
||||
|
||||
<UserControl.Resources>
|
||||
<!-- Green -->
|
||||
<Color x:Key="CheckBoxForegroundCheckedDisabled">Green</Color>
|
||||
<Color x:Key="CheckBoxCheckBackgroundStrokeCheckedDisabled">Green</Color>
|
||||
<Color x:Key="CheckBoxCheckGlyphForegroundCheckedDisabled">Green</Color>
|
||||
|
||||
<!-- Red -->
|
||||
<Color x:Key="CheckBoxForegroundUncheckedDisabled">Red</Color>
|
||||
<Color x:Key="CheckBoxCheckBackgroundStrokeUncheckedDisabled">Red</Color>
|
||||
<Color x:Key="CheckBoxCheckGlyphForegroundUncheckedDisabled">Red</Color>
|
||||
|
||||
</UserControl.Resources>
|
||||
|
||||
<StackPanel BorderThickness="2" BorderBrush="Black" Padding="5">
|
||||
<StackPanel Orientation="Horizontal" Padding="5" Visibility="{x:Bind ShowValue}">
|
||||
<TextBlock Text="Value" VerticalAlignment="Center" Margin="0,0,10,0"/>
|
||||
<TextBox x:Name="ValueText" Text="{x:Bind Value, Mode=TwoWay, FallbackValue='blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah blah '}"
|
||||
IsReadOnly="{x:Bind IsReadOnly}" MaxWidth="336" TextWrapping="Wrap" Width="336" PlaceholderText="" />
|
||||
<!--<TextBox x:Name="ValueText" Text="{x:Bind Value, Mode=OneWay }" IsReadOnly="True" MaxWidth="336" TextWrapping="Wrap" Width="336" />-->
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" Padding="5" >
|
||||
<TextBlock Text="User Description" VerticalAlignment="Center" Margin="0,0,10,0" />
|
||||
<TextBox x:Name="descText" Text="{x:Bind Characteristic.UserDescription, Mode=TwoWay}" IsReadOnly="True" Width="262"/>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Padding="5">
|
||||
<CheckBox Content="Read" IsChecked="{x:Bind Characteristic.HasReadDescriptor, Mode=TwoWay}" x:Name="cbRead" IsEnabled="False" />
|
||||
<CheckBox Content="Write" IsChecked="{x:Bind Characteristic.HasWriteDescriptor, Mode=TwoWay}" x:Name="cbWrite" IsEnabled="False"/>
|
||||
<CheckBox Content="WriteWithoutResponse" IsChecked="{x:Bind Characteristic.HasWriteWithoutResponseDescriptor, Mode=TwoWay}" x:Name="cbWriteWOResponds" IsEnabled="False" />
|
||||
<CheckBox Content="Notify" IsChecked="{x:Bind Characteristic.HasNotifyDescriptor, Mode=TwoWay}" x:Name="cbNotify" IsEnabled="False" />
|
||||
<CheckBox Content="Indicate" IsChecked="{x:Bind Characteristic.HasIndicateDescriptor, Mode=TwoWay}" x:Name="cbIndicate" IsEnabled="False" />
|
||||
</StackPanel>
|
||||
|
||||
<Button Content="{x:Bind NotifyButtonText}" Click="{x:Bind Characteristic.Characteristic.NotifyValue}" Visibility="{x:Bind NotifyButtonVisibility}" Margin="0,0,5,0"/>
|
||||
<!--<Button Content="Indicate" Click="{x:Bind Characteristic.Characteristic.NotifyValue}" Margin="5,0,0,0"/>-->
|
||||
|
||||
</StackPanel>
|
||||
|
||||
</UserControl>
|
|
@ -0,0 +1,242 @@
|
|||
// <copyright file="GattLocalCharacteristicControl.xaml.cs" company="Microsoft Corporation">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//----------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Text;
|
||||
using BluetoothLEExplorer.ViewModels;
|
||||
using Windows.ApplicationModel.Core;
|
||||
using Windows.UI.Core;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.Storage.Streams;
|
||||
using Windows.Devices.Bluetooth.GenericAttributeProfile;
|
||||
using Windows.Security.Cryptography;
|
||||
using GattHelper.Converters;
|
||||
using Windows.UI.Popups;
|
||||
|
||||
//// The User Control item template is documented at http://go.microsoft.com/fwlink/?LinkId=234236
|
||||
|
||||
namespace BluetoothLEExplorer.CustomControls
|
||||
{
|
||||
/// <summary>
|
||||
/// UserControl to display a Characteristic
|
||||
/// </summary>
|
||||
public sealed partial class GattLocalCharacteristicControl : UserControl, INotifyPropertyChanged
|
||||
{
|
||||
/// <summary>
|
||||
/// Callback when characteristic changes
|
||||
/// </summary>
|
||||
/// <param name="d"></param>
|
||||
/// <param name="e"></param>
|
||||
private static void OnCharacteristicPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
GattLocalCharacteristicControl control = d as GattLocalCharacteristicControl;
|
||||
control.Characteristic = e.NewValue as GenericGattCharacteristicViewModel;
|
||||
control.Value = GattServicesLibrary.Helpers.ValueConverter.ConvertGattCharacteristicValueToString(control.Characteristic.Characteristic);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GattLocalCharacteristicControl" /> class.
|
||||
/// </summary>
|
||||
public GattLocalCharacteristicControl()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes when the characteristic changes
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private async void Characteristic_PropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(
|
||||
CoreDispatcherPriority.Normal,
|
||||
() =>
|
||||
{
|
||||
if (e.PropertyName == "Value")
|
||||
{
|
||||
Value = GattServicesLibrary.Helpers.ValueConverter.ConvertGattCharacteristicValueToString(Characteristic.Characteristic);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source for the <see cref="Value"/> property
|
||||
/// </summary>
|
||||
private string value;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value of this characteristic
|
||||
/// </summary>
|
||||
public string Value
|
||||
{
|
||||
get
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (value != this.value)
|
||||
{
|
||||
// If this is a read only control then no need to send it back to the base characteristic
|
||||
if (this.IsReadOnly)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
Characteristic.SetValueFromString(value);
|
||||
this.value = value;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ErrorDialog(ex.Message);
|
||||
}
|
||||
}
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("Value"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async void ErrorDialog(string message)
|
||||
{
|
||||
MessageDialog err = new MessageDialog(message, "Error");
|
||||
await err.ShowAsync();
|
||||
return;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the visibility of the the notify button
|
||||
/// </summary>
|
||||
private Visibility NotifyButtonVisibility
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Characteristic.HasNotifyDescriptor == true || Characteristic.HasIndicateDescriptor == true)
|
||||
{
|
||||
return Visibility.Visible;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Visibility.Collapsed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the text for the notify button
|
||||
/// </summary>
|
||||
private string NotifyButtonText
|
||||
{
|
||||
get
|
||||
{
|
||||
StringBuilder ret = new StringBuilder();
|
||||
if (Characteristic.HasNotifyDescriptor)
|
||||
{
|
||||
ret.Append("Notify");
|
||||
if (Characteristic.HasIndicateDescriptor)
|
||||
{
|
||||
ret.Append("/Indicate");
|
||||
}
|
||||
}
|
||||
else if (Characteristic.HasIndicateDescriptor)
|
||||
{
|
||||
ret.Append("Indicate");
|
||||
}
|
||||
|
||||
return ret.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the characteristic this control wraps
|
||||
/// </summary>
|
||||
public GenericGattCharacteristicViewModel Characteristic
|
||||
{
|
||||
get
|
||||
{
|
||||
return (GenericGattCharacteristicViewModel)GetValue(CharacteristicProperty);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
SetValue(CharacteristicProperty, value);
|
||||
|
||||
Characteristic.Characteristic.PropertyChanged += Characteristic_PropertyChanged;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Using a DependencyProperty as the backing store for Characteristic. This enables animation, styling, binding, etc...
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty CharacteristicProperty =
|
||||
DependencyProperty.Register(
|
||||
"Characteristic",
|
||||
typeof(GenericGattCharacteristicViewModel),
|
||||
typeof(GattLocalCharacteristicControl),
|
||||
new PropertyMetadata(
|
||||
null,
|
||||
new PropertyChangedCallback(OnCharacteristicPropertyChanged)));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the visibility
|
||||
/// </summary>
|
||||
public Visibility ShowValue
|
||||
{
|
||||
get { return (Visibility)GetValue(ShowValueProperty); }
|
||||
set { SetValue(ShowValueProperty, Visibility); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Using a DependencyProperty as the backing store for ShowValue. This enables animation, styling, binding, etc...
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty ShowValueProperty =
|
||||
DependencyProperty.Register("ShowValue", typeof(Visibility), typeof(GattLocalCharacteristicControl), new PropertyMetadata(Visibility.Visible));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the readonly field of the value text box
|
||||
/// </summary>
|
||||
public bool IsReadOnly
|
||||
{
|
||||
get
|
||||
{
|
||||
return (bool)GetValue(IsReadOnlyProperty);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
SetValue(IsReadOnlyProperty, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Using a DependencyProperty as the backing store for IsReadOnly
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty IsReadOnlyProperty =
|
||||
DependencyProperty.Register("IsReadOnly", typeof(bool), typeof(GattLocalCharacteristicControl), new PropertyMetadata(true));
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Event to notify when this object has changed
|
||||
/// </summary>
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Property changed method
|
||||
/// </summary>
|
||||
/// <param name="e">Property that changed</param>
|
||||
private void OnPropertyChanged(PropertyChangedEventArgs e)
|
||||
{
|
||||
if (PropertyChanged != null)
|
||||
{
|
||||
PropertyChanged(this, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Template 10</title>
|
||||
</head>
|
||||
<body style="margin: 0; padding: 0;">
|
||||
<meta http-equiv="refresh" content="2;url=http://aka.ms/T10-Hamburger">
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,747 @@
|
|||
// <copyright file="GattSampleContext.cs" company="Microsoft Corporation">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//----------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BluetoothLEExplorer.ViewModels;
|
||||
using Windows.Devices.Bluetooth.Advertisement;
|
||||
using Windows.Devices.Enumeration;
|
||||
using Windows.UI.Popups;
|
||||
using Windows.Foundation.Metadata;
|
||||
using System.Threading;
|
||||
|
||||
namespace BluetoothLEExplorer.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Context for the entire app. This is where all app wide variables are stored
|
||||
/// </summary>
|
||||
public class GattSampleContext : INotifyPropertyChanged
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the app context
|
||||
/// </summary>
|
||||
public static GattSampleContext Context { get; private set; } = new GattSampleContext();
|
||||
|
||||
/// <summary>
|
||||
/// AQS search string used to find bluetooth devices
|
||||
/// </summary>
|
||||
private const string BTLEDeviceWatcherAQSString = "(System.Devices.Aep.ProtocolId:=\"{bb7bb05e-5972-42b5-94fc-76eaa7084d49}\")";
|
||||
|
||||
/// <summary>
|
||||
/// AQS search string to find bluetooth device dev nodes
|
||||
/// </summary>
|
||||
private const string DevNodeBTLEDeviceWatcherAQSString = "(System.Devices.ClassGuid:=\"{e0cbf06c-cd8b-4647-bb8a-263b43f0f974}\")";
|
||||
|
||||
/// <summary>
|
||||
/// Device dev node property to get battery level
|
||||
/// </summary>
|
||||
private const string BatteryLevelGUID = "{995EF0B0-7EB3-4A8B-B9CE-068BB3F4AF69} 10";
|
||||
|
||||
/// <summary>
|
||||
/// Device dev node property to get device address
|
||||
/// </summary>
|
||||
private const string BluetoothDeviceAddress = "System.DeviceInterface.Bluetooth.DeviceAddress";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of available bluetooth devices
|
||||
/// </summary>
|
||||
public ObservableCollection<ObservableBluetoothLEDevice> BluetoothLEDevices { get; set; } = new ObservableCollection<ObservableBluetoothLEDevice>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the selected bluetooth device
|
||||
/// </summary>
|
||||
public ObservableBluetoothLEDevice SelectedBluetoothLEDevice { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the selected characteristic
|
||||
/// </summary>
|
||||
public ObservableGattCharacteristics SelectedCharacteristic { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Lock around the <see cref="BluetoothLEDevices"/>. Used in the Add/Removed/Updated callbacks
|
||||
/// </summary>
|
||||
private SemaphoreSlim BluetoothLEDevicesLock = new SemaphoreSlim(1, 1);
|
||||
|
||||
/// <summary>
|
||||
/// Lock around the <see cref="devNodes"/>. Used in the Add/Removed/Updated callbacks
|
||||
/// </summary>
|
||||
private SemaphoreSlim DevNodeLock = new SemaphoreSlim(1, 1);
|
||||
|
||||
/// <summary>
|
||||
/// Device watcher used to find bluetooth devices
|
||||
/// </summary>
|
||||
private DeviceWatcher deviceWatcher;
|
||||
|
||||
/// <summary>
|
||||
/// Device watcher used to find bluetooth device dev nodes
|
||||
/// </summary>
|
||||
private DeviceWatcher devNodeWatcher;
|
||||
|
||||
/// <summary>
|
||||
/// Advertisement watcher used to find bluetooth devices
|
||||
/// </summary>
|
||||
private BluetoothLEAdvertisementWatcher advertisementWatcher;
|
||||
|
||||
/// <summary>
|
||||
/// We need to cache all DeviceInformation objects we get as they may
|
||||
/// get updated in the future. The update may make them eligible to be put on
|
||||
/// the displayed list.
|
||||
/// </summary>
|
||||
private List<DeviceInformation> unusedDevices = new List<DeviceInformation>();
|
||||
|
||||
/// <summary>
|
||||
/// All the bluetooth dev nodes on the system
|
||||
/// </summary>
|
||||
private List<DeviceInformation> devNodes = new List<DeviceInformation>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of created service
|
||||
/// </summary>
|
||||
public ObservableCollection<GenericGattServiceViewModel> CreatedServices { get; set; } = new ObservableCollection<GenericGattServiceViewModel>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the currently selected gatt server service
|
||||
/// </summary>
|
||||
public GenericGattServiceViewModel SelectedGattServerService { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Event to notify when this object has changed
|
||||
/// </summary>
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="IsEnumerating"/> property
|
||||
/// </summary>
|
||||
private bool isEnumerating = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether app is currently enumerating
|
||||
/// </summary>
|
||||
public bool IsEnumerating
|
||||
{
|
||||
get
|
||||
{
|
||||
return isEnumerating;
|
||||
}
|
||||
|
||||
private set
|
||||
{
|
||||
if (isEnumerating != value)
|
||||
{
|
||||
isEnumerating = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("IsEnumerating"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="EnumerationFinished"/> property
|
||||
/// </summary>
|
||||
private bool enumorationFinished = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the app is finished enumerating
|
||||
/// </summary>
|
||||
public bool EnumerationFinished
|
||||
{
|
||||
get
|
||||
{
|
||||
return enumorationFinished;
|
||||
}
|
||||
|
||||
private set
|
||||
{
|
||||
if (enumorationFinished != value)
|
||||
{
|
||||
enumorationFinished = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("EnumerationFinished"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="IsPeripheralRoleSupported"/>
|
||||
/// </summary>
|
||||
private bool isPeripheralRoleSupported = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether peripheral mode is supported by this device
|
||||
/// </summary>
|
||||
public bool IsPeripheralRoleSupported
|
||||
{
|
||||
get
|
||||
{
|
||||
return isPeripheralRoleSupported;
|
||||
}
|
||||
|
||||
private set
|
||||
{
|
||||
if (isPeripheralRoleSupported != value)
|
||||
{
|
||||
isPeripheralRoleSupported = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("IsPeripheralRoleSupported"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="IsCentralRoleSupported"/>
|
||||
/// </summary>
|
||||
private bool isCentralRoleSupported = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether central role is supported by this device
|
||||
/// </summary>
|
||||
public bool IsCentralRoleSupported
|
||||
{
|
||||
get
|
||||
{
|
||||
return isCentralRoleSupported;
|
||||
}
|
||||
|
||||
private set
|
||||
{
|
||||
if (isCentralRoleSupported != value)
|
||||
{
|
||||
isCentralRoleSupported = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("IsCentralRoleSupported"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prevents a default instance of the <see cref="GattSampleContext" /> class from being created.
|
||||
/// </summary>
|
||||
private GattSampleContext()
|
||||
{
|
||||
Init();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the app context
|
||||
/// </summary>
|
||||
private async void Init()
|
||||
{
|
||||
Windows.Devices.Bluetooth.BluetoothAdapter adapter = await Windows.Devices.Bluetooth.BluetoothAdapter.GetDefaultAsync();
|
||||
|
||||
if(adapter == null)
|
||||
{
|
||||
MessageDialog msg = new MessageDialog("Error getting access to Bluetooth adaptor. Do you have a have bluetooth enabled?", "Error");
|
||||
await msg.ShowAsync();
|
||||
|
||||
IsPeripheralRoleSupported = false;
|
||||
IsCentralRoleSupported = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
IsPeripheralRoleSupported = adapter.IsPeripheralRoleSupported;
|
||||
IsCentralRoleSupported = adapter.IsCentralRoleSupported;
|
||||
}
|
||||
|
||||
// Start the dev node watcher
|
||||
string[] requestedProperties =
|
||||
{
|
||||
BatteryLevelGUID,
|
||||
BluetoothDeviceAddress
|
||||
};
|
||||
|
||||
devNodeWatcher =
|
||||
DeviceInformation.CreateWatcher(
|
||||
DevNodeBTLEDeviceWatcherAQSString,
|
||||
requestedProperties,
|
||||
DeviceInformationKind.Device);
|
||||
|
||||
devNodeWatcher.Added += DevNodeWatcher_Added;
|
||||
devNodeWatcher.Removed += DevNodeWatcher_Removed;
|
||||
devNodeWatcher.Updated += DevNodeWatcher_Updated;
|
||||
|
||||
devNodeWatcher.Start();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private async void DevNodeWatcher_Added(DeviceWatcher sender, DeviceInformation args)
|
||||
{
|
||||
try
|
||||
{
|
||||
await DevNodeLock.WaitAsync();
|
||||
devNodes.Add(args);
|
||||
}
|
||||
finally
|
||||
{
|
||||
DevNodeLock.Release();
|
||||
}
|
||||
|
||||
await UpdateBatteryLevel(args);
|
||||
}
|
||||
|
||||
private async void DevNodeWatcher_Removed(DeviceWatcher sender, DeviceInformationUpdate args)
|
||||
{
|
||||
try
|
||||
{
|
||||
await DevNodeLock.WaitAsync();
|
||||
|
||||
int index = devNodes.FindIndex(x => x.Id == args.Id);
|
||||
devNodes.RemoveAt(index);
|
||||
}
|
||||
finally
|
||||
{
|
||||
DevNodeLock.Release();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private async void DevNodeWatcher_Updated(DeviceWatcher sender, DeviceInformationUpdate args)
|
||||
{
|
||||
DeviceInformation dev = null;
|
||||
try
|
||||
{
|
||||
await DevNodeLock.WaitAsync();
|
||||
dev = devNodes.FirstOrDefault(x => x.Id == args.Id);
|
||||
}
|
||||
finally
|
||||
{
|
||||
DevNodeLock.Release();
|
||||
}
|
||||
|
||||
if(dev != null)
|
||||
{
|
||||
dev.Update(args);
|
||||
await UpdateBatteryLevel(dev);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the battery level of a ObservableBluetoothLEDevice based on DeviceInfo object
|
||||
/// </summary>
|
||||
/// <param name="dev">DeviceInformation object</param>
|
||||
/// <returns></returns>
|
||||
private async Task UpdateBatteryLevel(DeviceInformation dev)
|
||||
{
|
||||
if (dev.Properties.Keys.Contains(BatteryLevelGUID) &&
|
||||
dev.Properties[BatteryLevelGUID] != null &&
|
||||
dev.Properties.Keys.Contains(BluetoothDeviceAddress) &&
|
||||
dev.Properties[BluetoothDeviceAddress] != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
await BluetoothLEDevicesLock.WaitAsync();
|
||||
|
||||
foreach (ObservableBluetoothLEDevice device in BluetoothLEDevices)
|
||||
{
|
||||
string addr = GetDelimitedAddr((string)dev.Properties[BluetoothDeviceAddress]);
|
||||
if (device.BluetoothAddressAsString == addr)
|
||||
{
|
||||
await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(
|
||||
Windows.UI.Core.CoreDispatcherPriority.Normal,
|
||||
() =>
|
||||
{
|
||||
device.BatteryLevel = Convert.ToInt32((byte)dev.Properties[BatteryLevelGUID]);
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
BluetoothLEDevicesLock.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the battery level of a ObservableBluetoothLEDevice by searching through known dev nodes
|
||||
/// </summary>
|
||||
/// <param name="dev">device to update</param>
|
||||
/// <returns></returns>
|
||||
private async Task UpdateBatteryLevel(ObservableBluetoothLEDevice dev)
|
||||
{
|
||||
foreach(DeviceInformation devNode in devNodes)
|
||||
{
|
||||
string addr = dev.BluetoothAddressAsString.Replace(":", String.Empty);
|
||||
|
||||
if (devNode.Properties.Keys.Contains(BatteryLevelGUID) &&
|
||||
devNode.Properties[BatteryLevelGUID] != null &&
|
||||
devNode.Properties.Keys.Contains(BluetoothDeviceAddress) &&
|
||||
devNode.Properties[BluetoothDeviceAddress] != null)
|
||||
{
|
||||
if ((string)devNode.Properties[BluetoothDeviceAddress] == addr)
|
||||
{
|
||||
await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(
|
||||
Windows.UI.Core.CoreDispatcherPriority.Normal,
|
||||
() =>
|
||||
{
|
||||
dev.BatteryLevel = Convert.ToInt32((byte)devNode.Properties[BatteryLevelGUID]);
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add : to bluetooth address without them
|
||||
/// </summary>
|
||||
/// <param name="addr"></param>
|
||||
/// <returns></returns>
|
||||
private string GetDelimitedAddr(string addr)
|
||||
{
|
||||
// Add : delimiters to raw address
|
||||
var list = Enumerable.Range(0, addr.Length / 2).Select(i => addr.Substring(i * 2, 2)).ToList();
|
||||
return string.Join(":", list);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts enumeration of bluetooth device
|
||||
/// </summary>
|
||||
public void StartEnumeration()
|
||||
{
|
||||
// Additional properties we would like about the device.
|
||||
string[] requestedProperties =
|
||||
{
|
||||
"System.Devices.Aep.Category",
|
||||
"System.Devices.Aep.ContainerId",
|
||||
"System.Devices.Aep.DeviceAddress",
|
||||
"System.Devices.Aep.IsConnected",
|
||||
"System.Devices.Aep.IsPaired",
|
||||
"System.Devices.Aep.IsPresent",
|
||||
"System.Devices.Aep.ProtocolId",
|
||||
"System.Devices.Aep.Bluetooth.Le.IsConnectable",
|
||||
"System.Devices.Aep.SignalStrength"
|
||||
};
|
||||
|
||||
// BT_Code: Currently Bluetooth APIs don't provide a selector to get ALL devices that are both paired and non-paired.
|
||||
deviceWatcher =
|
||||
DeviceInformation.CreateWatcher(
|
||||
BTLEDeviceWatcherAQSString,
|
||||
requestedProperties,
|
||||
DeviceInformationKind.AssociationEndpoint);
|
||||
|
||||
// Register event handlers before starting the watcher.
|
||||
deviceWatcher.Added += DeviceWatcher_Added;
|
||||
deviceWatcher.Updated += DeviceWatcher_Updated;
|
||||
deviceWatcher.Removed += DeviceWatcher_Removed;
|
||||
deviceWatcher.EnumerationCompleted += DeviceWatcher_EnumerationCompleted;
|
||||
deviceWatcher.Stopped += DeviceWatcher_Stopped;
|
||||
|
||||
advertisementWatcher = new BluetoothLEAdvertisementWatcher();
|
||||
advertisementWatcher.Received += AdvertisementWatcher_Received;
|
||||
|
||||
BluetoothLEDevices.Clear();
|
||||
|
||||
deviceWatcher.Start();
|
||||
advertisementWatcher.Start();
|
||||
IsEnumerating = true;
|
||||
EnumerationFinished = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops enumeration of bluetooth device
|
||||
/// </summary>
|
||||
public void StopEnumeration()
|
||||
{
|
||||
if (deviceWatcher != null)
|
||||
{
|
||||
// Unregister the event handlers.
|
||||
deviceWatcher.Added -= DeviceWatcher_Added;
|
||||
deviceWatcher.Updated -= DeviceWatcher_Updated;
|
||||
deviceWatcher.Removed -= DeviceWatcher_Removed;
|
||||
deviceWatcher.EnumerationCompleted -= DeviceWatcher_EnumerationCompleted;
|
||||
deviceWatcher.Stopped -= DeviceWatcher_Stopped;
|
||||
|
||||
advertisementWatcher.Received += AdvertisementWatcher_Received;
|
||||
|
||||
// Stop the watchers
|
||||
deviceWatcher.Stop();
|
||||
deviceWatcher = null;
|
||||
|
||||
advertisementWatcher.Stop();
|
||||
advertisementWatcher = null;
|
||||
IsEnumerating = false;
|
||||
EnumerationFinished = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates device metadata based on advertisement received
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="args"></param>
|
||||
private async void AdvertisementWatcher_Received(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementReceivedEventArgs args)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
await BluetoothLEDevicesLock.WaitAsync();
|
||||
|
||||
foreach (ObservableBluetoothLEDevice d in BluetoothLEDevices)
|
||||
{
|
||||
if (d.BluetoothAddressAsUlong == args.BluetoothAddress)
|
||||
{
|
||||
await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(
|
||||
Windows.UI.Core.CoreDispatcherPriority.Normal,
|
||||
() =>
|
||||
{
|
||||
d.ServiceCount = args.Advertisement.ServiceUuids.Count();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("AdvertisementWatcher_Received: ", ex.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
BluetoothLEDevicesLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Callback when a new device is found
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="deviceInfo"></param>
|
||||
private async void DeviceWatcher_Added(DeviceWatcher sender, DeviceInformation deviceInfo)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Protect against race condition if the task runs after the app stopped the deviceWatcher.
|
||||
if (sender == deviceWatcher)
|
||||
{
|
||||
await AddDeviceToList(deviceInfo);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("DeviceWatcher_Added: " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes when a device is updated
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="deviceInfoUpdate"></param>
|
||||
private async void DeviceWatcher_Updated(DeviceWatcher sender, DeviceInformationUpdate deviceInfoUpdate)
|
||||
{
|
||||
DeviceInformation di = null;
|
||||
bool addNewDI = false;
|
||||
|
||||
try
|
||||
{
|
||||
// Protect against race condition if the task runs after the app stopped the deviceWatcher.
|
||||
if (sender == deviceWatcher)
|
||||
{
|
||||
ObservableBluetoothLEDevice dev;
|
||||
|
||||
// Need to lock as another DeviceWatcher might be modifying BluetoothLEDevices
|
||||
try
|
||||
{
|
||||
await BluetoothLEDevicesLock.WaitAsync();
|
||||
dev = BluetoothLEDevices.FirstOrDefault(device => device.DeviceInfo.Id == deviceInfoUpdate.Id);
|
||||
if (dev != null)
|
||||
{ // Found a device in the list, updating it
|
||||
await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(
|
||||
Windows.UI.Core.CoreDispatcherPriority.Normal,
|
||||
async () =>
|
||||
{
|
||||
dev.Update(deviceInfoUpdate);
|
||||
await UpdateBatteryLevel(dev);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
// Need to add this device. Can't do that here as we have the lock
|
||||
addNewDI = true;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
BluetoothLEDevicesLock.Release();
|
||||
}
|
||||
|
||||
if(addNewDI == true)
|
||||
{
|
||||
try
|
||||
{
|
||||
await BluetoothLEDevicesLock.WaitAsync();
|
||||
di = unusedDevices.FirstOrDefault(device => device.Id == deviceInfoUpdate.Id);
|
||||
if (di != null)
|
||||
{ // We found this device before.
|
||||
unusedDevices.Remove(di);
|
||||
di.Update(deviceInfoUpdate);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
BluetoothLEDevicesLock.Release();
|
||||
}
|
||||
|
||||
if (di != null)
|
||||
{
|
||||
await AddDeviceToList(di);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("DeviceWatcher_Updated: " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes when a device is removed from enumeration
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="deviceInfoUpdate"></param>
|
||||
private async void DeviceWatcher_Removed(DeviceWatcher sender, DeviceInformationUpdate deviceInfoUpdate)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Protect against race condition if the task runs after the app stopped the deviceWatcher.
|
||||
if (sender == deviceWatcher)
|
||||
{
|
||||
ObservableBluetoothLEDevice dev;
|
||||
|
||||
try
|
||||
{
|
||||
// Need to lock as another DeviceWatcher might be modifying BluetoothLEDevices
|
||||
await BluetoothLEDevicesLock.WaitAsync();
|
||||
|
||||
// Find the corresponding DeviceInformation in the collection and remove it.
|
||||
dev = BluetoothLEDevices.FirstOrDefault(device => device.DeviceInfo.Id == deviceInfoUpdate.Id);
|
||||
if (dev != null)
|
||||
{ // Found it in our displayed devices
|
||||
await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(
|
||||
Windows.UI.Core.CoreDispatcherPriority.Normal,
|
||||
() =>
|
||||
{
|
||||
Debug.Assert(BluetoothLEDevices.Remove(dev), "DeviceWatcher_Removed: Failed to remove device from list");
|
||||
});
|
||||
}
|
||||
else
|
||||
{ // Did not find in diplayed list, let's check the unused list
|
||||
DeviceInformation di = unusedDevices.FirstOrDefault(device => device.Id == deviceInfoUpdate.Id);
|
||||
|
||||
if (di != null)
|
||||
{ // Found in unused devices, remove it
|
||||
Debug.Assert(unusedDevices.Remove(di), "DeviceWatcher_Removed: Failed to remove device from unused");
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
BluetoothLEDevicesLock.Release();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("DeviceWatcher_Removed: " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes when Enumeration has finished
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void DeviceWatcher_EnumerationCompleted(DeviceWatcher sender, object e)
|
||||
{
|
||||
// Protect against race condition if the task runs after the app stopped the deviceWatcher.
|
||||
if (sender == deviceWatcher)
|
||||
{
|
||||
StopEnumeration();
|
||||
EnumerationFinished = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the new or updated device to the displayed or unused list
|
||||
/// </summary>
|
||||
/// <param name="deviceInfo"></param>
|
||||
/// <returns></returns>
|
||||
private async Task AddDeviceToList(DeviceInformation deviceInfo)
|
||||
{
|
||||
ObservableBluetoothLEDevice dev = new ObservableBluetoothLEDevice(deviceInfo);
|
||||
|
||||
// Let's make it connectable by default, we have error handles in case it doesn't work
|
||||
bool shouldDisplay =
|
||||
((dev.DeviceInfo.Properties.Keys.Contains("System.Devices.Aep.Bluetooth.Le.IsConnectable") &&
|
||||
(bool)dev.DeviceInfo.Properties["System.Devices.Aep.Bluetooth.Le.IsConnectable"])) ||
|
||||
((dev.DeviceInfo.Properties.Keys.Contains("System.Devices.Aep.IsConnected") &&
|
||||
(bool)dev.DeviceInfo.Properties["System.Devices.Aep.IsConnected"]));
|
||||
|
||||
if (shouldDisplay)
|
||||
{
|
||||
// Need to lock as another DeviceWatcher might be modifying BluetoothLEDevices
|
||||
try
|
||||
{
|
||||
await BluetoothLEDevicesLock.WaitAsync();
|
||||
|
||||
await UpdateBatteryLevel(dev);
|
||||
|
||||
if (!BluetoothLEDevices.Contains(dev))
|
||||
{
|
||||
await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(
|
||||
Windows.UI.Core.CoreDispatcherPriority.Normal,
|
||||
() =>
|
||||
{
|
||||
BluetoothLEDevices.Add(dev);
|
||||
});
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
BluetoothLEDevicesLock.Release();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
await BluetoothLEDevicesLock.WaitAsync();
|
||||
unusedDevices.Add(deviceInfo);
|
||||
}
|
||||
finally
|
||||
{
|
||||
BluetoothLEDevicesLock.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes when device watcher has stopped
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void DeviceWatcher_Stopped(DeviceWatcher sender, object e)
|
||||
{
|
||||
// Implimented for completeness
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes when a property has changed
|
||||
/// </summary>
|
||||
/// <param name="e"></param>
|
||||
private void OnPropertyChanged(PropertyChangedEventArgs e)
|
||||
{
|
||||
if (PropertyChanged != null)
|
||||
{
|
||||
PropertyChanged(this, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,679 @@
|
|||
// <copyright file="ObservableBluetoothLEDevice.cs" company="Microsoft Corporation">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//----------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BluetoothLEExplorer.Services.DispatcherService;
|
||||
using Windows.Devices.Bluetooth;
|
||||
using Windows.Devices.Bluetooth.GenericAttributeProfile;
|
||||
using Windows.Devices.Enumeration;
|
||||
using Windows.UI.Popups;
|
||||
using Windows.UI.Xaml.Media.Imaging;
|
||||
using Windows.Foundation.Metadata;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BluetoothLEExplorer.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Wrapper around <see cref="BluetoothLEDevice"/> to make it easier to use
|
||||
/// </summary>
|
||||
public class ObservableBluetoothLEDevice : INotifyPropertyChanged, IEquatable<ObservableBluetoothLEDevice>
|
||||
{
|
||||
/// <summary>
|
||||
/// Compares RSSI values between ObservableBluetoothLEDevice. Sorts based on closest to furthest where 0 is unknown
|
||||
/// and is sorted as furthest away
|
||||
/// </summary>
|
||||
public class RSSIComparer : IComparer
|
||||
{
|
||||
public int Compare(object x, object y)
|
||||
{
|
||||
ObservableBluetoothLEDevice a = x as ObservableBluetoothLEDevice;
|
||||
ObservableBluetoothLEDevice b = y as ObservableBluetoothLEDevice;
|
||||
|
||||
if( a == null || b == null)
|
||||
{
|
||||
throw new InvalidOperationException("Compared objects are not ObservableBluetoothLEDevice");
|
||||
}
|
||||
|
||||
// If they're equal
|
||||
if(a.RSSI == b.RSSI)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// RSSI == 0 means we don't know it. Always make that the end.
|
||||
if(b.RSSI == 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(a.RSSI < b.RSSI || a.rssi == 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="BluetoothLEDevice"/>
|
||||
/// </summary>
|
||||
private BluetoothLEDevice bluetoothLEDevice;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bluetooth device this class wraps
|
||||
/// </summary>
|
||||
public BluetoothLEDevice BluetoothLEDevice
|
||||
{
|
||||
get
|
||||
{
|
||||
return bluetoothLEDevice;
|
||||
}
|
||||
|
||||
private set
|
||||
{
|
||||
bluetoothLEDevice = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("BluetoothLEDevice"));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="Glyph"/>
|
||||
/// </summary>
|
||||
private BitmapImage glyph;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the glyph of this bluetooth device
|
||||
/// </summary>
|
||||
public BitmapImage Glyph
|
||||
{
|
||||
get
|
||||
{
|
||||
return glyph;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
glyph = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("Glyph"));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="DeviceInfo"/>
|
||||
/// </summary>
|
||||
private DeviceInformation deviceInfo;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the device information for the device this class wraps
|
||||
/// </summary>
|
||||
public DeviceInformation DeviceInfo
|
||||
{
|
||||
get
|
||||
{
|
||||
return deviceInfo;
|
||||
}
|
||||
|
||||
private set
|
||||
{
|
||||
deviceInfo = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("DeviceInfo"));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="IsConnected"/>
|
||||
/// </summary>
|
||||
private bool isConnected;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this device is connected
|
||||
/// </summary>
|
||||
public bool IsConnected
|
||||
{
|
||||
get
|
||||
{
|
||||
return isConnected;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (isConnected != value)
|
||||
{
|
||||
isConnected = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("IsConnected"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="IsPaired"/>
|
||||
/// </summary>
|
||||
private bool isPaired;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this device is paired
|
||||
/// </summary>
|
||||
public bool IsPaired
|
||||
{
|
||||
get
|
||||
{
|
||||
return isPaired;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (isPaired != value)
|
||||
{
|
||||
isPaired = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("IsPaired"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool isSecureConnection;
|
||||
|
||||
public bool IsSecureConnection
|
||||
{
|
||||
get
|
||||
{
|
||||
return isSecureConnection;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (isSecureConnection != value)
|
||||
{
|
||||
isSecureConnection = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("IsSecureConnection"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="Services"/>
|
||||
/// </summary>
|
||||
private ObservableCollection<ObservableGattDeviceService> services = new ObservableCollection<ObservableGattDeviceService>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the services this device supports
|
||||
/// </summary>
|
||||
public ObservableCollection<ObservableGattDeviceService> Services
|
||||
{
|
||||
get
|
||||
{
|
||||
return services;
|
||||
}
|
||||
|
||||
private set
|
||||
{
|
||||
services = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("Services"));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="ServiceCount"/>
|
||||
/// </summary>
|
||||
private int serviceCount;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of services this device has
|
||||
/// </summary>
|
||||
public int ServiceCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return serviceCount;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (serviceCount < value)
|
||||
{
|
||||
serviceCount = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("ServiceCount"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="Name"/>
|
||||
/// </summary>
|
||||
private string name;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of this device
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
private set
|
||||
{
|
||||
if (name != value)
|
||||
{
|
||||
name = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("Name"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="ErrorText"/>
|
||||
/// </summary>
|
||||
private string errorText;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the error text when connecting to this device fails
|
||||
/// </summary>
|
||||
public string ErrorText
|
||||
{
|
||||
get
|
||||
{
|
||||
return errorText;
|
||||
}
|
||||
|
||||
private set
|
||||
{
|
||||
errorText = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("ErrorText"));
|
||||
}
|
||||
}
|
||||
|
||||
private Queue<int> RssiValue = new Queue<int>(10);
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="RSSI"/>
|
||||
/// </summary>
|
||||
private int rssi;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the RSSI value of this device
|
||||
/// </summary>
|
||||
public int RSSI
|
||||
{
|
||||
get
|
||||
{
|
||||
return rssi;
|
||||
}
|
||||
|
||||
private set
|
||||
{
|
||||
if (RssiValue.Count >= 10)
|
||||
{
|
||||
RssiValue.Dequeue();
|
||||
}
|
||||
RssiValue.Enqueue(value);
|
||||
|
||||
int newValue = (int)Math.Round(RssiValue.Average(), 0);
|
||||
|
||||
if (rssi != newValue)
|
||||
{
|
||||
rssi = newValue;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("RSSI"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="BatteryLevel"/>
|
||||
/// </summary>
|
||||
private int batteryLevel = -1;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Battery level of this device. -1 if unknown.
|
||||
/// </summary>
|
||||
public int BatteryLevel
|
||||
{
|
||||
get
|
||||
{
|
||||
return batteryLevel;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (batteryLevel != value)
|
||||
{
|
||||
batteryLevel = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("BatteryLevel"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string bluetoothAddressAsString;
|
||||
/// <summary>
|
||||
/// Gets the bluetooth address of this device as a string
|
||||
/// </summary>
|
||||
public string BluetoothAddressAsString
|
||||
{
|
||||
get
|
||||
{
|
||||
return bluetoothAddressAsString;
|
||||
}
|
||||
|
||||
private set
|
||||
{
|
||||
if(bluetoothAddressAsString != value)
|
||||
{
|
||||
bluetoothAddressAsString = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("BluetoothAddressAsString"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ulong bluetoothAddressAsUlong;
|
||||
/// <summary>
|
||||
/// Gets the bluetooth address of this device
|
||||
/// </summary>
|
||||
public ulong BluetoothAddressAsUlong
|
||||
{
|
||||
get
|
||||
{
|
||||
return bluetoothAddressAsUlong;
|
||||
}
|
||||
|
||||
private set
|
||||
{
|
||||
if (bluetoothAddressAsUlong != value)
|
||||
{
|
||||
bluetoothAddressAsUlong = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("BluetoothAddressAsUlong"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the<see cref="ObservableBluetoothLEDevice" /> class.
|
||||
/// </summary>
|
||||
/// <param name="deviceInfo">The device info that describes this bluetooth device"/></param>
|
||||
public ObservableBluetoothLEDevice(DeviceInformation deviceInfo)
|
||||
{
|
||||
DeviceInfo = deviceInfo;
|
||||
Name = DeviceInfo.Name;
|
||||
|
||||
string ret = String.Empty;
|
||||
|
||||
if(DeviceInfo.Properties.ContainsKey("System.Devices.Aep.DeviceAddress"))
|
||||
{
|
||||
BluetoothAddressAsString = ret = DeviceInfo.Properties["System.Devices.Aep.DeviceAddress"].ToString();
|
||||
BluetoothAddressAsUlong = Convert.ToUInt64(BluetoothAddressAsString.Replace(":", String.Empty), 16);
|
||||
}
|
||||
|
||||
IsPaired = DeviceInfo.Pairing.IsPaired;
|
||||
|
||||
LoadGlyph();
|
||||
|
||||
this.PropertyChanged += ObservableBluetoothLEDevice_PropertyChanged;
|
||||
}
|
||||
|
||||
private void ObservableBluetoothLEDevice_PropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if(e.PropertyName == "DeviceInfo")
|
||||
{
|
||||
if(DeviceInfo.Properties.ContainsKey("System.Devices.Aep.SignalStrength") && DeviceInfo.Properties["System.Devices.Aep.SignalStrength"] != null)
|
||||
{
|
||||
RSSI = (int)DeviceInfo.Properties["System.Devices.Aep.SignalStrength"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// result of finding all the services
|
||||
/// </summary>
|
||||
private GattDeviceServicesResult result;
|
||||
|
||||
/// <summary>
|
||||
/// Event to notify when this object has changed
|
||||
/// </summary>
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Connect to this bluetooth device
|
||||
/// </summary>
|
||||
/// <returns>Connection task</returns>
|
||||
public async Task<bool> Connect()
|
||||
{
|
||||
bool ret = false;
|
||||
string debugMsg = String.Format("Connect: ");
|
||||
|
||||
Debug.WriteLine(debugMsg + "Entering");
|
||||
|
||||
await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunTaskAsync(async () =>
|
||||
{
|
||||
Debug.WriteLine(debugMsg + "In UI thread");
|
||||
try
|
||||
{
|
||||
|
||||
if (BluetoothLEDevice == null)
|
||||
{
|
||||
Debug.WriteLine(debugMsg + "Calling BluetoothLEDevice.FromIdAsync");
|
||||
BluetoothLEDevice = await BluetoothLEDevice.FromIdAsync(DeviceInfo.Id);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.WriteLine(debugMsg + "Previously connected, not calling BluetoothLEDevice.FromIdAsync");
|
||||
}
|
||||
|
||||
if (BluetoothLEDevice == null)
|
||||
{
|
||||
ret = false;
|
||||
Debug.WriteLine(debugMsg + "BluetoothLEDevice is null");
|
||||
|
||||
MessageDialog dialog = new MessageDialog("No permission to access device", "Connection error");
|
||||
await dialog.ShowAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.WriteLine(debugMsg + "BluetoothLEDevice is " + BluetoothLEDevice.Name);
|
||||
|
||||
// Setup our event handlers and view model properties
|
||||
BluetoothLEDevice.ConnectionStatusChanged += BluetoothLEDevice_ConnectionStatusChanged;
|
||||
BluetoothLEDevice.NameChanged += BluetoothLEDevice_NameChanged;
|
||||
|
||||
IsPaired = DeviceInfo.Pairing.IsPaired;
|
||||
IsConnected = BluetoothLEDevice.ConnectionStatus == BluetoothConnectionStatus.Connected;
|
||||
|
||||
Name = BluetoothLEDevice.Name;
|
||||
|
||||
// Get all the services for this device
|
||||
CancellationTokenSource GetGattServicesAsyncTokenSource = new CancellationTokenSource(5000);
|
||||
var GetGattServicesAsyncTask = Task.Run(() => BluetoothLEDevice.GetGattServicesAsync(BluetoothCacheMode.Uncached), GetGattServicesAsyncTokenSource.Token);
|
||||
|
||||
result = await GetGattServicesAsyncTask.Result;
|
||||
|
||||
if (result.Status == GattCommunicationStatus.Success)
|
||||
{
|
||||
// In case we connected before, clear the service list and recreate it
|
||||
Services.Clear();
|
||||
|
||||
System.Diagnostics.Debug.WriteLine(debugMsg + "GetGattServiceAsync SUCCESS");
|
||||
foreach (var serv in result.Services)
|
||||
{
|
||||
Services.Add(new ObservableGattDeviceService(serv));
|
||||
}
|
||||
|
||||
ServiceCount = Services.Count();
|
||||
ret = true;
|
||||
}
|
||||
else if (result.Status == GattCommunicationStatus.ProtocolError)
|
||||
{
|
||||
ErrorText = debugMsg + "GetGattServiceAsync Error: Protocol Error - " + result.ProtocolError.Value;
|
||||
System.Diagnostics.Debug.WriteLine(ErrorText);
|
||||
string msg = "Connection protocol error: " + result.ProtocolError.Value.ToString();
|
||||
var messageDialog = new MessageDialog(msg, "Connection failures");
|
||||
await messageDialog.ShowAsync();
|
||||
|
||||
}
|
||||
else if (result.Status == GattCommunicationStatus.Unreachable)
|
||||
{
|
||||
ErrorText = debugMsg + "GetGattServiceAsync Error: Unreachable";
|
||||
System.Diagnostics.Debug.WriteLine(ErrorText);
|
||||
string msg = "Device unreachable";
|
||||
var messageDialog = new MessageDialog(msg, "Connection failures");
|
||||
await messageDialog.ShowAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine(debugMsg + "Exception - " + ex.Message);
|
||||
string msg = String.Format("Message:\n{0}\n\nInnerException:\n{1}\n\nStack:\n{2}", ex.Message, ex.InnerException, ex.StackTrace);
|
||||
|
||||
var messageDialog = new MessageDialog(msg, "Exception");
|
||||
await messageDialog.ShowAsync();
|
||||
|
||||
// Debugger break here so we can catch unknown exceptions
|
||||
Debugger.Break();
|
||||
}
|
||||
});
|
||||
|
||||
if (ret)
|
||||
{
|
||||
Debug.WriteLine(debugMsg + "Exiting (0)");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.WriteLine(debugMsg + "Exiting (-1)");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public async Task<bool> DoInAppPairing()
|
||||
{
|
||||
Debug.WriteLine("Trying in app pairing");
|
||||
|
||||
// BT_Code: Pair the currently selected device.
|
||||
DevicePairingResult result = await DeviceInfo.Pairing.PairAsync();
|
||||
|
||||
Debug.WriteLine($"Pairing result: {result.Status.ToString()}");
|
||||
|
||||
if (result.Status == DevicePairingResultStatus.Paired ||
|
||||
result.Status == DevicePairingResultStatus.AlreadyPaired)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageDialog d = new MessageDialog("Pairing error", result.Status.ToString());
|
||||
await d.ShowAsync();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes when the name of this devices changes
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="args"></param>
|
||||
private async void BluetoothLEDevice_NameChanged(BluetoothLEDevice sender, object args)
|
||||
{
|
||||
await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(
|
||||
Windows.UI.Core.CoreDispatcherPriority.Normal,
|
||||
() =>
|
||||
{
|
||||
Name = BluetoothLEDevice.Name;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes when the connection state changes
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="args"></param>
|
||||
private async void BluetoothLEDevice_ConnectionStatusChanged(BluetoothLEDevice sender, object args)
|
||||
{
|
||||
await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(
|
||||
Windows.UI.Core.CoreDispatcherPriority.Normal,
|
||||
() =>
|
||||
{
|
||||
IsPaired = DeviceInfo.Pairing.IsPaired;
|
||||
IsConnected = BluetoothLEDevice.ConnectionStatus == BluetoothConnectionStatus.Connected;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load the glyph for this device
|
||||
/// </summary>
|
||||
private async void LoadGlyph()
|
||||
{
|
||||
await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(
|
||||
Windows.UI.Core.CoreDispatcherPriority.Normal,
|
||||
async () =>
|
||||
{
|
||||
DeviceThumbnail deviceThumbnail = await DeviceInfo.GetGlyphThumbnailAsync();
|
||||
BitmapImage glyphBitmapImage = new BitmapImage();
|
||||
await glyphBitmapImage.SetSourceAsync(deviceThumbnail);
|
||||
Glyph = glyphBitmapImage;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes when a property is changed
|
||||
/// </summary>
|
||||
/// <param name="e"></param>
|
||||
private void OnPropertyChanged(PropertyChangedEventArgs e)
|
||||
{
|
||||
if (PropertyChanged != null)
|
||||
{
|
||||
PropertyChanged(this, e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the ToString function to return the name of the device
|
||||
/// </summary>
|
||||
/// <returns>Name of this characteristic</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return this.Name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares this device to other bluetooth devices by checking the id
|
||||
/// </summary>
|
||||
/// <param name="other"></param>
|
||||
/// <returns>true for equal</returns>
|
||||
bool IEquatable<ObservableBluetoothLEDevice>.Equals(ObservableBluetoothLEDevice other)
|
||||
{
|
||||
if (other == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.DeviceInfo.Id == other.DeviceInfo.Id)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates this device's deviceInformation
|
||||
/// </summary>
|
||||
/// <param name="deviceUpdate"></param>
|
||||
public void Update(DeviceInformationUpdate deviceUpdate)
|
||||
{
|
||||
DeviceInfo.Update(deviceUpdate);
|
||||
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("DeviceInfo"));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,742 @@
|
|||
// <copyright file="ObservableGattCharacteristics.cs" company="Microsoft Corporation">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//----------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using BluetoothLEExplorer.Services.GattUuidsService;
|
||||
using BluetoothLEExplorer.Services.Other;
|
||||
using Windows.Devices.Bluetooth;
|
||||
using Windows.Devices.Bluetooth.GenericAttributeProfile;
|
||||
using Windows.Security.Cryptography;
|
||||
using Windows.Storage.Streams;
|
||||
using GattHelper.Converters;
|
||||
|
||||
namespace BluetoothLEExplorer.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Wrapper around <see cref="GattCharacteristic"/> to make it easier to use
|
||||
/// </summary>
|
||||
public class ObservableGattCharacteristics : INotifyPropertyChanged
|
||||
{
|
||||
/// <summary>
|
||||
/// Enum used to determine how the <see cref="Value"/> should be displayed
|
||||
/// </summary>
|
||||
public enum DisplayTypes
|
||||
{
|
||||
NotSet,
|
||||
Bool,
|
||||
Decimal,
|
||||
Hex,
|
||||
UTF8,
|
||||
UTF16,
|
||||
Unsupported
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raw buffer of this value of this characteristic
|
||||
/// </summary>
|
||||
private IBuffer rawData;
|
||||
|
||||
/// <summary>
|
||||
/// byte array representation of the characteristic value
|
||||
/// </summary>
|
||||
private byte[] data;
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="Characteristic"/>
|
||||
/// </summary>
|
||||
private GattCharacteristic characteristic;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the characteristic this class wraps
|
||||
/// </summary>
|
||||
public GattCharacteristic Characteristic
|
||||
{
|
||||
get
|
||||
{
|
||||
return characteristic;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (characteristic != value)
|
||||
{
|
||||
characteristic = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("Characteristic"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="IsIndicateSet"/>
|
||||
/// </summary>
|
||||
private bool isIndicateSet = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether indicate is set
|
||||
/// </summary>
|
||||
public bool IsIndicateSet
|
||||
{
|
||||
get
|
||||
{
|
||||
return isIndicateSet;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (isIndicateSet != value)
|
||||
{
|
||||
isIndicateSet = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("IsIndicateSet"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="IsNotifySet"/>
|
||||
/// </summary>
|
||||
private bool isNotifySet = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether notify is set
|
||||
/// </summary>
|
||||
public bool IsNotifySet
|
||||
{
|
||||
get
|
||||
{
|
||||
return isNotifySet;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (isNotifySet != value)
|
||||
{
|
||||
isNotifySet = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("IsNotifySet"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="Parent"/>
|
||||
/// </summary>
|
||||
private ObservableGattDeviceService parent;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the parent service of this characteristic
|
||||
/// </summary>
|
||||
public ObservableGattDeviceService Parent
|
||||
{
|
||||
get
|
||||
{
|
||||
return parent;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (parent != value)
|
||||
{
|
||||
parent = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("Parent"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="Name"/>
|
||||
/// </summary>
|
||||
private string name;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of this characteristic
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (name != value)
|
||||
{
|
||||
name = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("Name"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="UUID"/>
|
||||
/// </summary>
|
||||
private string uuid;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the UUID of this characteristic
|
||||
/// </summary>
|
||||
public string UUID
|
||||
{
|
||||
get
|
||||
{
|
||||
return uuid;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (uuid != value)
|
||||
{
|
||||
uuid = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("UUID"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="Value"/>
|
||||
/// </summary>
|
||||
private string value;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of this characteristic
|
||||
/// </summary>
|
||||
public string Value
|
||||
{
|
||||
get
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
private set
|
||||
{
|
||||
if (this.value != value)
|
||||
{
|
||||
this.value = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("Value"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="DisplayType"/>
|
||||
/// </summary>
|
||||
private DisplayTypes displayType = DisplayTypes.NotSet;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets how this characteristic's value should be displayed
|
||||
/// </summary>
|
||||
public DisplayTypes DisplayType
|
||||
{
|
||||
get
|
||||
{
|
||||
return displayType;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (value == DisplayTypes.NotSet)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (displayType != value)
|
||||
{
|
||||
displayType = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("DisplayType"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the<see cref="ObservableGattCharacteristics" /> class.
|
||||
/// </summary>
|
||||
/// <param name="characteristic">Characteristic this class wraps</param>
|
||||
/// <param name="parent">The parent service that wraps this characteristic</param>
|
||||
public ObservableGattCharacteristics(GattCharacteristic characteristic, ObservableGattDeviceService parent)
|
||||
{
|
||||
Characteristic = characteristic;
|
||||
Parent = parent;
|
||||
Name = GattUuidsService.ConvertUuidToName(Characteristic.Uuid);
|
||||
UUID = Characteristic.Uuid.ToString();
|
||||
|
||||
ReadValueAsync();
|
||||
|
||||
characteristic.ValueChanged += Characteristic_ValueChanged;
|
||||
|
||||
PropertyChanged += ObservableGattCharacteristics_PropertyChanged;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destruct this object by unsetting notification/indication and unregistering from property changed callbacks
|
||||
/// </summary>
|
||||
~ObservableGattCharacteristics()
|
||||
{
|
||||
characteristic.ValueChanged -= Characteristic_ValueChanged;
|
||||
PropertyChanged -= ObservableGattCharacteristics_PropertyChanged;
|
||||
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cleanup this object by unsetting notification/indication
|
||||
/// </summary>
|
||||
private async void Cleanup()
|
||||
{
|
||||
await StopIndicate();
|
||||
await StopNotify();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes when this characteristic changes
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void ObservableGattCharacteristics_PropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == "DisplayType")
|
||||
{
|
||||
SetValue();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the value of the Characteristic
|
||||
/// </summary>
|
||||
public async void ReadValueAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
GattReadResult result = await Characteristic.ReadValueAsync(BluetoothCacheMode.Uncached);
|
||||
|
||||
if (result.Status == GattCommunicationStatus.Success)
|
||||
{
|
||||
SetValue(result.Value);
|
||||
}
|
||||
else if (result.Status == GattCommunicationStatus.ProtocolError)
|
||||
{
|
||||
Value = Services.Other.GattProtocolErrorParser.GetErrorString(result.ProtocolError);
|
||||
}
|
||||
else
|
||||
{
|
||||
Value = "Unreachable";
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("Exception: " + ex.Message);
|
||||
Value = "Exception!";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set's the indicate descriptor
|
||||
/// </summary>
|
||||
/// <returns>Set indicate task</returns>
|
||||
public async Task<bool> SetIndicate()
|
||||
{
|
||||
if (IsIndicateSet == true)
|
||||
{
|
||||
// already set
|
||||
return true;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// BT_Code: Must write the CCCD in order for server to send indications.
|
||||
// We receive them in the ValueChanged event handler.
|
||||
// Note that this sample configures either Indicate or Notify, but not both.
|
||||
var result = await
|
||||
Characteristic.WriteClientCharacteristicConfigurationDescriptorAsync(
|
||||
GattClientCharacteristicConfigurationDescriptorValue.Indicate);
|
||||
if (result == GattCommunicationStatus.Success)
|
||||
{
|
||||
Debug.WriteLine("Successfully registered for indications");
|
||||
IsIndicateSet = true;
|
||||
return true;
|
||||
}
|
||||
else if (result == GattCommunicationStatus.ProtocolError)
|
||||
{
|
||||
Debug.WriteLine("Error registering for indications: Protocol Error");
|
||||
IsIndicateSet = false;
|
||||
return false;
|
||||
}
|
||||
else if (result == GattCommunicationStatus.Unreachable)
|
||||
{
|
||||
Debug.WriteLine("Error registering for indications: Unreachable");
|
||||
IsIndicateSet = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (UnauthorizedAccessException ex)
|
||||
{
|
||||
// This usually happens when a device reports that it support indicate, but it actually doesn't.
|
||||
Debug.WriteLine("Unauthorized Exception: " + ex.Message);
|
||||
IsIndicateSet = false;
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("Generic Exception: " + ex.Message);
|
||||
IsIndicateSet = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
IsIndicateSet = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsets the indicate descriptor
|
||||
/// </summary>
|
||||
/// <returns>Unset indicate task</returns>
|
||||
public async Task<bool> StopIndicate()
|
||||
{
|
||||
if (IsIndicateSet == false)
|
||||
{
|
||||
// indicate is not set, can skip this
|
||||
return true;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// BT_Code: Must write the CCCD in order for server to send indications.
|
||||
// We receive them in the ValueChanged event handler.
|
||||
// Note that this sample configures either Indicate or Notify, but not both.
|
||||
var result = await
|
||||
Characteristic.WriteClientCharacteristicConfigurationDescriptorAsync(
|
||||
GattClientCharacteristicConfigurationDescriptorValue.None);
|
||||
if (result == GattCommunicationStatus.Success)
|
||||
{
|
||||
Debug.WriteLine("Successfully un-registered for indications");
|
||||
IsIndicateSet = false;
|
||||
return true;
|
||||
}
|
||||
else if (result == GattCommunicationStatus.ProtocolError)
|
||||
{
|
||||
Debug.WriteLine("Error un-registering for indications: Protocol Error");
|
||||
IsIndicateSet = true;
|
||||
return false;
|
||||
}
|
||||
else if (result == GattCommunicationStatus.Unreachable)
|
||||
{
|
||||
Debug.WriteLine("Error un-registering for indications: Unreachable");
|
||||
IsIndicateSet = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (UnauthorizedAccessException ex)
|
||||
{
|
||||
// This usually happens when a device reports that it support indicate, but it actually doesn't.
|
||||
Debug.WriteLine("Exception: " + ex.Message);
|
||||
IsIndicateSet = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the notify characteristic
|
||||
/// </summary>
|
||||
/// <returns>Set notify task</returns>
|
||||
public async Task<bool> SetNotify()
|
||||
{
|
||||
if (IsNotifySet == true)
|
||||
{
|
||||
// already set
|
||||
return true;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// BT_Code: Must write the CCCD in order for server to send indications.
|
||||
// We receive them in the ValueChanged event handler.
|
||||
// Note that this sample configures either Indicate or Notify, but not both.
|
||||
var result = await
|
||||
Characteristic.WriteClientCharacteristicConfigurationDescriptorAsync(
|
||||
GattClientCharacteristicConfigurationDescriptorValue.Notify);
|
||||
if (result == GattCommunicationStatus.Success)
|
||||
{
|
||||
Debug.WriteLine("Successfully registered for notifications");
|
||||
IsNotifySet = true;
|
||||
return true;
|
||||
}
|
||||
else if (result == GattCommunicationStatus.ProtocolError)
|
||||
{
|
||||
Debug.WriteLine("Error registering for notifications: Protocol Error");
|
||||
IsNotifySet = false;
|
||||
return false;
|
||||
}
|
||||
else if (result == GattCommunicationStatus.Unreachable)
|
||||
{
|
||||
Debug.WriteLine("Error registering for notifications: Unreachable");
|
||||
IsNotifySet = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (UnauthorizedAccessException ex)
|
||||
{
|
||||
// This usually happens when a device reports that it support indicate, but it actually doesn't.
|
||||
Debug.WriteLine("Unauthorized Exception: " + ex.Message);
|
||||
IsNotifySet = false;
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("Generic Exception: " + ex.Message);
|
||||
IsNotifySet = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
IsNotifySet = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsets the notify descriptor
|
||||
/// </summary>
|
||||
/// <returns>Unset notify task</returns>
|
||||
public async Task<bool> StopNotify()
|
||||
{
|
||||
if (IsNotifySet == false)
|
||||
{
|
||||
// indicate is not set, can skip this
|
||||
return true;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// BT_Code: Must write the CCCD in order for server to send indications.
|
||||
// We receive them in the ValueChanged event handler.
|
||||
// Note that this sample configures either Indicate or Notify, but not both.
|
||||
var result = await
|
||||
Characteristic.WriteClientCharacteristicConfigurationDescriptorAsync(
|
||||
GattClientCharacteristicConfigurationDescriptorValue.None);
|
||||
if (result == GattCommunicationStatus.Success)
|
||||
{
|
||||
Debug.WriteLine("Successfully un-registered for notifications");
|
||||
IsNotifySet = false;
|
||||
return true;
|
||||
}
|
||||
else if (result == GattCommunicationStatus.ProtocolError)
|
||||
{
|
||||
Debug.WriteLine("Error un-registering for notifications: Protocol Error");
|
||||
IsNotifySet = true;
|
||||
return false;
|
||||
}
|
||||
else if (result == GattCommunicationStatus.Unreachable)
|
||||
{
|
||||
Debug.WriteLine("Error un-registering for notifications: Unreachable");
|
||||
IsNotifySet = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (UnauthorizedAccessException ex)
|
||||
{
|
||||
// This usually happens when a device reports that it support indicate, but it actually doesn't.
|
||||
Debug.WriteLine("Exception: " + ex.Message);
|
||||
IsNotifySet = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes when value changes
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="args"></param>
|
||||
private async void Characteristic_ValueChanged(GattCharacteristic sender, GattValueChangedEventArgs args)
|
||||
{
|
||||
await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(
|
||||
Windows.UI.Core.CoreDispatcherPriority.Normal,
|
||||
() =>
|
||||
{
|
||||
SetValue(args.CharacteristicValue);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// helper function that copies the raw data into byte array
|
||||
/// </summary>
|
||||
/// <param name="buffer">The raw input buffer</param>
|
||||
private void SetValue(IBuffer buffer)
|
||||
{
|
||||
rawData = buffer;
|
||||
CryptographicBuffer.CopyToByteArray(rawData, out data);
|
||||
|
||||
SetValue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the value of this characteristic based on the display type
|
||||
/// </summary>
|
||||
private void SetValue()
|
||||
{
|
||||
if (data == null)
|
||||
{
|
||||
Value = "NULL";
|
||||
return;
|
||||
}
|
||||
|
||||
GattPresentationFormat format = null;
|
||||
|
||||
if (Characteristic.PresentationFormats.Count > 0)
|
||||
{
|
||||
format = Characteristic.PresentationFormats[0];
|
||||
}
|
||||
|
||||
// Determine what to set our DisplayType to
|
||||
if (format == null && DisplayType == DisplayTypes.NotSet)
|
||||
{
|
||||
if (Name == "DeviceName")
|
||||
{
|
||||
// All devices have DeviceName so this is a special case.
|
||||
DisplayType = DisplayTypes.UTF8;
|
||||
}
|
||||
else
|
||||
{
|
||||
string buffer = string.Empty;
|
||||
bool isString = true;
|
||||
|
||||
try
|
||||
{
|
||||
buffer = GattConvert.ToUTF8String(rawData);
|
||||
}
|
||||
catch(Exception)
|
||||
{
|
||||
isString = false;
|
||||
}
|
||||
|
||||
if (isString == true)
|
||||
{
|
||||
|
||||
// if buffer is only 1 char or 2 char with 0 at end then let's assume it's hex
|
||||
if (buffer.Length == 1)
|
||||
{
|
||||
isString = false;
|
||||
}
|
||||
else if (buffer.Length == 2 && buffer[1] == 0)
|
||||
{
|
||||
isString = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (char b in buffer)
|
||||
{
|
||||
// if within the reasonable range of used characters and not null, let's assume it's a UTF8 string by default, else hex
|
||||
if ((b < ' ' || b > '~') && b != 0)
|
||||
{
|
||||
isString = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isString)
|
||||
{
|
||||
DisplayType = DisplayTypes.UTF8;
|
||||
}
|
||||
else
|
||||
{
|
||||
// By default, display as Hex
|
||||
DisplayType = DisplayTypes.Hex;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (format != null && DisplayType == DisplayTypes.NotSet)
|
||||
{
|
||||
if (format.FormatType == GattPresentationFormatTypes.Boolean ||
|
||||
format.FormatType == GattPresentationFormatTypes.Bit2 ||
|
||||
format.FormatType == GattPresentationFormatTypes.Nibble ||
|
||||
format.FormatType == GattPresentationFormatTypes.UInt8 ||
|
||||
format.FormatType == GattPresentationFormatTypes.UInt12 ||
|
||||
format.FormatType == GattPresentationFormatTypes.UInt16 ||
|
||||
format.FormatType == GattPresentationFormatTypes.UInt24 ||
|
||||
format.FormatType == GattPresentationFormatTypes.UInt32 ||
|
||||
format.FormatType == GattPresentationFormatTypes.UInt48 ||
|
||||
format.FormatType == GattPresentationFormatTypes.UInt64 ||
|
||||
format.FormatType == GattPresentationFormatTypes.SInt8 ||
|
||||
format.FormatType == GattPresentationFormatTypes.SInt12 ||
|
||||
format.FormatType == GattPresentationFormatTypes.SInt16 ||
|
||||
format.FormatType == GattPresentationFormatTypes.SInt24 ||
|
||||
format.FormatType == GattPresentationFormatTypes.SInt32)
|
||||
{
|
||||
DisplayType = DisplayTypes.Decimal;
|
||||
}
|
||||
else if (format.FormatType == GattPresentationFormatTypes.Utf8)
|
||||
{
|
||||
DisplayType = DisplayTypes.UTF8;
|
||||
}
|
||||
else if (format.FormatType == GattPresentationFormatTypes.Utf16)
|
||||
{
|
||||
DisplayType = DisplayTypes.UTF16;
|
||||
}
|
||||
else if (format.FormatType == GattPresentationFormatTypes.UInt128 ||
|
||||
format.FormatType == GattPresentationFormatTypes.SInt128 ||
|
||||
format.FormatType == GattPresentationFormatTypes.DUInt16 ||
|
||||
format.FormatType == GattPresentationFormatTypes.SInt64 ||
|
||||
format.FormatType == GattPresentationFormatTypes.Struct ||
|
||||
format.FormatType == GattPresentationFormatTypes.Float ||
|
||||
format.FormatType == GattPresentationFormatTypes.Float32 ||
|
||||
format.FormatType == GattPresentationFormatTypes.Float64)
|
||||
{
|
||||
DisplayType = DisplayTypes.Unsupported;
|
||||
}
|
||||
else
|
||||
{
|
||||
DisplayType = DisplayTypes.Unsupported;
|
||||
}
|
||||
}
|
||||
|
||||
// Decode the value into the right display type
|
||||
if (DisplayType == DisplayTypes.Hex || DisplayType == DisplayTypes.Unsupported)
|
||||
{
|
||||
Value = GattConvert.ToHexString(rawData);
|
||||
}
|
||||
else if (DisplayType == DisplayTypes.Decimal)
|
||||
{
|
||||
//TODO: if data is larger then int32 this will overflow. Need to fix.
|
||||
Value = GattConvert.ToInt32(rawData).ToString();
|
||||
}
|
||||
else if (DisplayType == DisplayTypes.UTF8)
|
||||
{
|
||||
Value = GattConvert.ToUTF8String(rawData);
|
||||
}
|
||||
else if (DisplayType == DisplayTypes.UTF16)
|
||||
{
|
||||
Value = GattConvert.ToUTF16String(rawData);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event to notify when this object has changed
|
||||
/// </summary>
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Executes when this class changes
|
||||
/// </summary>
|
||||
/// <param name="e"></param>
|
||||
private void OnPropertyChanged(PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == "DisplayType")
|
||||
{
|
||||
Debug.WriteLine($"{this.Name} - DisplayType set: {this.DisplayType.ToString()}");
|
||||
}
|
||||
|
||||
if (PropertyChanged != null)
|
||||
{
|
||||
PropertyChanged(this, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,279 @@
|
|||
// <copyright file="ObservableGattDeviceService.cs" company="Microsoft Corporation">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//----------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BluetoothLEExplorer.Services.GattUuidsService;
|
||||
using Windows.Devices.Bluetooth.GenericAttributeProfile;
|
||||
|
||||
namespace BluetoothLEExplorer.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Wrapper around <see cref="GattDeviceService"/> to make it easier to use
|
||||
/// </summary>
|
||||
public class ObservableGattDeviceService : INotifyPropertyChanged
|
||||
{
|
||||
/// <summary>
|
||||
/// Source for <see cref="Service"/>
|
||||
/// </summary>
|
||||
private GattDeviceService service;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the service this class wraps
|
||||
/// </summary>
|
||||
public GattDeviceService Service
|
||||
{
|
||||
get
|
||||
{
|
||||
return service;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (service != value)
|
||||
{
|
||||
service = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("Service"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="Characteristics"/>
|
||||
/// </summary>
|
||||
private ObservableCollection<ObservableGattCharacteristics> characteristics = new ObservableCollection<ObservableGattCharacteristics>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets all the characteristics of this service
|
||||
/// </summary>
|
||||
public ObservableCollection<ObservableGattCharacteristics> Characteristics
|
||||
{
|
||||
get
|
||||
{
|
||||
return characteristics;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (characteristics != value)
|
||||
{
|
||||
characteristics = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("Characteristics"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="SelectedCharacteristic"/>
|
||||
/// </summary>
|
||||
private ObservableGattCharacteristics selectedCharacteristic;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the currently selected characteristic
|
||||
/// </summary>
|
||||
public ObservableGattCharacteristics SelectedCharacteristic
|
||||
{
|
||||
get
|
||||
{
|
||||
return selectedCharacteristic;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (selectedCharacteristic != value)
|
||||
{
|
||||
selectedCharacteristic = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("SelectedCharacteristic"));
|
||||
|
||||
// The SelectedProperty doesn't exist when this object is first created. This takes
|
||||
// care of adding the correct event handler after the first time it's changed.
|
||||
SelectedCharacteristic_PropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="Name"/>
|
||||
/// </summary>
|
||||
private string name;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of this service
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (name != value)
|
||||
{
|
||||
name = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("Name"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="UUID"/>
|
||||
/// </summary>
|
||||
private string uuid;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the UUID of this service
|
||||
/// </summary>
|
||||
public string UUID
|
||||
{
|
||||
get
|
||||
{
|
||||
return uuid;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (uuid != value)
|
||||
{
|
||||
uuid = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs("UUID"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the SelectedCharacteristic_PropertyChanged has been added
|
||||
/// </summary>
|
||||
private bool hasSelectedCharacteristicPropertyChangedHandler = false;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ObservableGattDeviceService" /> class.
|
||||
/// </summary>
|
||||
/// <param name="service">The service this class wraps</param>
|
||||
public ObservableGattDeviceService(GattDeviceService service)
|
||||
{
|
||||
Service = service;
|
||||
Name = GattUuidsService.ConvertUuidToName(service.Uuid);
|
||||
UUID = Service.Uuid.ToString();
|
||||
GetAllCharacteristics();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destruct by clearing characteristic list
|
||||
/// </summary>
|
||||
~ObservableGattDeviceService()
|
||||
{
|
||||
Characteristics.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the SelectedCharacteristic_PropertyChanged event handler
|
||||
/// </summary>
|
||||
private void SelectedCharacteristic_PropertyChanged()
|
||||
{
|
||||
if (hasSelectedCharacteristicPropertyChangedHandler == false)
|
||||
{
|
||||
SelectedCharacteristic.PropertyChanged += SelectedCharacteristic_PropertyChanged;
|
||||
hasSelectedCharacteristicPropertyChangedHandler = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the selected characteristic in the app context
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void SelectedCharacteristic_PropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
GattSampleContext.Context.SelectedCharacteristic = SelectedCharacteristic;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the characteristics of this service
|
||||
/// </summary>
|
||||
private async void GetAllCharacteristics()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append("ObservableGattDeviceService::getAllCharacteristics: ");
|
||||
sb.Append(Name);
|
||||
|
||||
try
|
||||
{
|
||||
CancellationTokenSource tokenSource = new CancellationTokenSource(5000);
|
||||
var t = Task.Run(() => Service.GetCharacteristicsAsync(Windows.Devices.Bluetooth.BluetoothCacheMode.Uncached), tokenSource.Token);
|
||||
|
||||
GattCharacteristicsResult result = null;
|
||||
result = await t.Result;
|
||||
|
||||
if (result.Status == GattCommunicationStatus.Success)
|
||||
{
|
||||
sb.Append(" - getAllCharacteristics found ");
|
||||
sb.Append(result.Characteristics.Count());
|
||||
sb.Append(" characteristics");
|
||||
Debug.WriteLine(sb);
|
||||
foreach (GattCharacteristic gattchar in result.Characteristics)
|
||||
{
|
||||
Characteristics.Add(new ObservableGattCharacteristics(gattchar, this));
|
||||
}
|
||||
}
|
||||
else if (result.Status == GattCommunicationStatus.Unreachable)
|
||||
{
|
||||
sb.Append(" - getAllCharacteristics failed with Unreachable");
|
||||
Debug.WriteLine(sb.ToString());
|
||||
}
|
||||
else if (result.Status == GattCommunicationStatus.ProtocolError)
|
||||
{
|
||||
sb.Append(" - getAllCharacteristics failed with Unreachable");
|
||||
Debug.WriteLine(sb.ToString());
|
||||
}
|
||||
}
|
||||
catch (AggregateException ae)
|
||||
{
|
||||
foreach (var ex in ae.InnerExceptions)
|
||||
{
|
||||
if (ex is TaskCanceledException)
|
||||
{
|
||||
Debug.WriteLine("Getting characteristics took too long.");
|
||||
Name += " - Timed out getting some characteristics";
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
// Bug 9145823:GetCharacteristicsAsync throw System.UnauthorizedAccessException when querying GenericAccess Service Characteristics
|
||||
Name += " - Unauthorized Access";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine("getAllCharacteristics: Exception - {0}" + ex.Message);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event to notify when this object has changed
|
||||
/// </summary>
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Property changed
|
||||
/// </summary>
|
||||
/// <param name="e"></param>
|
||||
private void OnPropertyChanged(PropertyChangedEventArgs e)
|
||||
{
|
||||
if (PropertyChanged != null)
|
||||
{
|
||||
PropertyChanged(this, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" IgnorableNamespaces="uap mp">
|
||||
<Identity Name="Microsoft.BluetoothLEExplorer" Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" Version="1.11.1.0" />
|
||||
<mp:PhoneIdentity PhoneProductId="8fce680c-fda2-4d28-8e68-9bb7d53f192f" PhonePublisherId="00000000-0000-0000-0000-000000000000" />
|
||||
<Properties>
|
||||
<DisplayName>Bluetooth LE Explorer</DisplayName>
|
||||
<PublisherDisplayName>Microsoft Corporation</PublisherDisplayName>
|
||||
<Logo>Assets\StoreLogo.png</Logo>
|
||||
</Properties>
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.0.0" MaxVersionTested="10.0.0.0" />
|
||||
</Dependencies>
|
||||
<Resources>
|
||||
<Resource Language="x-generate" />
|
||||
</Resources>
|
||||
<Applications>
|
||||
<Application Id="App" Executable="$targetnametoken$.exe" EntryPoint="BluetoothLEExplorer">
|
||||
<uap:VisualElements DisplayName="Bluetooth LE Explorer" Square150x150Logo="Assets\Square150x150Logo.png" Square44x44Logo="Assets\Square44x44Logo.png" Description="Sample" BackgroundColor="transparent">
|
||||
<uap:LockScreen Notification="badge" BadgeLogo="Assets\BadgeLogo.png" />
|
||||
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png" Square310x310Logo="Assets\Square310x310Logo.png" Square71x71Logo="Assets\Square71x71Logo.png">
|
||||
<uap:ShowNameOnTiles>
|
||||
<uap:ShowOn Tile="square310x310Logo" />
|
||||
</uap:ShowNameOnTiles>
|
||||
</uap:DefaultTile>
|
||||
<uap:SplashScreen Image="Assets\SplashScreen.png" />
|
||||
</uap:VisualElements>
|
||||
</Application>
|
||||
</Applications>
|
||||
<Capabilities>
|
||||
<Capability Name="internetClient" />
|
||||
<DeviceCapability Name="bluetooth" />
|
||||
</Capabilities>
|
||||
</Package>
|
|
@ -0,0 +1,33 @@
|
|||
// <copyright file="AssemblyInfo.cs" company="Microsoft Corporation">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//----------------------------------------------------------------------------------------------
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("BluetoothLEExplorer")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("Microsoft")]
|
||||
[assembly: AssemblyProduct("BluetoothLEExplorer")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2015")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
[assembly: ComVisible(false)]
|
|
@ -0,0 +1,31 @@
|
|||
<!--
|
||||
This file contains Runtime Directives used by .NET Native. The defaults here are suitable for most
|
||||
developers. However, you can modify these parameters to modify the behavior of the .NET Native
|
||||
optimizer.
|
||||
|
||||
Runtime Directives are documented at http://go.microsoft.com/fwlink/?LinkID=391919
|
||||
|
||||
To fully enable reflection for App1.MyClass and all of its public/private members
|
||||
<Type Name="App1.MyClass" Dynamic="Required All"/>
|
||||
|
||||
To enable dynamic creation of the specific instantiation of AppClass<T> over System.Int32
|
||||
<TypeInstantiation Name="App1.AppClass" Arguments="System.Int32" Activate="Required Public" />
|
||||
|
||||
Using the Namespace directive to apply reflection policy to all the types in a particular namespace
|
||||
<Namespace Name="DataClasses.ViewModels" Seralize="All" />
|
||||
-->
|
||||
|
||||
<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
|
||||
<Application>
|
||||
<!--
|
||||
An Assembly element with Name="*Application*" applies to all assemblies in
|
||||
the application package. The asterisks are not wildcards.
|
||||
-->
|
||||
<Assembly Name="*Application*" Dynamic="Required All" />
|
||||
|
||||
|
||||
<!-- Add your application specific runtime directives here. -->
|
||||
|
||||
|
||||
</Application>
|
||||
</Directives>
|
|
@ -0,0 +1,152 @@
|
|||
// <copyright file="Converters.cs" company="Microsoft Corporation">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//----------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using BluetoothLEExplorer.Models;
|
||||
using BluetoothLEExplorer.ViewModels;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Data;
|
||||
|
||||
[module: System.Diagnostics.CodeAnalysis.SuppressMessage(
|
||||
"StyleCop.CSharp.DocumentationRules",
|
||||
"SA1649:FileHeaderFileNameDocumentationMustMatchTypeName",
|
||||
Justification = "This is a helper file for Converters used in the xaml")]
|
||||
|
||||
[module: System.Diagnostics.CodeAnalysis.SuppressMessage(
|
||||
"Microsoft.StyleCop.CSharp.MaintainabilityRules",
|
||||
"SA1402:FileMayOnlyContainASingleClass",
|
||||
Justification = "This is a helper file for Converters used in the xaml")]
|
||||
|
||||
namespace BluetoothLEExplorer.Services.Converters
|
||||
{
|
||||
/// <summary>
|
||||
/// Converter to change <see cref="ObservableGattCharacteristics.DisplayTypes"/> to a boolean.
|
||||
/// This is used in to determine if a radio button is checked or not
|
||||
/// </summary>
|
||||
public class DisplayTypeToBooleanConverter : IValueConverter
|
||||
{
|
||||
/// <summary>
|
||||
/// Display type radio button converter
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="targetType"></param>
|
||||
/// <param name="parameter"></param>
|
||||
/// <param name="culture"></param>
|
||||
/// <returns>return value of display type radio button converter</returns>
|
||||
public object Convert(object value, Type targetType, object parameter, string culture)
|
||||
{
|
||||
Debug.WriteLine($"Convert: value = {value.ToString()}, {parameter.ToString()}");
|
||||
return value.ToString().Equals(parameter);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Display type radio button back converter
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="targetType"></param>
|
||||
/// <param name="parameter"></param>
|
||||
/// <param name="culture"></param>
|
||||
/// <returns>Return value of display type radio button back converter</returns>
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string culture)
|
||||
{
|
||||
Debug.Write($"ConvertBack: {value.ToString()}, ");
|
||||
ObservableGattCharacteristics.DisplayTypes type = ObservableGattCharacteristics.DisplayTypes.NotSet;
|
||||
if (value.Equals(true))
|
||||
{
|
||||
Debug.WriteLine("value true");
|
||||
object ret = Enum.Parse(type.GetType(), parameter as string);
|
||||
Debug.WriteLine($"Enum.Parse(type.GetType(), parameter as string) - {ret.ToString()}");
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.WriteLine("unsetting value");
|
||||
return DependencyProperty.UnsetValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts <see cref="CharacteristicPageViewModel.WriteTypes"/> to boolean.
|
||||
/// Used in radio buttons.
|
||||
/// </summary>
|
||||
public class WriteTypeToBooleanConverter : IValueConverter
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts WriteType to boolean used in radio buttons
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="targetType"></param>
|
||||
/// <param name="parameter"></param>
|
||||
/// <param name="culture"></param>
|
||||
/// <returns>Boolean if radio button should be checked</returns>
|
||||
public object Convert(object value, Type targetType, object parameter, string culture)
|
||||
{
|
||||
return value.ToString().Equals(parameter);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Radio button state of write type radio buttons to boolean
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="targetType"></param>
|
||||
/// <param name="parameter"></param>
|
||||
/// <param name="culture"></param>
|
||||
/// <returns>Write value of radio button</returns>
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string culture)
|
||||
{
|
||||
CharacteristicPageViewModel.WriteTypes type = CharacteristicPageViewModel.WriteTypes.NotSet;
|
||||
if (value.Equals(true))
|
||||
{
|
||||
return Enum.Parse(type.GetType(), parameter as string);
|
||||
}
|
||||
else
|
||||
{
|
||||
return DependencyProperty.UnsetValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uses a boolean to determine if text should be crossed out
|
||||
/// </summary>
|
||||
public class ShellFontConverter : IValueConverter
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts boolean value to strikethrough font
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="targetType"></param>
|
||||
/// <param name="parameter"></param>
|
||||
/// <param name="language"></param>
|
||||
/// <returns>Either a normal font or a strike through font</returns>
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if ((bool)value == true)
|
||||
{
|
||||
return Windows.UI.Text.TextDecorations.None;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Windows.UI.Text.TextDecorations.Strikethrough;
|
||||
}
|
||||
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Not implemented
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="targetType"></param>
|
||||
/// <param name="parameter"></param>
|
||||
/// <param name="language"></param>
|
||||
/// <returns>Not implemented return value</returns>
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
// <copyright file="DispatcherService.cs" company="Microsoft Corporation">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//----------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.UI.Core;
|
||||
|
||||
namespace BluetoothLEExplorer.Services.DispatcherService
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper class used to run functions on the UI thread
|
||||
/// </summary>
|
||||
public static class DispatcherService
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper function to run task on UI thread
|
||||
/// </summary>
|
||||
/// <typeparam name="T">return value of task</typeparam>
|
||||
/// <param name="dispatcher"></param>
|
||||
/// <param name="func"></param>
|
||||
/// <param name="priority"></param>
|
||||
/// <returns>UI thread task</returns>
|
||||
public static async Task<T> RunTaskAsync<T>(
|
||||
this CoreDispatcher dispatcher,
|
||||
Func<Task<T>> func,
|
||||
CoreDispatcherPriority priority = CoreDispatcherPriority.Normal)
|
||||
{
|
||||
var taskCompletionSource = new TaskCompletionSource<T>();
|
||||
|
||||
await dispatcher.RunAsync(
|
||||
priority,
|
||||
async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
taskCompletionSource.SetResult(await func());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
taskCompletionSource.SetException(ex);
|
||||
}
|
||||
});
|
||||
return await taskCompletionSource.Task;
|
||||
}
|
||||
|
||||
//// There is no TaskCompletionSource<void> so we use a bool that we throw away.
|
||||
|
||||
/// <summary>
|
||||
/// Helper function to run task on UI thread
|
||||
/// </summary>
|
||||
/// <param name="dispatcher"></param>
|
||||
/// <param name="func"></param>
|
||||
/// <param name="priority"></param>
|
||||
/// <returns>UI thread task</returns>
|
||||
public static async Task RunTaskAsync(
|
||||
this CoreDispatcher dispatcher,
|
||||
Func<Task> func,
|
||||
CoreDispatcherPriority priority = CoreDispatcherPriority.Normal) =>
|
||||
await RunTaskAsync(
|
||||
dispatcher,
|
||||
async () =>
|
||||
{
|
||||
await func();
|
||||
return false;
|
||||
},
|
||||
priority);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,221 @@
|
|||
// <copyright file="GattUuidsService.cs" company="Microsoft Corporation">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//----------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
|
||||
namespace BluetoothLEExplorer.Services.GattUuidsService
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper class used when working with UUIDs
|
||||
/// </summary>
|
||||
public static class GattUuidsService
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper function to convert a UUID to a name
|
||||
/// </summary>
|
||||
/// <param name="uuid"></param>
|
||||
/// <returns>Name of the UUID</returns>
|
||||
public static string ConvertUuidToName(Guid uuid)
|
||||
{
|
||||
GattNativeUuid name;
|
||||
if (Enum.TryParse(ConvertUuidToShortId(uuid).ToString(), out name) == true)
|
||||
{
|
||||
return name.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
return uuid.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts from standard 128bit UUID to the assigned 32bit UUIDs. Makes it easy to compare services
|
||||
/// that devices expose to the standard list.
|
||||
/// </summary>
|
||||
/// <param name="uuid">UUID to convert to 32 bit</param>
|
||||
/// <returns>32bit version of the input UUID</returns>
|
||||
public static ushort ConvertUuidToShortId(Guid uuid)
|
||||
{
|
||||
// Get the short Uuid
|
||||
var bytes = uuid.ToByteArray();
|
||||
var shortUuid = (ushort)(bytes[0] | (bytes[1] << 8));
|
||||
return shortUuid;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This enum assists in finding a string representation of a BT SIG assigned value for UUIDS
|
||||
/// Reference: https://developer.bluetooth.org/gatt/services/Pages/ServicesHome.aspx
|
||||
/// Reference: https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicsHome.aspx
|
||||
/// Reference: https://developer.bluetooth.org/gatt/descriptors/Pages/DescriptorsHomePage.aspx
|
||||
/// </summary>
|
||||
public enum GattNativeUuid : ushort
|
||||
{
|
||||
/// <summary>
|
||||
/// This enum assists in finding a string representation of a BT SIG assigned value for Service UUIDS
|
||||
/// Reference: https://developer.bluetooth.org/gatt/services/Pages/ServicesHome.aspx
|
||||
/// </summary>
|
||||
None = 0,
|
||||
AlertNotificationService = 0x1811,
|
||||
AutomationIO = 0x1815,
|
||||
BatteryService = 0x180F,
|
||||
BloodPressure = 0x1810,
|
||||
BodyComposition = 0x181B,
|
||||
BondManagement = 0x181E,
|
||||
ContinuousGlucoseMonitoring = 0x181F,
|
||||
CurrentTimeService = 0x1805,
|
||||
CyclingPower = 0x1818,
|
||||
CyclingSpeedandCadence = 0x1816,
|
||||
DeviceInformation = 0x180A,
|
||||
EnvironmentalSensing = 0x181A,
|
||||
GenericAccess = 0x1800,
|
||||
GenericAttribute = 0x1801,
|
||||
Glucose = 0x1808,
|
||||
HealthThermometer = 0x1809,
|
||||
HeartRate = 0x180D,
|
||||
HTTPProxy = 0x1823,
|
||||
HumanInterfaceDevice = 0x1812,
|
||||
ImmediateAlert = 0x1802,
|
||||
IndoorPositioning = 0x1821,
|
||||
InternetProtocolSupport = 0x1820,
|
||||
LinkLoss = 0x1803,
|
||||
LocationandNavigation = 0x1819,
|
||||
NextDSTChangeService = 0x1807,
|
||||
ObjectTransfer = 0x1825,
|
||||
PhoneAlertStatusService = 0x180E,
|
||||
PulseOximeter = 0x1822,
|
||||
ReferenceTimeUpdateService = 0x1806,
|
||||
RunningSpeedandCadence = 0x1814,
|
||||
ScanParameters = 0x1813,
|
||||
TransportDiscovery = 0x1824,
|
||||
TxPower = 0x1804,
|
||||
UserData = 0x181C,
|
||||
WeightScale = 0x181D,
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// This enum is nice for finding a string representation of a BT SIG assigned value for Characteristic UUIDs
|
||||
/// Reference: https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicsHome.aspx
|
||||
/// </summary>
|
||||
AlertCategoryID = 0x2A43,
|
||||
AlertCategoryIDBitMask = 0x2A42,
|
||||
AlertLevel = 0x2A06,
|
||||
AlertNotificationControlPoint = 0x2A44,
|
||||
AlertStatus = 0x2A3F,
|
||||
Appearance = 0x2A01,
|
||||
BatteryLevel = 0x2A19,
|
||||
BloodPressureFeature = 0x2A49,
|
||||
BloodPressureMeasurement = 0x2A35,
|
||||
BodySensorLocation = 0x2A38,
|
||||
BootKeyboardInputReport = 0x2A22,
|
||||
BootKeyboardOutputReport = 0x2A32,
|
||||
BootMouseInputReport = 0x2A33,
|
||||
CSCFeature = 0x2A5C,
|
||||
CSCMeasurement = 0x2A5B,
|
||||
CurrentTime = 0x2A2B,
|
||||
DateTime = 0x2A08,
|
||||
DayDateTime = 0x2A0A,
|
||||
DayofWeek = 0x2A09,
|
||||
DeviceName = 0x2A00,
|
||||
DSTOffset = 0x2A0D,
|
||||
ExactTime256 = 0x2A0C,
|
||||
FirmwareRevisionString = 0x2A26,
|
||||
GlucoseFeature = 0x2A51,
|
||||
GlucoseMeasurement = 0x2A18,
|
||||
GlucoseMeasurementContext = 0x2A34,
|
||||
HardwareRevisionString = 0x2A27,
|
||||
HeartRateControlPoint = 0x2A39,
|
||||
HeartRateMeasurement = 0x2A37,
|
||||
HIDControlPoint = 0x2A4C,
|
||||
HIDInformation = 0x2A4A,
|
||||
IEEE11073_20601RegulatoryCertificationDataList = 0x2A2A,
|
||||
IntermediateCuffPressure = 0x2A36,
|
||||
IntermediateTemperature = 0x2A1E,
|
||||
LocalTimeInformation = 0x2A0F,
|
||||
ManufacturerNameString = 0x2A29,
|
||||
MeasurementInterval = 0x2A21,
|
||||
ModelNumberString = 0x2A24,
|
||||
NewAlert = 0x2A46,
|
||||
PeripheralPreferredConnectionParameters = 0x2A04,
|
||||
PeripheralPrivacyFlag = 0x2A02,
|
||||
PnPID = 0x2A50,
|
||||
ProtocolMode = 0x2A4E,
|
||||
ReconnectionAddress = 0x2A03,
|
||||
RecordAccessControlPoint = 0x2A52,
|
||||
ReferenceTimeInformation = 0x2A14,
|
||||
Report = 0x2A4D,
|
||||
ReportMap = 0x2A4B,
|
||||
RingerControlPoint = 0x2A40,
|
||||
RingerSetting = 0x2A41,
|
||||
RSCFeature = 0x2A54,
|
||||
RSCMeasurement = 0x2A53,
|
||||
SCControlPoint = 0x2A55,
|
||||
ScanIntervalWindow = 0x2A4F,
|
||||
ScanRefresh = 0x2A31,
|
||||
SensorLocation = 0x2A5D,
|
||||
SerialNumberString = 0x2A25,
|
||||
ServiceChanged = 0x2A05,
|
||||
SoftwareRevisionString = 0x2A28,
|
||||
SupportedNewAlertCategory = 0x2A47,
|
||||
SupportedUnreadAlertCategory = 0x2A48,
|
||||
SystemID = 0x2A23,
|
||||
TemperatureMeasurement = 0x2A1C,
|
||||
TemperatureType = 0x2A1D,
|
||||
TimeAccuracy = 0x2A12,
|
||||
TimeSource = 0x2A13,
|
||||
TimeUpdateControlPoint = 0x2A16,
|
||||
TimeUpdateState = 0x2A17,
|
||||
TimewithDST = 0x2A11,
|
||||
TimeZone = 0x2A0E,
|
||||
TxPowerLevel = 0x2A07,
|
||||
UnreadAlertStatus = 0x2A45,
|
||||
AggregateInput = 0x2A5A,
|
||||
AnalogInput = 0x2A58,
|
||||
AnalogOutput = 0x2A59,
|
||||
CyclingPowerControlPoint = 0x2A66,
|
||||
CyclingPowerFeature = 0x2A65,
|
||||
CyclingPowerMeasurement = 0x2A63,
|
||||
CyclingPowerVector = 0x2A64,
|
||||
DigitalInput = 0x2A56,
|
||||
DigitalOutput = 0x2A57,
|
||||
ExactTime100 = 0x2A0B,
|
||||
LNControlPoint = 0x2A6B,
|
||||
LNFeature = 0x2A6A,
|
||||
LocationandSpeed = 0x2A67,
|
||||
Navigation = 0x2A68,
|
||||
NetworkAvailability = 0x2A3E,
|
||||
PositionQuality = 0x2A69,
|
||||
ScientificTemperatureinCelsius = 0x2A3C,
|
||||
SecondaryTimeZone = 0x2A10,
|
||||
String = 0x2A3D,
|
||||
TemperatureinCelsius = 0x2A1F,
|
||||
TemperatureinFahrenheit = 0x2A20,
|
||||
TimeBroadcast = 0x2A15,
|
||||
BatteryLevelState = 0x2A1B,
|
||||
BatteryPowerState = 0x2A1A,
|
||||
PulseOximetryContinuousMeasurement = 0x2A5F,
|
||||
PulseOximetryControlPoint = 0x2A62,
|
||||
PulseOximetryFeatures = 0x2A61,
|
||||
PulseOximetryPulsatileEvent = 0x2A60,
|
||||
SimpleKeyState = 0xFFE1,
|
||||
|
||||
/// <summary>
|
||||
/// This enum assists in finding a string representation of a BT SIG assigned value for Descriptor UUIDs
|
||||
/// Reference: https://developer.bluetooth.org/gatt/descriptors/Pages/DescriptorsHomePage.aspx
|
||||
/// </summary>
|
||||
CharacteristicExtendedProperties = 0x2900,
|
||||
CharacteristicUserDescription = 0x2901,
|
||||
ClientCharacteristicConfiguration = 0x2902,
|
||||
ServerCharacteristicConfiguration = 0x2903,
|
||||
CharacteristicPresentationFormat = 0x2904,
|
||||
CharacteristicAggregateFormat = 0x2905,
|
||||
ValidRange = 0x2906,
|
||||
ExternalReportReference = 0x2907,
|
||||
ReportReference = 0x2908
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
// <copyright file="BytePadder.cs" company="Microsoft Corporation">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//----------------------------------------------------------------------------------------------
|
||||
|
||||
namespace BluetoothLEExplorer.Services.Other
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper class used to pad bytes
|
||||
/// </summary>
|
||||
public static class BytePadder
|
||||
{
|
||||
/// <summary>
|
||||
/// Takes an input array of bytes and returns an array with more zeros in the front
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <param name="length"></param>
|
||||
/// <returns>A byte array with more zeros in front"/></returns>
|
||||
public static byte[] GetBytes(byte[] input, int length)
|
||||
{
|
||||
byte[] ret = new byte[length];
|
||||
|
||||
if (input.Length >= length)
|
||||
{
|
||||
return input;
|
||||
}
|
||||
|
||||
for (int i = 0; i < input.Length; i++)
|
||||
{
|
||||
ret[i] = input[i];
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
// <copyright file="GattProtocolErrorParser.cs" company="Microsoft Corporation">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//----------------------------------------------------------------------------------------------
|
||||
using Windows.Devices.Bluetooth.GenericAttributeProfile;
|
||||
|
||||
namespace BluetoothLEExplorer.Services.Other
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper function when working with <see cref="GattProtocolError"/>
|
||||
/// </summary>
|
||||
public static class GattProtocolErrorParser
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper to convert an gatt error value into a string
|
||||
/// </summary>
|
||||
/// <param name="errorValue"></param>
|
||||
/// <returns>String representation of the error</returns>
|
||||
public static string GetErrorString(byte? errorValue)
|
||||
{
|
||||
string ret = "Protocol Error";
|
||||
|
||||
if (errorValue.HasValue == false)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (errorValue == GattProtocolError.AttributeNotFound)
|
||||
{
|
||||
return "Attribute Not Found";
|
||||
}
|
||||
else if (errorValue == GattProtocolError.AttributeNotLong)
|
||||
{
|
||||
return "Attribute Not Long";
|
||||
}
|
||||
else if (errorValue == GattProtocolError.InsufficientAuthentication)
|
||||
{
|
||||
return "Insufficient Authentication";
|
||||
}
|
||||
else if (errorValue == GattProtocolError.InsufficientAuthorization)
|
||||
{
|
||||
return "Insufficient Authorization";
|
||||
}
|
||||
else if (errorValue == GattProtocolError.InsufficientEncryption)
|
||||
{
|
||||
return "Insufficient Encryption";
|
||||
}
|
||||
else if (errorValue == GattProtocolError.InsufficientEncryptionKeySize)
|
||||
{
|
||||
return "Insufficient Encryption Key Size";
|
||||
}
|
||||
else if (errorValue == GattProtocolError.InsufficientResources)
|
||||
{
|
||||
return "Insufficient Resources";
|
||||
}
|
||||
else if (errorValue == GattProtocolError.InvalidAttributeValueLength)
|
||||
{
|
||||
return "Invalid Attribute Value Length";
|
||||
}
|
||||
else if (errorValue == GattProtocolError.InvalidHandle)
|
||||
{
|
||||
return "Invalid Handle";
|
||||
}
|
||||
else if (errorValue == GattProtocolError.InvalidOffset)
|
||||
{
|
||||
return "Invalid Offset";
|
||||
}
|
||||
else if (errorValue == GattProtocolError.InvalidPdu)
|
||||
{
|
||||
return "Invalid Pdu";
|
||||
}
|
||||
else if (errorValue == GattProtocolError.PrepareQueueFull)
|
||||
{
|
||||
return "Prepare Queue Full";
|
||||
}
|
||||
else if (errorValue == GattProtocolError.ReadNotPermitted)
|
||||
{
|
||||
return "Read Not Permitted";
|
||||
}
|
||||
else if (errorValue == GattProtocolError.RequestNotSupported)
|
||||
{
|
||||
return "Request Not Supported";
|
||||
}
|
||||
else if (errorValue == GattProtocolError.UnlikelyError)
|
||||
{
|
||||
return "UnlikelyError";
|
||||
}
|
||||
else if (errorValue == GattProtocolError.UnsupportedGroupType)
|
||||
{
|
||||
return "Unsupported Group Type";
|
||||
}
|
||||
else if (errorValue == GattProtocolError.WriteNotPermitted)
|
||||
{
|
||||
return "Write Not Permitted";
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
// <copyright file="SettingsService.cs" company="Microsoft Corporation">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//----------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using Template10.Common;
|
||||
using Template10.Utils;
|
||||
using Windows.UI.Xaml;
|
||||
|
||||
namespace BluetoothLEExplorer.Services.SettingsServices
|
||||
{
|
||||
/// <summary>
|
||||
/// Settings helper service
|
||||
/// </summary>
|
||||
public class SettingsService
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the settings service
|
||||
/// </summary>
|
||||
public static SettingsService Instance { get; } = new SettingsService();
|
||||
|
||||
/// <summary>
|
||||
/// The settings helper
|
||||
/// </summary>
|
||||
private Template10.Services.SettingsService.ISettingsHelper helper;
|
||||
|
||||
/// <summary>
|
||||
/// Prevents a default instance of the<see cref="SettingsService" /> class from being created.
|
||||
/// </summary>
|
||||
private SettingsService()
|
||||
{
|
||||
helper = new Template10.Services.SettingsService.SettingsHelper();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the shell screen should have a back button
|
||||
/// </summary>
|
||||
public bool UseShellBackButton
|
||||
{
|
||||
get
|
||||
{
|
||||
return helper.Read<bool>(nameof(UseShellBackButton), true);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
helper.Write(nameof(UseShellBackButton), value);
|
||||
BootStrapper.Current.NavigationService.Dispatcher.Dispatch(() =>
|
||||
{
|
||||
BootStrapper.Current.ShowShellBackButton = value;
|
||||
BootStrapper.Current.UpdateShellBackButton();
|
||||
BootStrapper.Current.NavigationService.Refresh();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the application theme
|
||||
/// </summary>
|
||||
public ApplicationTheme AppTheme
|
||||
{
|
||||
get
|
||||
{
|
||||
var theme = ApplicationTheme.Light;
|
||||
var value = helper.Read<string>(nameof(AppTheme), theme.ToString());
|
||||
return Enum.TryParse<ApplicationTheme>(value, out theme) ? theme : ApplicationTheme.Dark;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
helper.Write(nameof(AppTheme), value.ToString());
|
||||
(Window.Current.Content as FrameworkElement).RequestedTheme = value.ToElementTheme();
|
||||
Views.Shell.HamburgerMenu.RefreshStyles(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value that I don't know what it does
|
||||
/// </summary>
|
||||
public TimeSpan CacheMaxDuration
|
||||
{
|
||||
get
|
||||
{
|
||||
return helper.Read<TimeSpan>(nameof(CacheMaxDuration), TimeSpan.FromDays(2));
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
helper.Write(nameof(CacheMaxDuration), value);
|
||||
BootStrapper.Current.CacheMaxDuration = value;
|
||||
}
|
||||
}
|
||||
|
||||
public ObservableDictionary<string, object> SettingsDictionary = new ObservableDictionary<string, object>();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
// <copyright file="ToastService.cs" company="Microsoft Corporation">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//----------------------------------------------------------------------------------------------
|
||||
using System.Text;
|
||||
using Windows.Data.Xml.Dom;
|
||||
using Windows.Foundation.Collections;
|
||||
using Windows.Foundation.Metadata;
|
||||
using Windows.UI.Notifications;
|
||||
|
||||
namespace BluetoothLEExplorer.Services.ToastService
|
||||
{
|
||||
/// <summary>
|
||||
/// Service to help displaying toast notifications
|
||||
/// </summary>
|
||||
public static class ToastService
|
||||
{
|
||||
/// <summary>
|
||||
/// Pop up a toast
|
||||
/// </summary>
|
||||
/// <param name="title"></param>
|
||||
/// <param name="content"></param>
|
||||
/// <returns>A toast notification</returns>
|
||||
public static ToastNotification PopToast(string title, string content)
|
||||
{
|
||||
return PopToast(title, content, null, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pop up a toast
|
||||
/// </summary>
|
||||
/// <param name="title"></param>
|
||||
/// <param name="content"></param>
|
||||
/// <param name="tag"></param>
|
||||
/// <param name="group"></param>
|
||||
/// <returns>A toast notification</returns>
|
||||
public static ToastNotification PopToast(string title, string content, string tag, string group)
|
||||
{
|
||||
string xml = $@"<toast activationType='foreground'>
|
||||
<visual>
|
||||
<binding template='ToastGeneric'>
|
||||
</binding>
|
||||
</visual>
|
||||
</toast>";
|
||||
|
||||
XmlDocument doc = new XmlDocument();
|
||||
|
||||
doc.LoadXml(xml);
|
||||
|
||||
var binding = doc.SelectSingleNode("//binding");
|
||||
|
||||
var el = doc.CreateElement("text");
|
||||
el.InnerText = title;
|
||||
|
||||
binding.AppendChild(el);
|
||||
|
||||
el = doc.CreateElement("text");
|
||||
|
||||
try
|
||||
{
|
||||
el.InnerText = content;
|
||||
}
|
||||
catch (System.Runtime.InteropServices.COMException)
|
||||
{
|
||||
el.InnerText = "Undisplayable UTF8 character";
|
||||
}
|
||||
|
||||
binding.AppendChild(el);
|
||||
|
||||
return PopCustomToast(doc, tag, group);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pop up a task
|
||||
/// </summary>
|
||||
/// <param name="xml"></param>
|
||||
/// <returns>A toast notification</returns>
|
||||
public static ToastNotification PopCustomToast(string xml)
|
||||
{
|
||||
return PopCustomToast(xml, null, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pop up a custom toast
|
||||
/// </summary>
|
||||
/// <param name="xml"></param>
|
||||
/// <param name="tag"></param>
|
||||
/// <param name="group"></param>
|
||||
/// <returns>A toast notification</returns>
|
||||
public static ToastNotification PopCustomToast(string xml, string tag, string group)
|
||||
{
|
||||
XmlDocument doc = new XmlDocument();
|
||||
doc.LoadXml(xml);
|
||||
|
||||
return PopCustomToast(doc, tag, group);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// pop up a custom toast
|
||||
/// </summary>
|
||||
/// <param name="doc"></param>
|
||||
/// <param name="tag"></param>
|
||||
/// <param name="group"></param>
|
||||
/// <returns>A toast notification</returns>
|
||||
[DefaultOverloadAttribute]
|
||||
public static ToastNotification PopCustomToast(XmlDocument doc, string tag, string group)
|
||||
{
|
||||
var toast = new ToastNotification(doc);
|
||||
|
||||
if (tag != null)
|
||||
{
|
||||
toast.Tag = tag;
|
||||
}
|
||||
|
||||
if (group != null)
|
||||
{
|
||||
toast.Group = group;
|
||||
}
|
||||
|
||||
ToastNotificationManager.CreateToastNotifier().Show(toast);
|
||||
|
||||
return toast;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<StyleCopSettings Version="105">
|
||||
<GlobalSettings>
|
||||
<CollectionProperty Name="RecognizedWords">
|
||||
<Value>aspx</Value>
|
||||
</CollectionProperty>
|
||||
</GlobalSettings>
|
||||
<Analyzers>
|
||||
<Analyzer AnalyzerId="StyleCop.CSharp.ReadabilityRules">
|
||||
<Rules>
|
||||
<Rule Name="DoNotPlaceRegionsWithinElements">
|
||||
<RuleSettings>
|
||||
<BooleanProperty Name="Enabled">False</BooleanProperty>
|
||||
</RuleSettings>
|
||||
</Rule>
|
||||
</Rules>
|
||||
<AnalyzerSettings />
|
||||
</Analyzer>
|
||||
</Analyzers>
|
||||
</StyleCopSettings>
|
|
@ -0,0 +1,123 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:Template10.Controls">
|
||||
|
||||
<x:Double x:Key="NarrowMinWidth">0</x:Double>
|
||||
<x:Double x:Key="NormalMinWidth">521</x:Double>
|
||||
<x:Double x:Key="WideMinWidth">1200</x:Double>
|
||||
|
||||
<x:Double x:Key="MinDisplayTypeWidth">820</x:Double>
|
||||
|
||||
<Color x:Key="CustomColor">#05A6F0</Color>
|
||||
<Color x:Key="ContrastColor">Black</Color>
|
||||
<Color x:Key="SystemAccentColor">#05A6F0</Color>
|
||||
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
|
||||
<ResourceDictionary x:Key="Light">
|
||||
|
||||
<SolidColorBrush x:Key="CustomColorBrush" Color="{StaticResource SystemAccentColor}" />
|
||||
<SolidColorBrush x:Key="ContrastColorBrush" Color="{StaticResource ContrastColor}" />
|
||||
<SolidColorBrush x:Key="ExtendedSplashBackground" Color="{StaticResource CustomColor}" />
|
||||
<SolidColorBrush x:Key="ExtendedSplashForeground" Color="{StaticResource ContrastColor}" />
|
||||
|
||||
<Style TargetType="controls:HamburgerMenu">
|
||||
<Setter Property="AccentColor" Value="{StaticResource CustomColor}" />
|
||||
<Setter Property="VisualStateNarrowMinWidth" Value="{StaticResource NarrowMinWidth}" />
|
||||
<Setter Property="VisualStateNormalMinWidth" Value="{StaticResource NormalMinWidth}" />
|
||||
<Setter Property="VisualStateWideMinWidth" Value="{StaticResource WideMinWidth}" />
|
||||
</Style>
|
||||
|
||||
<Style TargetType="controls:PageHeader">
|
||||
<Setter Property="Background" Value="{ThemeResource CustomColorBrush}" />
|
||||
<Setter Property="Foreground" Value="{ThemeResource ContrastColorBrush}" />
|
||||
<Setter Property="VisualStateNarrowMinWidth" Value="{StaticResource NarrowMinWidth}" />
|
||||
<Setter Property="VisualStateNormalMinWidth" Value="{StaticResource NormalMinWidth}" />
|
||||
</Style>
|
||||
|
||||
<Style TargetType="controls:Resizer">
|
||||
<Setter Property="GrabberBrush" Value="{ThemeResource CustomColorBrush}" />
|
||||
<Setter Property="GrabberVisibility" Value="Visible" />
|
||||
</Style>
|
||||
|
||||
</ResourceDictionary>
|
||||
|
||||
<ResourceDictionary x:Key="Default">
|
||||
|
||||
<SolidColorBrush x:Key="CustomColorBrush" Color="{StaticResource SystemAccentColor}" />
|
||||
<SolidColorBrush x:Key="ContrastColorBrush" Color="{StaticResource ContrastColor}" />
|
||||
<SolidColorBrush x:Key="ExtendedSplashBackground" Color="{StaticResource CustomColor}" />
|
||||
<SolidColorBrush x:Key="ExtendedSplashForeground" Color="{StaticResource ContrastColor}" />
|
||||
|
||||
<Style TargetType="controls:HamburgerMenu">
|
||||
|
||||
<!--
|
||||
|
||||
Windows 10 2015 (1511) Hamburger Menu Style
|
||||
Simply replace AccentColor with the follow setters.
|
||||
|
||||
<Setter Property="HamburgerBackground" Value="{StaticResource CustomColorBrush}" />
|
||||
<Setter Property="HamburgerForeground" Value="White" />
|
||||
<Setter Property="NavAreaBackground" Value="#FF2C2C2C" />
|
||||
<Setter Property="NavButtonForeground" Value="White" />
|
||||
<Setter Property="NavButtonBackground" Value="Transparent" />
|
||||
<Setter Property="NavButtonHoverForeground" Value="White" />
|
||||
<Setter Property="NavButtonHoverBackground" Value="#FF585858" />
|
||||
<Setter Property="NavButtonPressedForeground" Value="White" />
|
||||
<Setter Property="NavButtonPressedBackground" Value="#FF848484" />
|
||||
<Setter Property="NavButtonCheckedForeground" Value="White" />
|
||||
<Setter Property="NavButtonCheckedBackground" Value="#FF2A4E6C" />
|
||||
<Setter Property="NavButtonCheckedIndicatorBrush" Value="Transparent" />
|
||||
|
||||
-->
|
||||
|
||||
<Setter Property="AccentColor" Value="{StaticResource CustomColor}" />
|
||||
<Setter Property="VisualStateNarrowMinWidth" Value="{StaticResource NarrowMinWidth}" />
|
||||
<Setter Property="VisualStateNormalMinWidth" Value="{StaticResource NormalMinWidth}" />
|
||||
<Setter Property="VisualStateWideMinWidth" Value="{StaticResource WideMinWidth}" />
|
||||
</Style>
|
||||
|
||||
<Style TargetType="controls:PageHeader">
|
||||
<Setter Property="Background" Value="{ThemeResource CustomColorBrush}" />
|
||||
<Setter Property="Foreground" Value="{ThemeResource ContrastColorBrush}" />
|
||||
<Setter Property="VisualStateNarrowMinWidth" Value="{StaticResource NarrowMinWidth}" />
|
||||
<Setter Property="VisualStateNormalMinWidth" Value="{StaticResource NormalMinWidth}" />
|
||||
</Style>
|
||||
|
||||
<Style TargetType="controls:Resizer">
|
||||
<Setter Property="GrabberBrush" Value="{ThemeResource CustomColorBrush}" />
|
||||
<Setter Property="GrabberVisibility" Value="Visible" />
|
||||
</Style>
|
||||
|
||||
</ResourceDictionary>
|
||||
|
||||
<ResourceDictionary x:Key="HighContrast">
|
||||
|
||||
<SolidColorBrush x:Key="ExtendedSplashBackground" Color="Black" />
|
||||
<SolidColorBrush x:Key="ExtendedSplashForeground" Color="White" />
|
||||
|
||||
<Style TargetType="controls:HamburgerMenu">
|
||||
<Setter Property="PaneBorderThickness" Value="0" />
|
||||
<Setter Property="SecondarySeparator" Value="{ThemeResource SystemColorWindowTextColor}" />
|
||||
<Setter Property="NavButtonBackground" Value="{ThemeResource SystemColorWindowColor}" />
|
||||
<Setter Property="NavButtonForeground" Value="{ThemeResource SystemColorWindowTextColor}" />
|
||||
<Setter Property="NavAreaBackground" Value="{ThemeResource SystemColorWindowColor}" />
|
||||
<Setter Property="HamburgerForeground" Value="{ThemeResource SystemColorWindowColor}" />
|
||||
<Setter Property="HamburgerBackground" Value="{ThemeResource SystemColorWindowTextColor}" />
|
||||
<Setter Property="VisualStateNarrowMinWidth" Value="{ThemeResource NarrowMinWidth}" />
|
||||
<Setter Property="VisualStateNormalMinWidth" Value="{ThemeResource NormalMinWidth}" />
|
||||
<Setter Property="VisualStateWideMinWidth" Value="{ThemeResource WideMinWidth}" />
|
||||
</Style>
|
||||
|
||||
<Style TargetType="controls:PageHeader">
|
||||
<Setter Property="Background" Value="{ThemeResource SystemColorWindowColor}" />
|
||||
<Setter Property="Foreground" Value="{ThemeResource SystemColorWindowTextColor}" />
|
||||
<Setter Property="VisualStateNarrowMinWidth" Value="{ThemeResource NarrowMinWidth}" />
|
||||
<Setter Property="VisualStateNormalMinWidth" Value="{ThemeResource NormalMinWidth}" />
|
||||
</Style>
|
||||
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
</ResourceDictionary>
|
||||
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
// <copyright file="BeaconViewModel.cs" company="Microsoft Corporation">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//----------------------------------------------------------------------------------------------
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Template10.Mvvm;
|
||||
using Template10.Services.NavigationService;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
|
||||
namespace BluetoothLEExplorer.ViewModels
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BeaconViewModel" /> class. Currently unused.
|
||||
/// </summary>
|
||||
public class BeaconViewModel : ViewModelBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BeaconViewModel" /> class.
|
||||
/// </summary>
|
||||
public BeaconViewModel()
|
||||
{
|
||||
if (Windows.ApplicationModel.DesignMode.DesignModeEnabled)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigate to page
|
||||
/// </summary>
|
||||
/// <param name="parameter"></param>
|
||||
/// <param name="mode"></param>
|
||||
/// <param name="suspensionState"></param>
|
||||
/// <returns>Navigate to task</returns>
|
||||
public override async Task OnNavigatedToAsync(object parameter, NavigationMode mode, IDictionary<string, object> suspensionState)
|
||||
{
|
||||
if (suspensionState.Any())
|
||||
{
|
||||
}
|
||||
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigate from page
|
||||
/// </summary>
|
||||
/// <param name="suspensionState"></param>
|
||||
/// <param name="suspending"></param>
|
||||
/// <returns>Navigate from task</returns>
|
||||
public override async Task OnNavigatedFromAsync(IDictionary<string, object> suspensionState, bool suspending)
|
||||
{
|
||||
if (suspending)
|
||||
{
|
||||
}
|
||||
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigate from page
|
||||
/// </summary>
|
||||
/// <param name="args"></param>
|
||||
/// <returns>Navigate from task</returns>
|
||||
public override async Task OnNavigatingFromAsync(NavigatingEventArgs args)
|
||||
{
|
||||
args.Cancel = false;
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Go to settings
|
||||
/// </summary>
|
||||
public void GotoSettings() =>
|
||||
NavigationService.Navigate(typeof(Views.SettingsPage), 0);
|
||||
|
||||
/// <summary>
|
||||
/// Go to privacy
|
||||
/// </summary>
|
||||
public void GotoPrivacy() =>
|
||||
NavigationService.Navigate(typeof(Views.SettingsPage), 1);
|
||||
|
||||
/// <summary>
|
||||
/// Go to about
|
||||
/// </summary>
|
||||
public void GotoAbout() =>
|
||||
NavigationService.Navigate(typeof(Views.SettingsPage), 2);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,714 @@
|
|||
// <copyright file="CharacteristicPageViewModel.cs" company="Microsoft Corporation">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//----------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using BluetoothLEExplorer.Models;
|
||||
using Template10.Mvvm;
|
||||
using Template10.Services.NavigationService;
|
||||
using Windows.Devices.Bluetooth.GenericAttributeProfile;
|
||||
using Windows.Security.Cryptography;
|
||||
using Windows.Storage.Streams;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
using Windows.UI.Popups;
|
||||
using GattHelper.Converters;
|
||||
|
||||
namespace BluetoothLEExplorer.ViewModels
|
||||
{
|
||||
/// <summary>
|
||||
/// View model for Characteristics View
|
||||
/// </summary>
|
||||
public class CharacteristicPageViewModel : ViewModelBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Enum to determine how to write the <see cref="ValueToWrite"/> to the server
|
||||
/// </summary>
|
||||
public enum WriteTypes
|
||||
{
|
||||
NotSet,
|
||||
Decimal,
|
||||
Hex,
|
||||
UTF8
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// App context
|
||||
/// </summary>
|
||||
private GattSampleContext context = GattSampleContext.Context;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets strings to show user
|
||||
/// </summary>
|
||||
public ObservableCollection<string> NotifyUser { get; set; } = new ObservableCollection<string>();
|
||||
|
||||
/// <summary>
|
||||
/// True if currently settings the error message
|
||||
/// </summary>
|
||||
private bool settingErrorMessage = false;
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="Characteristic"/>
|
||||
/// </summary>
|
||||
private ObservableGattCharacteristics characteristic = GattSampleContext.Context.SelectedCharacteristic;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the characteristic that this view model wraps
|
||||
/// </summary>
|
||||
public ObservableGattCharacteristics Characteristic
|
||||
{
|
||||
get
|
||||
{
|
||||
return characteristic;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
Set(ref characteristic, value, "Characteristic");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="Properties"/>
|
||||
/// </summary>
|
||||
private string properties = "None";
|
||||
|
||||
/// <summary>
|
||||
/// Gets the string showing what properties are supported
|
||||
/// </summary>
|
||||
public string Properties
|
||||
{
|
||||
get
|
||||
{
|
||||
return properties;
|
||||
}
|
||||
|
||||
private set
|
||||
{
|
||||
Set(ref properties, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="Notify"/>
|
||||
/// </summary>
|
||||
private bool notify = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether notify is supported by this characteristic
|
||||
/// </summary>
|
||||
public bool Notify
|
||||
{
|
||||
get
|
||||
{
|
||||
return notify;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (notify == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// The heavy lifting for writting the CCCD is done in the PropertyChanged method
|
||||
// in this class that gets called when this property is actually changed.
|
||||
|
||||
Set(ref notify, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="NotifyProgress"/>
|
||||
/// </summary>
|
||||
private bool notifyProgress = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the progress ring should be displayed
|
||||
/// while the notify descriptor is written
|
||||
/// </summary>
|
||||
public bool NotifyProgress
|
||||
{
|
||||
get
|
||||
{
|
||||
return notifyProgress;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (notifyProgress == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Set(ref notifyProgress, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="NotifyError"/>
|
||||
/// </summary>
|
||||
private bool notifyError = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether there was an error setting the notify descriptor
|
||||
/// </summary>
|
||||
public bool NotifyError
|
||||
{
|
||||
get
|
||||
{
|
||||
return notifyError;
|
||||
}
|
||||
|
||||
private set
|
||||
{
|
||||
if (notifyError == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Set(ref notifyError, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="Indicate"/>
|
||||
/// </summary>
|
||||
private bool indicate = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether indicate is supported by this characteristic
|
||||
/// </summary>
|
||||
public bool Indicate
|
||||
{
|
||||
get
|
||||
{
|
||||
return indicate;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (indicate == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// The heavy lifting for writting the CCCD is done in the PropertyChanged method
|
||||
// in this class that gets called when this property is actually changed.
|
||||
|
||||
Set(ref indicate, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="IndicateProgress"/>
|
||||
/// </summary>
|
||||
private bool indicateProgress = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the progress ring should be displayed
|
||||
/// while the indicate descriptor is written
|
||||
/// </summary>
|
||||
public bool IndicateProgress
|
||||
{
|
||||
get
|
||||
{
|
||||
return indicateProgress;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (indicateProgress == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Set(ref indicateProgress, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="IndicateError"/>
|
||||
/// </summary>
|
||||
private bool indicateError = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether there was an error setting the indicate descriptor
|
||||
/// </summary>
|
||||
public bool IndicateError
|
||||
{
|
||||
get
|
||||
{
|
||||
return indicateError;
|
||||
}
|
||||
|
||||
private set
|
||||
{
|
||||
if (indicateError == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Set(ref indicateError, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this characteristic can be read
|
||||
/// </summary>
|
||||
public bool CharacteristicCanBeRead
|
||||
{
|
||||
get
|
||||
{
|
||||
return Characteristic.Characteristic.CharacteristicProperties.HasFlag(GattCharacteristicProperties.Read);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this characteristic can be written to
|
||||
/// </summary>
|
||||
public bool CharacteristicCanWrite
|
||||
{
|
||||
get
|
||||
{
|
||||
return ( Characteristic.Characteristic.CharacteristicProperties.HasFlag(GattCharacteristicProperties.Write) ||
|
||||
Characteristic.Characteristic.CharacteristicProperties.HasFlag(GattCharacteristicProperties.WriteWithoutResponse));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this characteristic can notify
|
||||
/// </summary>
|
||||
public bool CharacteristicCanNotify
|
||||
{
|
||||
get
|
||||
{
|
||||
return Characteristic.Characteristic.CharacteristicProperties.HasFlag(GattCharacteristicProperties.Notify);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this characteristic can indicate
|
||||
/// </summary>
|
||||
public bool CharacteristicCanIndicate
|
||||
{
|
||||
get
|
||||
{
|
||||
return Characteristic.Characteristic.CharacteristicProperties.HasFlag(GattCharacteristicProperties.Indicate);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this characteristic can notify or indicate
|
||||
/// </summary>
|
||||
public bool CharacteristicCanNotifyOrIndicate
|
||||
{
|
||||
get
|
||||
{
|
||||
return CharacteristicCanNotify | CharacteristicCanIndicate;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the value text box should be shown
|
||||
/// </summary>
|
||||
public bool CharacteristicValueVisible
|
||||
{
|
||||
get
|
||||
{
|
||||
return CharacteristicCanNotify | CharacteristicCanIndicate | CharacteristicCanBeRead;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="ValueToWrite"/>
|
||||
/// </summary>
|
||||
private string valueToWrite = String.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value to write to server
|
||||
/// </summary>
|
||||
public string ValueToWrite
|
||||
{
|
||||
get
|
||||
{
|
||||
return valueToWrite;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
Set(ref valueToWrite, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="WriteType"/>
|
||||
/// </summary>
|
||||
private WriteTypes writeType = WriteTypes.Hex;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets how the value should be written to server
|
||||
/// </summary>
|
||||
public WriteTypes WriteType
|
||||
{
|
||||
get
|
||||
{
|
||||
return writeType;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
Set(ref writeType, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="UseWindowsNotifications"/>
|
||||
/// </summary>
|
||||
private bool useWindowsNotifications = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether windows notifications should be used
|
||||
/// </summary>
|
||||
public bool UseWindowsNotifications
|
||||
{
|
||||
get
|
||||
{
|
||||
return useWindowsNotifications;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
Set(ref useWindowsNotifications, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the device
|
||||
/// </summary>
|
||||
public string DeviceName
|
||||
{
|
||||
get
|
||||
{
|
||||
return context.SelectedBluetoothLEDevice.Name;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="DisplayPresentError"/>
|
||||
/// </summary>
|
||||
private Windows.UI.Xaml.Visibility displayPresentError = Windows.UI.Xaml.Visibility.Visible;
|
||||
|
||||
/// <summary>
|
||||
/// Gets value to show or hide error text depending on <see cref="ObservableGattCharacteristics.DisplayTypes"/>
|
||||
/// </summary>
|
||||
public Windows.UI.Xaml.Visibility DisplayPresentError
|
||||
{
|
||||
get
|
||||
{
|
||||
return displayPresentError;
|
||||
}
|
||||
|
||||
private set
|
||||
{
|
||||
Set(ref displayPresentError, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CharacteristicPageViewModel" /> class.
|
||||
/// </summary>
|
||||
public CharacteristicPageViewModel()
|
||||
{
|
||||
if (Windows.ApplicationModel.DesignMode.DesignModeEnabled)
|
||||
{
|
||||
}
|
||||
|
||||
Notify = Characteristic.IsNotifySet;
|
||||
Indicate = Characteristic.IsIndicateSet;
|
||||
|
||||
this.PropertyChanged += CharacteristicPageViewModel_PropertyChanged;
|
||||
Characteristic.PropertyChanged += Characteristic_PropertyChanged;
|
||||
|
||||
if (Characteristic.DisplayType == ObservableGattCharacteristics.DisplayTypes.Unsupported)
|
||||
{
|
||||
DisplayPresentError = Windows.UI.Xaml.Visibility.Visible;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Characteristic changed
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void Characteristic_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == "Value")
|
||||
{
|
||||
if (UseWindowsNotifications == true && (Notify == true || Indicate == true))
|
||||
{
|
||||
BluetoothLEExplorer.Services.ToastService.ToastService.PopToast(Characteristic.Name, Characteristic.Value, "Notification", "Notification");
|
||||
}
|
||||
}
|
||||
|
||||
if (e.PropertyName == "DisplayType")
|
||||
{
|
||||
if (Characteristic.DisplayType == ObservableGattCharacteristics.DisplayTypes.Unsupported)
|
||||
{
|
||||
DisplayPresentError = Windows.UI.Xaml.Visibility.Visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Characteristic view model changed
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private async void CharacteristicPageViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == "Notify")
|
||||
{
|
||||
if (Notify == true)
|
||||
{
|
||||
NotifyProgress = true;
|
||||
bool success = await Characteristic.SetNotify();
|
||||
NotifyProgress = false;
|
||||
if (success == true)
|
||||
{
|
||||
if (settingErrorMessage == true)
|
||||
{
|
||||
settingErrorMessage = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
NotifyError = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
settingErrorMessage = true;
|
||||
Notify = false;
|
||||
NotifyError = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NotifyProgress = true;
|
||||
bool success = await Characteristic.StopNotify();
|
||||
NotifyProgress = false;
|
||||
if (success == true)
|
||||
{
|
||||
if (settingErrorMessage == true)
|
||||
{
|
||||
settingErrorMessage = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
NotifyError = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
settingErrorMessage = true;
|
||||
NotifyError = true;
|
||||
Notify = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (e.PropertyName == "Indicate")
|
||||
{
|
||||
if (Indicate == true)
|
||||
{
|
||||
IndicateProgress = true;
|
||||
bool success = await Characteristic.SetIndicate();
|
||||
IndicateProgress = false;
|
||||
if (success == true)
|
||||
{
|
||||
IndicateError = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Indicate = false;
|
||||
IndicateError = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
IndicateProgress = true;
|
||||
bool success = await Characteristic.StopIndicate();
|
||||
IndicateProgress = false;
|
||||
|
||||
if (success == true)
|
||||
{
|
||||
IndicateError = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
IndicateError = true;
|
||||
Indicate = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write the value to the server
|
||||
/// </summary>
|
||||
public async void WriteValue()
|
||||
{
|
||||
if (!String.IsNullOrEmpty(ValueToWrite))
|
||||
{
|
||||
IBuffer writeBuffer = null;
|
||||
|
||||
if (WriteType == WriteTypes.Decimal)
|
||||
{
|
||||
DataWriter writer = new DataWriter();
|
||||
writer.ByteOrder = ByteOrder.LittleEndian;
|
||||
writer.WriteInt32(Int32.Parse(ValueToWrite));
|
||||
writeBuffer = writer.DetachBuffer();
|
||||
}
|
||||
else if (WriteType == WriteTypes.Hex)
|
||||
{
|
||||
try
|
||||
{
|
||||
// pad the value if we've received odd number of bytes
|
||||
if (ValueToWrite.Length % 2 == 1)
|
||||
{
|
||||
writeBuffer = GattConvert.ToIBufferFromHexString("0" + ValueToWrite);
|
||||
}
|
||||
else
|
||||
{
|
||||
writeBuffer = GattConvert.ToIBufferFromHexString(ValueToWrite);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageDialog dialog = new MessageDialog(ex.Message, "Error");
|
||||
await dialog.ShowAsync();
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
else if (WriteType == WriteTypes.UTF8)
|
||||
{
|
||||
writeBuffer = CryptographicBuffer.ConvertStringToBinary(ValueToWrite,
|
||||
BinaryStringEncoding.Utf8);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// BT_Code: Writes the value from the buffer to the characteristic.
|
||||
GattCommunicationStatus result = await Characteristic.Characteristic.WriteValueAsync(writeBuffer);
|
||||
|
||||
if (result == GattCommunicationStatus.Unreachable)
|
||||
{
|
||||
NotifyUser.Insert(0, "Unable to write data - Device unreachable");
|
||||
}
|
||||
else if (result == GattCommunicationStatus.ProtocolError)
|
||||
{
|
||||
NotifyUser.Insert(0, "Unable to write data - Protocol error");
|
||||
}
|
||||
}
|
||||
catch (Exception ex) when ((uint)ex.HResult == 0x80650003 || (uint)ex.HResult == 0x80070005)
|
||||
{
|
||||
// E_BLUETOOTH_ATT_WRITE_NOT_PERMITTED or E_ACCESSDENIED
|
||||
// This usually happens when a device reports that it support writing, but it actually doesn't.
|
||||
NotifyUser.Insert(0, "Error writing to characteristic. This usually happens when a device reports that it support writing, but it actually doesn't.");
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
MessageDialog dialog = new MessageDialog(ex.Message, "Error");
|
||||
await dialog.ShowAsync();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NotifyUser.Insert(0, "No data to write to device");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigate to page
|
||||
/// </summary>
|
||||
/// <param name="parameter"></param>
|
||||
/// <param name="mode"></param>
|
||||
/// <param name="suspensionState"></param>
|
||||
/// <returns>Navigate to task</returns>
|
||||
public override async Task OnNavigatedToAsync(object parameter, NavigationMode mode, IDictionary<string, object> suspensionState)
|
||||
{
|
||||
await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(
|
||||
Windows.UI.Core.CoreDispatcherPriority.Normal,
|
||||
() =>
|
||||
{
|
||||
|
||||
if(Services.SettingsServices.SettingsService.Instance.SettingsDictionary.Keys.Contains(Characteristic.UUID + "-UseWindowsNotification"))
|
||||
{
|
||||
UseWindowsNotifications = (bool)Services.SettingsServices.SettingsService.Instance.SettingsDictionary[Characteristic.UUID + "-UseWindowsNotification"];
|
||||
}
|
||||
});
|
||||
|
||||
if (Characteristic.Characteristic.CharacteristicProperties != GattCharacteristicProperties.None)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
bool first = true;
|
||||
|
||||
foreach (GattCharacteristicProperties p in Enum.GetValues(typeof(GattCharacteristicProperties)))
|
||||
{
|
||||
if (p == GattCharacteristicProperties.None)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Characteristic.Characteristic.CharacteristicProperties.HasFlag(p))
|
||||
{
|
||||
if (!first)
|
||||
{
|
||||
sb.Append(", ");
|
||||
}
|
||||
else
|
||||
{
|
||||
first = false;
|
||||
}
|
||||
|
||||
sb.Append(Enum.GetName(typeof(GattCharacteristicProperties), p));
|
||||
}
|
||||
}
|
||||
|
||||
Properties = sb.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigate from page
|
||||
/// </summary>
|
||||
/// <param name="suspensionState"></param>
|
||||
/// <param name="suspending"></param>
|
||||
/// <returns>Navigate task</returns>
|
||||
public override async Task OnNavigatedFromAsync(IDictionary<string, object> suspensionState, bool suspending)
|
||||
{
|
||||
if (suspending)
|
||||
{
|
||||
}
|
||||
|
||||
Services.SettingsServices.SettingsService.Instance.SettingsDictionary[Characteristic.UUID + "-UseWindowsNotification"] = useWindowsNotifications;
|
||||
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigate from page
|
||||
/// </summary>
|
||||
/// <param name="args"></param>
|
||||
/// <returns>Navigate from task</returns>
|
||||
public override async Task OnNavigatingFromAsync(NavigatingEventArgs args)
|
||||
{
|
||||
args.Cancel = false;
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
// <copyright file="DeviceServicesPageViewModel.cs" company="Microsoft Corporation">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//----------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using BluetoothLEExplorer.Models;
|
||||
using BluetoothLEExplorer.Views;
|
||||
using Template10.Mvvm;
|
||||
using Template10.Services.NavigationService;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
|
||||
namespace BluetoothLEExplorer.ViewModels
|
||||
{
|
||||
/// <summary>
|
||||
/// View Model for Device Services View
|
||||
/// </summary>
|
||||
public class DeviceServicesPageViewModel : ViewModelBase
|
||||
{
|
||||
/// <summary>
|
||||
/// App context
|
||||
/// </summary>
|
||||
private GattSampleContext context = GattSampleContext.Context;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the currently selected bluetooth device
|
||||
/// </summary>
|
||||
public ObservableBluetoothLEDevice Device { get; private set; } = GattSampleContext.Context.SelectedBluetoothLEDevice;
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="ErrorText"/>
|
||||
/// </summary>
|
||||
private string errorText = String.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the error text to display
|
||||
/// </summary>
|
||||
public string ErrorText
|
||||
{
|
||||
get
|
||||
{
|
||||
return errorText;
|
||||
}
|
||||
|
||||
private set
|
||||
{
|
||||
Set(ref errorText, value, "ErrorText");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="SelectedCharacteristic"/>
|
||||
/// </summary>
|
||||
private ObservableGattCharacteristics selectedCharacteristic;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the currently selected characteristic
|
||||
/// </summary>
|
||||
public ObservableGattCharacteristics SelectedCharacteristic
|
||||
{
|
||||
get
|
||||
{
|
||||
return selectedCharacteristic;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
Set(ref selectedCharacteristic, value, "SelectedCharacteristic");
|
||||
context.SelectedCharacteristic = SelectedCharacteristic;
|
||||
NavigationService.Navigate(typeof(CharacteristicPage));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DeviceServicesPageViewModel" /> class.
|
||||
/// </summary>
|
||||
public DeviceServicesPageViewModel()
|
||||
{
|
||||
if (Windows.ApplicationModel.DesignMode.DesignModeEnabled)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigate from page
|
||||
/// </summary>
|
||||
/// <param name="parameter"></param>
|
||||
/// <param name="mode"></param>
|
||||
/// <param name="suspensionState"></param>
|
||||
/// <returns>Navigate from Task</returns>
|
||||
public override async Task OnNavigatedToAsync(object parameter, NavigationMode mode, IDictionary<string, object> suspensionState)
|
||||
{
|
||||
await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(
|
||||
Windows.UI.Core.CoreDispatcherPriority.Normal,
|
||||
() =>
|
||||
{
|
||||
});
|
||||
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigate from page
|
||||
/// </summary>
|
||||
/// <param name="suspensionState"></param>
|
||||
/// <param name="suspending"></param>
|
||||
/// <returns>Navigate from task</returns>
|
||||
public override async Task OnNavigatedFromAsync(IDictionary<string, object> suspensionState, bool suspending)
|
||||
{
|
||||
if (suspending)
|
||||
{
|
||||
}
|
||||
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigate from page
|
||||
/// </summary>
|
||||
/// <param name="args"></param>
|
||||
/// <returns>Navigate from Task</returns>
|
||||
public override async Task OnNavigatingFromAsync(NavigatingEventArgs args)
|
||||
{
|
||||
args.Cancel = false;
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,449 @@
|
|||
// <copyright file="DiscoverViewModel.cs" company="Microsoft Corporation">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//----------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BluetoothLEExplorer.Models;
|
||||
using Template10.Mvvm;
|
||||
using Template10.Services.NavigationService;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
using SortedObservableCollection;
|
||||
|
||||
namespace BluetoothLEExplorer.ViewModels
|
||||
{
|
||||
/// <summary>
|
||||
/// View Model for the device discovery page
|
||||
/// </summary>
|
||||
public class DiscoverViewModel : ViewModelBase
|
||||
{
|
||||
/// <summary>
|
||||
/// App context
|
||||
/// </summary>
|
||||
//private GattSampleContext context;
|
||||
|
||||
public GattSampleContext Context
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
private object deviceListLock = new object();
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="DeviceList"/>
|
||||
/// </summary>
|
||||
private SortedObservableCollection<ObservableBluetoothLEDevice> deviceList = new SortedObservableCollection<ObservableBluetoothLEDevice>(new ObservableBluetoothLEDevice.RSSIComparer(), "RSSI");
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the device list
|
||||
/// </summary>
|
||||
public SortedObservableCollection<ObservableBluetoothLEDevice> DeviceList
|
||||
{
|
||||
get
|
||||
{
|
||||
return deviceList;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
Set(ref deviceList, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="SelectedDevice"/>
|
||||
/// </summary>
|
||||
private ObservableBluetoothLEDevice selectedDevice;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets currently selected service
|
||||
/// </summary>
|
||||
public ObservableBluetoothLEDevice SelectedDevice
|
||||
{
|
||||
get
|
||||
{
|
||||
return selectedDevice;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
bool same = selectedDevice == value;
|
||||
|
||||
Set(ref selectedDevice, value, "SelectedDevice");
|
||||
Context.SelectedBluetoothLEDevice = selectedDevice;
|
||||
|
||||
if (same == false && value != null)
|
||||
{
|
||||
ConnectToSelectedDevice();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="IsEnumerating"/>
|
||||
/// </summary>
|
||||
private bool isEnumerating = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether app is currently enumerating BT LE devices
|
||||
/// </summary>
|
||||
public bool IsEnumerating
|
||||
{
|
||||
get
|
||||
{
|
||||
return Context.IsEnumerating;
|
||||
}
|
||||
|
||||
private set
|
||||
{
|
||||
Set(ref isEnumerating, value, "IsEnumerating");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="EnumerationFinished"/>
|
||||
/// </summary>
|
||||
private bool enumerationFinished = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether enumeration has finished
|
||||
/// </summary>
|
||||
public bool EnumerationFinished
|
||||
{
|
||||
get
|
||||
{
|
||||
return enumerationFinished;
|
||||
}
|
||||
|
||||
private set
|
||||
{
|
||||
Set(ref enumerationFinished, value, "EnumerationFinished");
|
||||
}
|
||||
}
|
||||
|
||||
private bool continuousEnumeration = false;
|
||||
public bool ContinuousEnumeration
|
||||
{
|
||||
get
|
||||
{
|
||||
return continuousEnumeration;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
Set(ref continuousEnumeration, value, "ContinuousEnumeration");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether peripheral role is supported
|
||||
/// </summary>
|
||||
public bool IsPeripheralRoleSupported
|
||||
{
|
||||
get
|
||||
{
|
||||
return GattSampleContext.Context.IsPeripheralRoleSupported;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether central role is supported by this device
|
||||
/// </summary>
|
||||
public bool IsCentralRoleSupported
|
||||
{
|
||||
get
|
||||
{
|
||||
return Context.IsCentralRoleSupported;
|
||||
}
|
||||
}
|
||||
|
||||
private string gridFilter = string.Empty;
|
||||
public string GridFilter
|
||||
{
|
||||
get
|
||||
{
|
||||
return gridFilter;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if(gridFilter != value)
|
||||
{
|
||||
Set(ref gridFilter, value, "GridFilter");
|
||||
UpdateDeviceList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DiscoverViewModel" /> class.
|
||||
/// </summary>
|
||||
public DiscoverViewModel()
|
||||
{
|
||||
if (Windows.ApplicationModel.DesignMode.DesignModeEnabled)
|
||||
{
|
||||
}
|
||||
|
||||
Context = GattSampleContext.Context;
|
||||
Context.PropertyChanged += Context_PropertyChanged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Context property changed
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void Context_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == "IsEnumerating")
|
||||
{
|
||||
IsEnumerating = Context.IsEnumerating;
|
||||
}
|
||||
|
||||
if (e.PropertyName == "EnumerationFinished")
|
||||
{
|
||||
EnumerationFinished = Context.EnumerationFinished;
|
||||
if(ContinuousEnumeration == true)
|
||||
{
|
||||
Debug.WriteLine("Enumeration finished, but continue is set, continuing");
|
||||
StartEnumeration();
|
||||
}
|
||||
}
|
||||
|
||||
if (e.PropertyName == "IsPeripheralRoleSupported")
|
||||
{
|
||||
this.RaisePropertyChanged("IsPeripheralRoleSupported");
|
||||
}
|
||||
|
||||
if (e.PropertyName == "IsCentralRoleSupported")
|
||||
{
|
||||
RaisePropertyChanged("IsCentralRoleSupported");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The devices list changed
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void BluetoothLEDevices_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
string msg = string.Empty;
|
||||
lock (deviceListLock)
|
||||
{
|
||||
if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
|
||||
{
|
||||
foreach (ObservableBluetoothLEDevice newDev in e.NewItems)
|
||||
{
|
||||
if (ShouldShow(newDev))
|
||||
{
|
||||
DeviceList.Add(newDev);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Remove)
|
||||
{
|
||||
foreach (ObservableBluetoothLEDevice oldDev in e.OldItems)
|
||||
{
|
||||
DeviceList.Remove(oldDev);
|
||||
}
|
||||
}
|
||||
else if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Reset)
|
||||
{
|
||||
DeviceList.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
UpdateDeviceList();
|
||||
}
|
||||
|
||||
private bool ShouldShow(ObservableBluetoothLEDevice dev)
|
||||
{
|
||||
string filter = gridFilter.ToUpper();
|
||||
return (dev.Name.ToUpper().Contains(filter) || dev.BluetoothAddressAsString.ToUpper().Contains(filter));
|
||||
}
|
||||
|
||||
private void UpdateDeviceList()
|
||||
{
|
||||
List<ObservableBluetoothLEDevice> toRemove = new List<ObservableBluetoothLEDevice>();
|
||||
|
||||
lock (deviceListLock)
|
||||
{
|
||||
foreach (ObservableBluetoothLEDevice dev in DeviceList)
|
||||
{
|
||||
if (ShouldShow(dev) == false)
|
||||
{
|
||||
toRemove.Add(dev);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (ObservableBluetoothLEDevice dev in toRemove)
|
||||
{
|
||||
DeviceList.Remove(dev);
|
||||
}
|
||||
|
||||
foreach (ObservableBluetoothLEDevice dev in Context.BluetoothLEDevices)
|
||||
{
|
||||
if (ShouldShow(dev) && DeviceList.Contains(dev) == false)
|
||||
{
|
||||
DeviceList.Add(dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Connect to the currently selected service
|
||||
/// </summary>
|
||||
public async void ConnectToSelectedDevice()
|
||||
{
|
||||
Debug.WriteLine("ConnectToSelectedDevice: Entering");
|
||||
StopEnumeration();
|
||||
Views.Busy.SetBusy(true, "Connecting to " + SelectedDevice.Name);
|
||||
|
||||
Debug.WriteLine("ConnectToSelectedDevice: Trying to connect to " + SelectedDevice.Name);
|
||||
|
||||
if (await SelectedDevice.Connect() == false)
|
||||
{
|
||||
Debug.WriteLine("ConnectToSelectedDevice: Something went wrong getting the BluetoothLEDevice");
|
||||
Views.Busy.SetBusy(false);
|
||||
SelectedDevice = null;
|
||||
NavigationService.Navigate(typeof(Views.Discover));
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.WriteLine("ConnectToSelectedDevice: Going to Device Service Page");
|
||||
Views.Busy.SetBusy(false);
|
||||
GotoDeviceServicesPage();
|
||||
Debug.WriteLine("ConnectToSelectedDevice: Exiting");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigate to page
|
||||
/// </summary>
|
||||
/// <param name="parameter"></param>
|
||||
/// <param name="mode"></param>
|
||||
/// <param name="suspensionState"></param>
|
||||
/// <returns>Navigate to task</returns>
|
||||
public override async Task OnNavigatedToAsync(object parameter, NavigationMode mode, IDictionary<string, object> suspensionState)
|
||||
{
|
||||
if (suspensionState.Any())
|
||||
{
|
||||
}
|
||||
|
||||
Context.BluetoothLEDevices.CollectionChanged += BluetoothLEDevices_CollectionChanged;
|
||||
GridFilter = "";
|
||||
UpdateDeviceList();
|
||||
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigate from page
|
||||
/// </summary>
|
||||
/// <param name="suspensionState"></param>
|
||||
/// <param name="suspending"></param>
|
||||
/// <returns>Navigate from task</returns>
|
||||
public override async Task OnNavigatedFromAsync(IDictionary<string, object> suspensionState, bool suspending)
|
||||
{
|
||||
if (suspending)
|
||||
{
|
||||
}
|
||||
|
||||
Context.BluetoothLEDevices.CollectionChanged -= BluetoothLEDevices_CollectionChanged;
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigate from page
|
||||
/// </summary>
|
||||
/// <param name="args"></param>
|
||||
/// <returns>Navigate from task</returns>
|
||||
public override async Task OnNavigatingFromAsync(NavigatingEventArgs args)
|
||||
{
|
||||
args.Cancel = false;
|
||||
Context.StopEnumeration();
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Toggles the enumeration
|
||||
/// </summary>
|
||||
public async void ToggleEnumeration()
|
||||
{
|
||||
await Dispatcher.DispatchAsync(() =>
|
||||
{
|
||||
if (Context.IsEnumerating == false)
|
||||
{
|
||||
Context.StartEnumeration();
|
||||
}
|
||||
else
|
||||
{
|
||||
Context.StopEnumeration();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start enumeration
|
||||
/// </summary>
|
||||
public async void StartEnumeration()
|
||||
{
|
||||
if (Context.IsEnumerating == true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await Dispatcher.DispatchAsync(() =>
|
||||
{
|
||||
Context.StartEnumeration();
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop enumeration
|
||||
/// </summary>
|
||||
public async void StopEnumeration()
|
||||
{
|
||||
if (Context.IsEnumerating == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await Dispatcher.DispatchAsync(() =>
|
||||
{
|
||||
Context.StopEnumeration();
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Go to Devices page
|
||||
/// </summary>
|
||||
public void GotoDeviceServicesPage()
|
||||
{
|
||||
NavigationService.Navigate(typeof(Views.DeviceServicesPage));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Go to settings
|
||||
/// </summary>
|
||||
public void GotoSettings() =>
|
||||
NavigationService.Navigate(typeof(Views.SettingsPage), 0);
|
||||
|
||||
/// <summary>
|
||||
/// Go to privacy
|
||||
/// </summary>
|
||||
public void GotoPrivacy() =>
|
||||
NavigationService.Navigate(typeof(Views.SettingsPage), 1);
|
||||
|
||||
/// <summary>
|
||||
/// Go to about
|
||||
/// </summary>
|
||||
public void GotoAbout() =>
|
||||
NavigationService.Navigate(typeof(Views.SettingsPage), 2);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,239 @@
|
|||
// <copyright file="GenericGattCharacteristicViewModel.cs" company="Microsoft Corporation">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//----------------------------------------------------------------------------------------------
|
||||
using System.Collections.ObjectModel;
|
||||
using GattServicesLibrary;
|
||||
using Template10.Mvvm;
|
||||
using Windows.Devices.Bluetooth.GenericAttributeProfile;
|
||||
using GattHelper.Converters;
|
||||
using System;
|
||||
|
||||
namespace BluetoothLEExplorer.ViewModels
|
||||
{
|
||||
/// <summary>
|
||||
/// View model used to display a <see cref="GenericGattCharacteristic"/>
|
||||
/// </summary>
|
||||
public class GenericGattCharacteristicViewModel : ViewModelBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Source for <see cref="Characteristic"/>
|
||||
/// </summary>
|
||||
private GenericGattCharacteristic characteristic;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the characteristic that this class wraps
|
||||
/// </summary>
|
||||
public GenericGattCharacteristic Characteristic
|
||||
{
|
||||
get
|
||||
{
|
||||
return characteristic;
|
||||
}
|
||||
|
||||
private set
|
||||
{
|
||||
Set(ref characteristic, value, "Characteristic");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="UserDescription"/>
|
||||
/// </summary>
|
||||
private string userDescription;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the user description
|
||||
/// </summary>
|
||||
public string UserDescription
|
||||
{
|
||||
get
|
||||
{
|
||||
return userDescription;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
Set(ref userDescription, value, "UserDescription");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="Descriptors"/>
|
||||
/// </summary>
|
||||
private ObservableCollection<GattCharacteristicProperties> descriptors;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of descriptors for this service
|
||||
/// </summary>
|
||||
private ObservableCollection<GattCharacteristicProperties> Descriptors
|
||||
{
|
||||
get
|
||||
{
|
||||
return descriptors;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
Set(ref descriptors, value, "Descriptors");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="HasReadDescriptor"/>
|
||||
/// </summary>
|
||||
private bool hasReadDescriptor = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this characteristic can be read
|
||||
/// </summary>
|
||||
public bool HasReadDescriptor
|
||||
{
|
||||
get
|
||||
{
|
||||
return hasReadDescriptor;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
Set(ref hasReadDescriptor, value, "HasReadDescriptor");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="HasWriteDescriptor"/>
|
||||
/// </summary>
|
||||
private bool hasWriteDescriptor = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this characteristic can write
|
||||
/// </summary>
|
||||
public bool HasWriteDescriptor
|
||||
{
|
||||
get
|
||||
{
|
||||
return hasWriteDescriptor;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
Set(ref hasWriteDescriptor, value, "HasWriteDescriptor");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="HasWriteWithoutResponseDescriptor"/>
|
||||
/// </summary>
|
||||
private bool hasWriteWithoutResponseDescriptor = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this characteristic can write without responds
|
||||
/// </summary>
|
||||
public bool HasWriteWithoutResponseDescriptor
|
||||
{
|
||||
get
|
||||
{
|
||||
return hasWriteWithoutResponseDescriptor;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
Set(ref hasWriteWithoutResponseDescriptor, value, "JasWriteWithoutRespondsDescriptor");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="HasNotifyDescriptor"/>
|
||||
/// </summary>
|
||||
private bool hasNotifyDescriptor = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this characteristic can notify
|
||||
/// </summary>
|
||||
public bool HasNotifyDescriptor
|
||||
{
|
||||
get
|
||||
{
|
||||
return hasNotifyDescriptor;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
Set(ref hasNotifyDescriptor, value, "HasNotifyDescriptor");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="HasIndicateDescriptor"/>
|
||||
/// </summary>
|
||||
private bool hasIndicateDescriptor = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this characteristic can indicate
|
||||
/// </summary>
|
||||
public bool HasIndicateDescriptor
|
||||
{
|
||||
get
|
||||
{
|
||||
return hasIndicateDescriptor;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
Set(ref hasIndicateDescriptor, value, "HasIndicateDescriptor");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GenericGattCharacteristicViewModel" /> class.
|
||||
/// </summary>
|
||||
/// <param name="characteristic"></param>
|
||||
public GenericGattCharacteristicViewModel(GenericGattCharacteristic characteristic)
|
||||
{
|
||||
this.characteristic = characteristic;
|
||||
userDescription = Characteristic.Characteristic.UserDescription;
|
||||
|
||||
HasReadDescriptor = Characteristic.Characteristic.CharacteristicProperties.HasFlag(GattCharacteristicProperties.Read);
|
||||
HasWriteDescriptor = Characteristic.Characteristic.CharacteristicProperties.HasFlag(GattCharacteristicProperties.Write);
|
||||
HasWriteWithoutResponseDescriptor = Characteristic.Characteristic.CharacteristicProperties.HasFlag(GattCharacteristicProperties.WriteWithoutResponse);
|
||||
HasNotifyDescriptor = Characteristic.Characteristic.CharacteristicProperties.HasFlag(GattCharacteristicProperties.Notify);
|
||||
HasIndicateDescriptor = Characteristic.Characteristic.CharacteristicProperties.HasFlag(GattCharacteristicProperties.Indicate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This sets the value based on the input string with regard to the characteristics first presentation format
|
||||
/// </summary>
|
||||
/// <param name="value">value to set</param>
|
||||
/// <param name="isHexString">is the passed in string a hex byte array</param>
|
||||
public void SetValueFromString(string value, bool isHexString = false)
|
||||
{
|
||||
if(isHexString == true)
|
||||
{
|
||||
Characteristic.Value = GattConvert.ToIBufferFromHexString(value);
|
||||
return;
|
||||
}
|
||||
|
||||
if(Characteristic.Characteristic.PresentationFormats.Count > 0)
|
||||
{
|
||||
byte format = Characteristic.Characteristic.PresentationFormats[0].FormatType;
|
||||
|
||||
// Check our supported formats to convert a string to this Characteristics Value
|
||||
if(!((format != GattPresentationFormatTypes.SInt32) ||
|
||||
(format != GattPresentationFormatTypes.Utf8)))
|
||||
{
|
||||
throw new NotImplementedException("Only SInt32 and UTF8 are supported");
|
||||
}
|
||||
|
||||
//TODO: Support more presentation types
|
||||
if(format == GattPresentationFormatTypes.SInt32)
|
||||
{
|
||||
Characteristic.Value = GattConvert.ToIBuffer(Convert.ToInt32(value));
|
||||
}
|
||||
else if(format == GattPresentationFormatTypes.Utf8)
|
||||
{
|
||||
Characteristic.Value = GattConvert.ToIBuffer(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
// <copyright file="GenericGattServiceViewModel.cs" company="Microsoft Corporation">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//----------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using BluetoothLEExplorer.Models;
|
||||
using GattServicesLibrary;
|
||||
using Template10.Mvvm;
|
||||
using Windows.Devices.Bluetooth.GenericAttributeProfile;
|
||||
|
||||
namespace BluetoothLEExplorer.ViewModels
|
||||
{
|
||||
/// <summary>
|
||||
/// View Model used to display a <see cref="GenericGattService"/>
|
||||
/// </summary>
|
||||
public class GenericGattServiceViewModel : ViewModelBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Source for <see cref="Service"/> property
|
||||
/// </summary>
|
||||
private GenericGattService service = null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the service for this view model
|
||||
/// </summary>
|
||||
public GenericGattService Service
|
||||
{
|
||||
get
|
||||
{
|
||||
return service;
|
||||
}
|
||||
|
||||
private set
|
||||
{
|
||||
Set(ref service, value, "Service");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="IsPublishing"/> property
|
||||
/// </summary>
|
||||
private bool isPublishing = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the Advertisement status is published or not
|
||||
/// </summary>
|
||||
public bool IsPublishing
|
||||
{
|
||||
get
|
||||
{
|
||||
return isPublishing;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if(value != isPublishing)
|
||||
{
|
||||
if(value == true)
|
||||
{
|
||||
Start();
|
||||
|
||||
}
|
||||
if(value == false)
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
isPublishing = value;
|
||||
RaisePropertyChanged("IsPublishing");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the service
|
||||
/// </summary>
|
||||
private async void Start()
|
||||
{
|
||||
await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(
|
||||
Windows.UI.Core.CoreDispatcherPriority.Normal,
|
||||
() =>
|
||||
{
|
||||
Service.Start(true);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the service
|
||||
/// </summary>
|
||||
private async void Stop()
|
||||
{
|
||||
await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(
|
||||
Windows.UI.Core.CoreDispatcherPriority.Normal,
|
||||
() =>
|
||||
{
|
||||
Service.Stop();
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GenericGattServiceViewModel" /> class.
|
||||
/// </summary>
|
||||
/// <param name="service"></param>
|
||||
public GenericGattServiceViewModel(GenericGattService service)
|
||||
{
|
||||
Service = service;
|
||||
Service.PropertyChanged += Service_PropertyChanged;
|
||||
}
|
||||
|
||||
private void Service_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
{
|
||||
// Track the IsPublishing property
|
||||
IsPublishing = Service.IsPublishing;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes this service from context
|
||||
/// </summary>
|
||||
public void RemoveThisFromContext()
|
||||
{
|
||||
GattSampleContext.Context.CreatedServices.Remove(this);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
// <copyright file="BatteryServicePageViewModel.cs" company="Microsoft Corporation">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//----------------------------------------------------------------------------------------------
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BluetoothLEExplorer.Models;
|
||||
using GattServicesLibrary.Services;
|
||||
using Template10.Mvvm;
|
||||
using Template10.Services.NavigationService;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
|
||||
namespace BluetoothLEExplorer.ViewModels
|
||||
{
|
||||
/// <summary>
|
||||
/// View Model for the <see cref="BatteryService"/> view
|
||||
/// </summary>
|
||||
public class BatteryServicePageViewModel : ViewModelBase
|
||||
{
|
||||
/// <summary>
|
||||
/// App context
|
||||
/// </summary>
|
||||
private GattSampleContext context = GattSampleContext.Context;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the currently selected service view model
|
||||
/// </summary>
|
||||
public GenericGattServiceViewModel ServiceVM { get; set; } = GattSampleContext.Context.SelectedGattServerService;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the currently selected service
|
||||
/// </summary>
|
||||
public BatteryService Service { get; set; } = GattSampleContext.Context.SelectedGattServerService.Service as BatteryService;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the battery level
|
||||
/// </summary>
|
||||
public GenericGattCharacteristicViewModel BatteryLevel { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BatteryServicePageViewModel" /> class.
|
||||
/// </summary>
|
||||
public BatteryServicePageViewModel()
|
||||
{
|
||||
BatteryLevel = new GenericGattCharacteristicViewModel(Service.BatteryLevel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigate from page
|
||||
/// </summary>
|
||||
/// <param name="args"></param>
|
||||
/// <returns>Navigate from task</returns>
|
||||
public override async Task OnNavigatingFromAsync(NavigatingEventArgs args)
|
||||
{
|
||||
context.SelectedGattServerService = null;
|
||||
args.Cancel = false;
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Go to settings
|
||||
/// </summary>
|
||||
public void GotoSettings() =>
|
||||
NavigationService.Navigate(typeof(Views.SettingsPage), 0);
|
||||
|
||||
/// <summary>
|
||||
/// Go to privacy
|
||||
/// </summary>
|
||||
public void GotoPrivacy() =>
|
||||
NavigationService.Navigate(typeof(Views.SettingsPage), 1);
|
||||
|
||||
/// <summary>
|
||||
/// Go to about
|
||||
/// </summary>
|
||||
public void GotoAbout() =>
|
||||
NavigationService.Navigate(typeof(Views.SettingsPage), 2);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
// <copyright file="BloodPressureServicePageViewModel.cs" company="Microsoft Corporation">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//----------------------------------------------------------------------------------------------
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BluetoothLEExplorer.Models;
|
||||
using GattServicesLibrary.Services;
|
||||
using Template10.Mvvm;
|
||||
using Template10.Services.NavigationService;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
|
||||
namespace BluetoothLEExplorer.ViewModels
|
||||
{
|
||||
/// <summary>
|
||||
/// View Model for the <see cref="BloodPressureServicePageViewModel"/> view
|
||||
/// </summary>
|
||||
public class BloodPressureServicePageViewModel : ViewModelBase
|
||||
{
|
||||
/// <summary>
|
||||
/// App context
|
||||
/// </summary>
|
||||
private GattSampleContext context = GattSampleContext.Context;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the currently selected service view model
|
||||
/// </summary>
|
||||
public GenericGattServiceViewModel ServiceVM { get; set; } = GattSampleContext.Context.SelectedGattServerService;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the currently selected service
|
||||
/// </summary>
|
||||
public BloodPressureService Service { get; set; } = GattSampleContext.Context.SelectedGattServerService.Service as BloodPressureService;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the blood pressure measurement
|
||||
/// </summary>
|
||||
public GenericGattCharacteristicViewModel BloodPressureMeasurement { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the blood pressure feature
|
||||
/// </summary>
|
||||
public GenericGattCharacteristicViewModel BloodPressureFeature { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BloodPressureServicePageViewModel" /> class.
|
||||
/// </summary>
|
||||
public BloodPressureServicePageViewModel()
|
||||
{
|
||||
BloodPressureMeasurement = new GenericGattCharacteristicViewModel(Service.BloodPressureMeasurement);
|
||||
BloodPressureFeature = new GenericGattCharacteristicViewModel(Service.BloodPressureFeature);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigate from page
|
||||
/// </summary>
|
||||
/// <param name="args"></param>
|
||||
/// <returns>Navigate from task</returns>
|
||||
public override async Task OnNavigatingFromAsync(NavigatingEventArgs args)
|
||||
{
|
||||
context.SelectedGattServerService = null;
|
||||
args.Cancel = false;
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Go to settings
|
||||
/// </summary>
|
||||
public void GotoSettings() =>
|
||||
NavigationService.Navigate(typeof(Views.SettingsPage), 0);
|
||||
|
||||
/// <summary>
|
||||
/// Go to privacy
|
||||
/// </summary>
|
||||
public void GotoPrivacy() =>
|
||||
NavigationService.Navigate(typeof(Views.SettingsPage), 1);
|
||||
|
||||
/// <summary>
|
||||
/// Go to about
|
||||
/// </summary>
|
||||
public void GotoAbout() =>
|
||||
NavigationService.Navigate(typeof(Views.SettingsPage), 2);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
// <copyright file="HeartRateServicePageViewModel.cs" company="Microsoft Corporation">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//----------------------------------------------------------------------------------------------
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BluetoothLEExplorer.Models;
|
||||
using GattServicesLibrary.Services;
|
||||
using Template10.Mvvm;
|
||||
using Template10.Services.NavigationService;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
|
||||
namespace BluetoothLEExplorer.ViewModels
|
||||
{
|
||||
/// <summary>
|
||||
/// View Model for the <see cref="HeartRateServicePageViewModel"/> view
|
||||
/// </summary>
|
||||
public class HeartRateServicePageViewModel : ViewModelBase
|
||||
{
|
||||
/// <summary>
|
||||
/// App context
|
||||
/// </summary>
|
||||
private GattSampleContext context = GattSampleContext.Context;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the currently selected service view model
|
||||
/// </summary>
|
||||
public GenericGattServiceViewModel ServiceVM { get; set; } = GattSampleContext.Context.SelectedGattServerService;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the currently selected service
|
||||
/// </summary>
|
||||
public HeartRateService Service { get; set; } = GattSampleContext.Context.SelectedGattServerService.Service as HeartRateService;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the heart rate measurement
|
||||
/// </summary>
|
||||
public GenericGattCharacteristicViewModel HeartRateMeasurement { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="HeartRateServicePageViewModel" /> class.
|
||||
/// </summary>
|
||||
public HeartRateServicePageViewModel()
|
||||
{
|
||||
HeartRateMeasurement = new GenericGattCharacteristicViewModel(Service.HeartRateMeasurement);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigate from page
|
||||
/// </summary>
|
||||
/// <param name="args"></param>
|
||||
/// <returns>Navigate from task</returns>
|
||||
public override async Task OnNavigatingFromAsync(NavigatingEventArgs args)
|
||||
{
|
||||
context.SelectedGattServerService = null;
|
||||
args.Cancel = false;
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Go to settings
|
||||
/// </summary>
|
||||
public void GotoSettings() =>
|
||||
NavigationService.Navigate(typeof(Views.SettingsPage), 0);
|
||||
|
||||
/// <summary>
|
||||
/// Go to privacy
|
||||
/// </summary>
|
||||
public void GotoPrivacy() =>
|
||||
NavigationService.Navigate(typeof(Views.SettingsPage), 1);
|
||||
|
||||
/// <summary>
|
||||
/// Go to about
|
||||
/// </summary>
|
||||
public void GotoAbout() =>
|
||||
NavigationService.Navigate(typeof(Views.SettingsPage), 2);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
// <copyright file="MicrosoftServicePageViewModel.cs" company="Microsoft Corporation">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//----------------------------------------------------------------------------------------------
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BluetoothLEExplorer.Models;
|
||||
using GattServicesLibrary.Services;
|
||||
using Template10.Mvvm;
|
||||
using Template10.Services.NavigationService;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
|
||||
namespace BluetoothLEExplorer.ViewModels
|
||||
{
|
||||
/// <summary>
|
||||
/// View Model for the <see cref="MicrosoftService"/> view
|
||||
/// </summary>
|
||||
public class MicrosoftServicePageViewModel : ViewModelBase
|
||||
{
|
||||
/// <summary>
|
||||
/// App context
|
||||
/// </summary>
|
||||
private GattSampleContext context = GattSampleContext.Context;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the selected service view model
|
||||
/// </summary>
|
||||
public GenericGattServiceViewModel ServiceVM { get; set; } = GattSampleContext.Context.SelectedGattServerService;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the selected service
|
||||
/// </summary>
|
||||
public MicrosoftService Service { get; set; } = GattSampleContext.Context.SelectedGattServerService.Service as MicrosoftService;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the read Characteristic
|
||||
/// </summary>
|
||||
public GenericGattCharacteristicViewModel ReadCharacteristic { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets write Characteristic
|
||||
/// </summary>
|
||||
public GenericGattCharacteristicViewModel WriteCharacteristic { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets notify Characteristic
|
||||
/// </summary>
|
||||
public GenericGattCharacteristicViewModel NotifyCharacteristic { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets indicate Characteristic
|
||||
/// </summary>
|
||||
public GenericGattCharacteristicViewModel IndicateCharacteristic { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the read long characteristic
|
||||
/// </summary>
|
||||
public GenericGattCharacteristicViewModel ReadLongCharacteristic { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MicrosoftServicePageViewModel" /> class.
|
||||
/// </summary>
|
||||
public MicrosoftServicePageViewModel()
|
||||
{
|
||||
ReadCharacteristic = new GenericGattCharacteristicViewModel(Service.ReadCharacteristic);
|
||||
WriteCharacteristic = new GenericGattCharacteristicViewModel(Service.WriteCharacteristic);
|
||||
NotifyCharacteristic = new GenericGattCharacteristicViewModel(Service.NotifyCharacteristic);
|
||||
IndicateCharacteristic = new GenericGattCharacteristicViewModel(Service.IndicateCharacteristic);
|
||||
ReadLongCharacteristic = new GenericGattCharacteristicViewModel(Service.ReadLongCharacteristic);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigate from page
|
||||
/// </summary>
|
||||
/// <param name="args"></param>
|
||||
/// <returns>Navigate from task</returns>
|
||||
public override async Task OnNavigatingFromAsync(NavigatingEventArgs args)
|
||||
{
|
||||
context.SelectedGattServerService = null;
|
||||
args.Cancel = false;
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Go to settings
|
||||
/// </summary>
|
||||
public void GotoSettings() =>
|
||||
NavigationService.Navigate(typeof(Views.SettingsPage), 0);
|
||||
|
||||
/// <summary>
|
||||
/// Go to privacy
|
||||
/// </summary>
|
||||
public void GotoPrivacy() =>
|
||||
NavigationService.Navigate(typeof(Views.SettingsPage), 1);
|
||||
|
||||
/// <summary>
|
||||
/// Go to about
|
||||
/// </summary>
|
||||
public void GotoAbout() =>
|
||||
NavigationService.Navigate(typeof(Views.SettingsPage), 2);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
// <copyright file="SettingsPageViewModel.cs" company="Microsoft Corporation">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//----------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Template10.Mvvm;
|
||||
using Windows.UI.Xaml;
|
||||
|
||||
[module: System.Diagnostics.CodeAnalysis.SuppressMessage(
|
||||
"Microsoft.StyleCop.CSharp.MaintainabilityRules",
|
||||
"SA1402:FileMayOnlyContainASingleClass",
|
||||
Justification = "Template 10 comes this way and it improves readability")]
|
||||
|
||||
namespace BluetoothLEExplorer.ViewModels
|
||||
{
|
||||
/// <summary>
|
||||
/// View Model for the settings page
|
||||
/// </summary>
|
||||
public class SettingsPageViewModel : ViewModelBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the settings part of the settings page
|
||||
/// </summary>
|
||||
public SettingsPartViewModel SettingsPartViewModel { get; } = new SettingsPartViewModel();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the about part of the settings page
|
||||
/// </summary>
|
||||
public AboutPartViewModel AboutPartViewModel { get; } = new AboutPartViewModel();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// View Model for the settings part of the settings page
|
||||
/// </summary>
|
||||
public class SettingsPartViewModel : ViewModelBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Settings service
|
||||
/// </summary>
|
||||
private Services.SettingsServices.SettingsService settings;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SettingsPartViewModel" /> class.
|
||||
/// </summary>
|
||||
public SettingsPartViewModel()
|
||||
{
|
||||
if (Windows.ApplicationModel.DesignMode.DesignModeEnabled)
|
||||
{
|
||||
// designtime
|
||||
}
|
||||
else
|
||||
{
|
||||
settings = Services.SettingsServices.SettingsService.Instance;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the shell should have the back button
|
||||
/// </summary>
|
||||
public bool UseShellBackButton
|
||||
{
|
||||
get
|
||||
{
|
||||
return settings.UseShellBackButton;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
settings.UseShellBackButton = value;
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether light theme should be used
|
||||
/// </summary>
|
||||
public bool UseLightThemeButton
|
||||
{
|
||||
get
|
||||
{
|
||||
return settings.AppTheme.Equals(ApplicationTheme.Light);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
settings.AppTheme = value ? ApplicationTheme.Light : ApplicationTheme.Dark;
|
||||
RaisePropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Text to show on busy page
|
||||
/// </summary>
|
||||
private string busyText = "Please wait...";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the busy text
|
||||
/// </summary>
|
||||
public string BusyText
|
||||
{
|
||||
get
|
||||
{
|
||||
return busyText;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
Set(ref busyText, value);
|
||||
showBusyCommand.RaiseCanExecuteChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delegate command to show busy page
|
||||
/// </summary>
|
||||
private DelegateCommand showBusyCommand;
|
||||
|
||||
/// <summary>
|
||||
/// Show busy command delegate
|
||||
/// </summary>
|
||||
public DelegateCommand ShowBusyCommand
|
||||
=> showBusyCommand ?? (showBusyCommand = new DelegateCommand(async () =>
|
||||
{
|
||||
Views.Busy.SetBusy(true, busyText);
|
||||
await Task.Delay(5000);
|
||||
Views.Busy.SetBusy(false);
|
||||
},
|
||||
() => !string.IsNullOrEmpty(BusyText)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// View Model of the about part of the settings page
|
||||
/// </summary>
|
||||
public class AboutPartViewModel : ViewModelBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the logo of this application
|
||||
/// </summary>
|
||||
public Uri Logo => Windows.ApplicationModel.Package.Current.Logo;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the display name of this application
|
||||
/// </summary>
|
||||
public string DisplayName => Windows.ApplicationModel.Package.Current.DisplayName;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the publisher of this application
|
||||
/// </summary>
|
||||
public string Publisher => Windows.ApplicationModel.Package.Current.PublisherDisplayName;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the version string
|
||||
/// </summary>
|
||||
public string Version
|
||||
{
|
||||
get
|
||||
{
|
||||
var v = Windows.ApplicationModel.Package.Current.Id.Version;
|
||||
return $"{v.Major}.{v.Minor}.{v.Build}.{v.Revision}";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
// <copyright file="ShellViewModel.cs" company="Microsoft Corporation">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//----------------------------------------------------------------------------------------------
|
||||
using BluetoothLEExplorer.Models;
|
||||
using Template10.Mvvm;
|
||||
|
||||
namespace BluetoothLEExplorer.ViewModels
|
||||
{
|
||||
/// <summary>
|
||||
/// View Model for the shell
|
||||
/// </summary>
|
||||
public class ShellViewModel : ViewModelBase
|
||||
{
|
||||
/// <summary>
|
||||
/// App context
|
||||
/// </summary>
|
||||
private GattSampleContext context = GattSampleContext.Context;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this host supports peripheral role
|
||||
/// </summary>
|
||||
public bool IsPeripheralRoleSupported
|
||||
{
|
||||
get
|
||||
{
|
||||
return context.IsPeripheralRoleSupported;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this host supports central role
|
||||
/// </summary>
|
||||
public bool IsCentralRoleSupported
|
||||
{
|
||||
get
|
||||
{
|
||||
return context.IsCentralRoleSupported;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ShellViewModel" /> class.
|
||||
/// </summary>
|
||||
public ShellViewModel()
|
||||
{
|
||||
if (Windows.ApplicationModel.DesignMode.DesignModeEnabled)
|
||||
{
|
||||
}
|
||||
|
||||
context.PropertyChanged += Context_PropertyChanged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Property changed
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void Context_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == "IsPeripheralRoleSupported")
|
||||
{
|
||||
this.RaisePropertyChanged("IsPeripheralRoleSupported");
|
||||
}
|
||||
|
||||
if (e.PropertyName == "IsCentralRoleSupported")
|
||||
{
|
||||
RaisePropertyChanged("IsCentralRoleSupported");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,241 @@
|
|||
// <copyright file="VirtualPeripheralPageViewModel.cs" company="Microsoft Corporation">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//----------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BluetoothLEExplorer.Models;
|
||||
using Template10.Mvvm;
|
||||
using Template10.Services.NavigationService;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
|
||||
namespace BluetoothLEExplorer.ViewModels
|
||||
{
|
||||
/// <summary>
|
||||
/// View Model for the Virtual Peripheral Page
|
||||
/// </summary>
|
||||
public class VirtualPeripheralPageViewModel : ViewModelBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the available services
|
||||
/// </summary>
|
||||
public List<string> AvailableServices { get; set; } = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the newly selected service
|
||||
/// </summary>
|
||||
public string NewSelectedService { get; set; } = String.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Source for <see cref="SelectedService"/>
|
||||
/// </summary>
|
||||
private GenericGattServiceViewModel selectedService;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the currently selected service
|
||||
/// </summary>
|
||||
public GenericGattServiceViewModel SelectedService
|
||||
{
|
||||
get
|
||||
{
|
||||
return selectedService;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (selectedService != value && value != null)
|
||||
{
|
||||
Set(ref selectedService, value, "SelectedService");
|
||||
Context.SelectedGattServerService = selectedService;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this host supports peripheral role
|
||||
/// </summary>
|
||||
public bool IsPeripheralRoleSupported
|
||||
{
|
||||
get
|
||||
{
|
||||
return GattSampleContext.Context.IsPeripheralRoleSupported;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this host supports central role
|
||||
/// </summary>
|
||||
public bool IsCentralRoleSupported
|
||||
{
|
||||
get
|
||||
{
|
||||
return Context.IsCentralRoleSupported;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="VirtualPeripheralPageViewModel" /> class.
|
||||
/// </summary>
|
||||
public VirtualPeripheralPageViewModel()
|
||||
{
|
||||
Context.PropertyChanged += Context_PropertyChanged;
|
||||
AvailableServices.Add("Battery");
|
||||
AvailableServices.Add("Microsoft Service");
|
||||
AvailableServices.Add("Heart Rate Service");
|
||||
AvailableServices.Add("Blood Pressure Service");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Callback when context changes
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void Context_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == "IsPeripheralRoleSupported")
|
||||
{
|
||||
this.RaisePropertyChanged("IsPeripheralRoleSupported");
|
||||
}
|
||||
|
||||
if (e.PropertyName == "IsCentralRoleSupported")
|
||||
{
|
||||
RaisePropertyChanged("IsCentralRoleSupported");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the context
|
||||
/// </summary>
|
||||
public GattSampleContext Context { get; } = GattSampleContext.Context;
|
||||
|
||||
/// <summary>
|
||||
/// Creates the service that was selected
|
||||
/// </summary>
|
||||
public async void CreateService()
|
||||
{
|
||||
GattServicesLibrary.GenericGattService service = null;
|
||||
|
||||
if (NewSelectedService == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Views.Busy.SetBusy(true, "Creating Service");
|
||||
|
||||
switch(NewSelectedService)
|
||||
{
|
||||
case "Battery":
|
||||
service = new GattServicesLibrary.Services.BatteryService();
|
||||
break;
|
||||
|
||||
case "Microsoft Service":
|
||||
service = new GattServicesLibrary.Services.MicrosoftService();
|
||||
break;
|
||||
|
||||
case "Heart Rate Service":
|
||||
service = new GattServicesLibrary.Services.HeartRateService();
|
||||
break;
|
||||
|
||||
case "Blood Pressure Service":
|
||||
service = new GattServicesLibrary.Services.BloodPressureService();
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
await service.Init();
|
||||
GenericGattServiceViewModel serviceVM = new GenericGattServiceViewModel(service);
|
||||
Context.CreatedServices.Add(serviceVM);
|
||||
Context.SelectedGattServerService = serviceVM;
|
||||
NavigateToService();
|
||||
return;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes <see cref="NavigateToService"/>
|
||||
/// </summary>
|
||||
public void NavigateToService()
|
||||
{
|
||||
if (Context.SelectedGattServerService == null)
|
||||
{
|
||||
Views.Busy.SetBusy(false);
|
||||
return;
|
||||
}
|
||||
|
||||
switch(Context.SelectedGattServerService.Service.Name)
|
||||
{
|
||||
case "Battery Service":
|
||||
Views.Busy.SetBusy(false);
|
||||
NavigationService.Navigate(typeof(Views.BatteryServicePage));
|
||||
break;
|
||||
|
||||
case "Microsoft Service":
|
||||
Views.Busy.SetBusy(false);
|
||||
NavigationService.Navigate(typeof(Views.MicrosoftServicePage));
|
||||
break;
|
||||
|
||||
case "Heart Rate Service":
|
||||
Views.Busy.SetBusy(false);
|
||||
NavigationService.Navigate(typeof(Views.HeartRateServicePage));
|
||||
break;
|
||||
|
||||
case "Blood Pressure Service":
|
||||
Views.Busy.SetBusy(false);
|
||||
NavigationService.Navigate(typeof(Views.BloodPressureServicePage));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigating to page
|
||||
/// </summary>
|
||||
/// <param name="parameter"></param>
|
||||
/// <param name="mode"></param>
|
||||
/// <param name="suspensionState"></param>
|
||||
/// <returns>Navigate to task</returns>
|
||||
public override async Task OnNavigatedToAsync(object parameter, NavigationMode mode, IDictionary<string, object> suspensionState)
|
||||
{
|
||||
if (suspensionState.Any())
|
||||
{
|
||||
}
|
||||
|
||||
Context.SelectedGattServerService = null;
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigating from page
|
||||
/// </summary>
|
||||
/// <param name="args"></param>
|
||||
/// <returns>Navigate from task</returns>
|
||||
public override async Task OnNavigatingFromAsync(NavigatingEventArgs args)
|
||||
{
|
||||
args.Cancel = false;
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Go to settings
|
||||
/// </summary>
|
||||
public void GotoSettings() =>
|
||||
NavigationService.Navigate(typeof(Views.SettingsPage), 0);
|
||||
|
||||
/// <summary>
|
||||
/// Go to private
|
||||
/// </summary>
|
||||
public void GotoPrivacy() =>
|
||||
NavigationService.Navigate(typeof(Views.SettingsPage), 1);
|
||||
|
||||
/// <summary>
|
||||
/// Go to about
|
||||
/// </summary>
|
||||
public void GotoAbout() =>
|
||||
NavigationService.Navigate(typeof(Views.SettingsPage), 2);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
<Page x:Class="BluetoothLEExplorer.Views.Beacon"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:Behaviors="using:Template10.Behaviors"
|
||||
xmlns:Core="using:Microsoft.Xaml.Interactions.Core"
|
||||
xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
|
||||
xmlns:controls="using:Template10.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:BluetoothLEExplorer.Views"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:vm="using:BluetoothLEExplorer.ViewModels" mc:Ignorable="d"
|
||||
xmlns:models="using:BluetoothLEExplorer.Models">
|
||||
|
||||
<Page.DataContext>
|
||||
<vm:BeaconViewModel x:Name="ViewModel" />
|
||||
</Page.DataContext>
|
||||
|
||||
<RelativePanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="AdaptiveVisualStateGroup">
|
||||
<VisualState x:Name="VisualStateDisplayType">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="{StaticResource MinDisplayTypeWidth}" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<!-- TODO: change properties for narrow view -->
|
||||
<Setter Target="stateTextBox.Text" Value="DisplayType Visual State" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
|
||||
|
||||
|
||||
|
||||
<VisualState x:Name="VisualStateNarrow">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="{StaticResource NarrowMinWidth}" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<!-- TODO: change properties for narrow view -->
|
||||
<Setter Target="stateTextBox.Text" Value="Narrow Visual State" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
|
||||
|
||||
<VisualState x:Name="VisualStateNormal">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="{StaticResource NormalMinWidth}" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<!-- TODO: change properties for normal view -->
|
||||
<Setter Target="stateTextBox.Text" Value="Normal Visual State" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="VisualStateWide">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="{StaticResource WideMinWidth}" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<!-- TODO: change properties for wide view -->
|
||||
<Setter Target="stateTextBox.Text" Value="Wide Visual State" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
|
||||
<controls:PageHeader x:Name="pageHeader" RelativePanel.AlignLeftWithPanel="True"
|
||||
RelativePanel.AlignRightWithPanel="True"
|
||||
RelativePanel.AlignTopWithPanel="True" Text="Beacon">
|
||||
|
||||
<!-- secondary commands -->
|
||||
<controls:PageHeader.SecondaryCommands>
|
||||
<AppBarButton Click="{x:Bind ViewModel.GotoSettings}" Label="Settings" />
|
||||
<AppBarButton Click="{x:Bind ViewModel.GotoPrivacy}" Label="Privacy" />
|
||||
<AppBarButton Click="{x:Bind ViewModel.GotoAbout}" Label="About" />
|
||||
</controls:PageHeader.SecondaryCommands>
|
||||
|
||||
</controls:PageHeader>
|
||||
|
||||
<controls:Resizer x:Name="parameterResizer" Margin="16,16,16,0"
|
||||
RelativePanel.AlignLeftWithPanel="True"
|
||||
RelativePanel.Below="pageHeader">
|
||||
|
||||
<TextBox MinWidth="150" MinHeight="62"
|
||||
Header="Parameter to pass"
|
||||
Text="{Binding Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
TextWrapping="Wrap">
|
||||
<Interactivity:Interaction.Behaviors>
|
||||
<!-- enable submit on enter key -->
|
||||
<Behaviors:KeyBehavior Key="Enter">
|
||||
<Core:CallMethodAction MethodName="GotoDetailsPage" TargetObject="{Binding}" />
|
||||
</Behaviors:KeyBehavior>
|
||||
<!-- focus on textbox when page loads -->
|
||||
<Core:EventTriggerBehavior>
|
||||
<Behaviors:FocusAction />
|
||||
</Core:EventTriggerBehavior>
|
||||
</Interactivity:Interaction.Behaviors>
|
||||
</TextBox>
|
||||
|
||||
</controls:Resizer>
|
||||
|
||||
<TextBlock x:Name="stateTextBox" Margin="16,16,0,0"
|
||||
RelativePanel.AlignLeftWith="parameterResizer"
|
||||
RelativePanel.Below="parameterResizer"
|
||||
Text="Current Visual State" />
|
||||
|
||||
|
||||
<TextBlock x:Name="labelMyCounter"
|
||||
Text="MyCounter:"
|
||||
RelativePanel.AlignLeftWithPanel="True"
|
||||
RelativePanel.Below="stateTextBox" />
|
||||
|
||||
<TextBlock MinWidth="150" MinHeight="31"
|
||||
Text="{Binding MyCounter}"
|
||||
TextWrapping="Wrap"
|
||||
RelativePanel.RightOf="labelMyCounter"
|
||||
RelativePanel.AlignVerticalCenterWith="labelMyCounter">
|
||||
</TextBlock>
|
||||
|
||||
|
||||
|
||||
</RelativePanel>
|
||||
</Page>
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
// <copyright file="Beacon.xaml.cs" company="Microsoft Corporation">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//----------------------------------------------------------------------------------------------
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
//// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
|
||||
|
||||
namespace BluetoothLEExplorer.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
public sealed partial class Beacon : Page
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Beacon" /> class.
|
||||
/// </summary>
|
||||
public Beacon()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
<UserControl x:Class="BluetoothLEExplorer.Views.Busy"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:BluetoothLEExplorer.Views"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
d:DesignHeight="300" d:DesignWidth="400"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Viewbox Height="32" HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ProgressRing Width="16" Height="16"
|
||||
Foreground="White" IsActive="{x:Bind IsBusy, Mode=OneWay}" />
|
||||
<TextBlock Grid.Column="1" Margin="12,0,0,0"
|
||||
VerticalAlignment="Center" Foreground="White"
|
||||
Text="{x:Bind BusyText, Mode=OneWay, FallbackValue='BusyText'}" />
|
||||
</Grid>
|
||||
</Viewbox>
|
||||
|
||||
</UserControl>
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
// <copyright file="Busy.xaml.cs" company="Microsoft Corporation">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//----------------------------------------------------------------------------------------------
|
||||
using Template10.Common;
|
||||
using Template10.Controls;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace BluetoothLEExplorer.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// Busy overlay
|
||||
/// </summary>
|
||||
public sealed partial class Busy : UserControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Busy" /> class.
|
||||
/// </summary>
|
||||
public Busy()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value of the busy text
|
||||
/// </summary>
|
||||
public string BusyText
|
||||
{
|
||||
get { return (string)GetValue(BusyTextProperty); }
|
||||
set { SetValue(BusyTextProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Busy text property
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty BusyTextProperty = DependencyProperty.Register(nameof(BusyText), typeof(string), typeof(Busy), new PropertyMetadata("Please wait..."));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the busy screen should be displayed
|
||||
/// </summary>
|
||||
public bool IsBusy
|
||||
{
|
||||
get { return (bool)GetValue(IsBusyProperty); }
|
||||
set { SetValue(IsBusyProperty, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dependency object for is busy property
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty IsBusyProperty = DependencyProperty.Register(nameof(IsBusy), typeof(bool), typeof(Busy), new PropertyMetadata(false));
|
||||
|
||||
/// <summary>
|
||||
/// hide and show busy dialog
|
||||
/// </summary>
|
||||
/// <param name="busy"></param>
|
||||
/// <param name="text"></param>
|
||||
public static void SetBusy(bool busy, string text = null)
|
||||
{
|
||||
WindowWrapper.Current().Dispatcher.Dispatch(() =>
|
||||
{
|
||||
var modal = Window.Current.Content as ModalDialog;
|
||||
var view = modal.ModalContent as Busy;
|
||||
|
||||
if (view == null)
|
||||
{
|
||||
modal.ModalContent = view = new Busy();
|
||||
}
|
||||
|
||||
modal.IsModal = view.IsBusy = busy;
|
||||
view.BusyText = text;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,292 @@
|
|||
<Page x:Class="BluetoothLEExplorer.Views.CharacteristicPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:Behaviors="using:Template10.Behaviors"
|
||||
xmlns:Core="using:Microsoft.Xaml.Interactions.Core"
|
||||
xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
|
||||
xmlns:controls="using:Template10.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:BluetoothLEExplorer.Views"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:converters="using:Template10.Converters"
|
||||
xmlns:myconverters="using:BluetoothLEExplorer.Services.Converters"
|
||||
xmlns:vm="using:BluetoothLEExplorer.ViewModels" x:Name="ThisPage"
|
||||
xmlns:models="using:BluetoothLEExplorer.Models"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Page.DataContext>
|
||||
<vm:CharacteristicPageViewModel x:Name="ViewModel" />
|
||||
</Page.DataContext>
|
||||
|
||||
<Page.Resources>
|
||||
<converters:ValueWhenConverter x:Key="VisibleWhenBlankConverter" When="">
|
||||
<converters:ValueWhenConverter.Value>
|
||||
<Visibility>Collapsed</Visibility>
|
||||
</converters:ValueWhenConverter.Value>
|
||||
<converters:ValueWhenConverter.Otherwise>
|
||||
<Visibility>Visible</Visibility>
|
||||
</converters:ValueWhenConverter.Otherwise>
|
||||
</converters:ValueWhenConverter>
|
||||
|
||||
<converters:ValueWhenConverter x:Key="VisibleWhenTrueConverter">
|
||||
<converters:ValueWhenConverter.When>
|
||||
<x:Boolean>True</x:Boolean>
|
||||
</converters:ValueWhenConverter.When>
|
||||
<converters:ValueWhenConverter.Value>
|
||||
<Visibility>Visible</Visibility>
|
||||
</converters:ValueWhenConverter.Value>
|
||||
<converters:ValueWhenConverter.Otherwise>
|
||||
<Visibility>Collapsed</Visibility>
|
||||
</converters:ValueWhenConverter.Otherwise>
|
||||
</converters:ValueWhenConverter>
|
||||
|
||||
<myconverters:DisplayTypeToBooleanConverter x:Key="DisplayTypeToBooleanConverter" />
|
||||
<myconverters:WriteTypeToBooleanConverter x:Key="WriteTypeToBooleanConverter" />
|
||||
|
||||
|
||||
</Page.Resources>
|
||||
|
||||
<RelativePanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="AdaptiveVisualStateGroup">
|
||||
|
||||
|
||||
|
||||
<VisualState x:Name="VisualStateNarrow">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="{StaticResource NarrowMinWidth}" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<!-- TODO: change properties for narrow view -->
|
||||
<Setter Target="DisplayType.(RelativePanel.Below)" Value="ReadAgainButton" />
|
||||
<Setter Target="DisplayType.(RelativePanel.AlignLeftWith)" Value="ReadValueText" />
|
||||
<Setter Target="ReadValueText.(RelativePanel.AlignVerticalCenterWithPanel)" Value="False" />
|
||||
<Setter Target="Value.(FrameworkElement.MaxWidth)" Value="180"/>
|
||||
<Setter Target="radioButton.(ContentControl.Content)" Value="Dec"/>
|
||||
<Setter Target="radioButton.(FrameworkElement.Margin)">
|
||||
<Setter.Value>
|
||||
<Thickness>0,0,-45,0</Thickness>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Target="radioButton1.(FrameworkElement.Margin)">
|
||||
<Setter.Value>
|
||||
<Thickness>0,0,-45,0</Thickness>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Target="radioButton2.(FrameworkElement.Margin)">
|
||||
<Setter.Value>
|
||||
<Thickness>0,0,-45,0</Thickness>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Target="radioButton3.(FrameworkElement.Margin)">
|
||||
<Setter.Value>
|
||||
<Thickness>0,0,-45,0</Thickness>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Target="ReadAgainButton.(RelativePanel.AlignLeftWithPanel)" Value="True"/>
|
||||
<Setter Target="ReadAgainButton.(RelativePanel.AlignTopWith)" Value=""/>
|
||||
<Setter Target="ReadAgainButton.(RelativePanel.Below)" Value="Value"/>
|
||||
<Setter Target="ReadAgainButton.(FrameworkElement.Width)" Value="300"/>
|
||||
<Setter Target="WriteType.(RelativePanel.Below)" Value="WriteButton"/>
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="VisualStateNormal">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="{StaticResource NormalMinWidth}" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<!-- TODO: change properties for normal view -->
|
||||
<Setter Target="DisplayType.(RelativePanel.Below)" Value="ReadValueText" />
|
||||
<Setter Target="DisplayType.(RelativePanel.AlignLeftWith)" Value="ReadValueText" />
|
||||
<Setter Target="ReadValueText.(RelativePanel.AlignVerticalCenterWithPanel)" Value="False" />
|
||||
<Setter Target="radioButton.(ContentControl.Content)" Value="Dec"/>
|
||||
<Setter Target="radioButton.(FrameworkElement.Margin)">
|
||||
<Setter.Value>
|
||||
<Thickness>0,0,-45,0</Thickness>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Target="radioButton1.(FrameworkElement.Margin)">
|
||||
<Setter.Value>
|
||||
<Thickness>0,0,-45,0</Thickness>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Target="radioButton2.(FrameworkElement.Margin)">
|
||||
<Setter.Value>
|
||||
<Thickness>0,0,-45,0</Thickness>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Target="radioButton3.(FrameworkElement.Margin)">
|
||||
<Setter.Value>
|
||||
<Thickness>0,0,-45,0</Thickness>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Target="Value.(FrameworkElement.MaxWidth)" Value="180"/>
|
||||
<Setter Target="ReadAgainButton.(RelativePanel.RightOf)" Value="Value"/>
|
||||
<Setter Target="WriteType.(RelativePanel.Below)" Value="WriteButton"/>
|
||||
<Setter Target="WriteType.(RelativePanel.AlignLeftWithPanel)" Value="True"/>
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="VisualStateDisplayType">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="{StaticResource MinDisplayTypeWidth}" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<!-- TODO: change properties for narrow view -->
|
||||
<Setter Target="DisplayType.(RelativePanel.RightOf)" Value="" />
|
||||
<Setter Target="DisplayType.(RelativePanel.Below)" Value="Value"/>
|
||||
<Setter Target="DisplayType.(RelativePanel.AlignLeftWithPanel)" Value="True"/>
|
||||
<Setter Target="radioButton.(ContentControl.Content)" Value="Decimal"/>
|
||||
<Setter Target="ReadAgainButton.(RelativePanel.RightOf)" Value="Value"/>
|
||||
<Setter Target="WriteType.(RelativePanel.RightOf)" Value=""/>
|
||||
<Setter Target="WriteType.(RelativePanel.Below)" Value="WriteButton"/>
|
||||
<Setter Target="WriteType.(RelativePanel.AlignLeftWithPanel)" Value="True"/>
|
||||
</VisualState.Setters>
|
||||
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="VisualStateWide">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="{StaticResource WideMinWidth}" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<!-- TODO: change properties for wide view -->
|
||||
<Setter Target="DisplayType.(RelativePanel.RightOf)" Value="ReadAgainButton" />
|
||||
<Setter Target="ReadAgainButton.(RelativePanel.RightOf)" Value="Value"/>
|
||||
<Setter Target="DisplayType.(RelativePanel.AlignLeftWithPanel)" Value="False"/>
|
||||
<Setter Target="DisplayType.(RelativePanel.Below)" Value=""/>
|
||||
<Setter Target="WriteType.(RelativePanel.RightOf)" Value="WriteButton"/>
|
||||
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
|
||||
<!-- header -->
|
||||
<controls:PageHeader x:Name="pageHeader" Frame="{x:Bind Frame}"
|
||||
RelativePanel.AlignLeftWithPanel="True"
|
||||
RelativePanel.AlignRightWithPanel="True"
|
||||
RelativePanel.AlignTopWithPanel="True" Text="Characteristics Page" />
|
||||
|
||||
<!-- content -->
|
||||
|
||||
<Grid x:Name="Content" Padding="10"
|
||||
RelativePanel.Below="pageHeader"
|
||||
RelativePanel.AlignLeftWithPanel="True"
|
||||
RelativePanel.AlignRightWithPanel="True"
|
||||
RelativePanel.AlignBottomWith="Notify">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="auto" />
|
||||
<!-- Row 0 -->
|
||||
<RowDefinition Height="auto"/>
|
||||
<!-- Row 1 -->
|
||||
<RowDefinition Height="auto"/>
|
||||
<!-- Row 2 -->
|
||||
<RowDefinition Height="auto" />
|
||||
<!-- Row 3 -->
|
||||
<RowDefinition Height="auto" />
|
||||
<!-- Row 4 -->
|
||||
<RowDefinition Height="*" />
|
||||
<!-- Row 5 -->
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Grid.Row="0" >
|
||||
<Run Text="{x:Bind ViewModel.DeviceName}" />
|
||||
<Run Text=" - " />
|
||||
<Run Text="{x:Bind ViewModel.Characteristic.Parent.Name}" />
|
||||
<Run Text=" - " />
|
||||
<Run Text="{x:Bind ViewModel.Characteristic.Name}" />
|
||||
</TextBlock>
|
||||
|
||||
<!--<StackPanel Grid.Row="1" Orientation="Horizontal" >-->
|
||||
<StackPanel Grid.Row="1">
|
||||
<!--<TextBlock Foreground="Red" Text="Unsupported presentation format - using hex" Visibility="{x:Bind ViewModel.DisplayPresenationError}" />-->
|
||||
|
||||
<RelativePanel VerticalAlignment="Center">
|
||||
<TextBlock x:Name="HandleValueText" Text="Handle: " VerticalAlignment="Center" Width="100" Height="32" Margin="0,5,5,5" />
|
||||
<TextBlock Text="{x:Bind ViewModel.Characteristic.Characteristic.AttributeHandle, Mode=OneWay}" RelativePanel.RightOf="HandleValueText" RelativePanel.AlignVerticalCenterWith="HandleValueText" Width="100" Height="32" Margin="5" />
|
||||
</RelativePanel>
|
||||
<RelativePanel VerticalAlignment="Center" Visibility="{x:Bind ViewModel.CharacteristicValueVisible, Converter={StaticResource VisibleWhenTrueConverter}}" >
|
||||
<TextBlock Name="ReadValueText" Text="Read Value: " VerticalAlignment="Center" Width="100" Height="32" Margin="0,5,5,5"/>
|
||||
<Border Name="Value" Background="LightGray" RelativePanel.RightOf="ReadValueText" RelativePanel.AlignVerticalCenterWith="ReadValueText" MinWidth="75" Height="32" Margin="5" >
|
||||
<TextBlock Text="{x:Bind ViewModel.Characteristic.Value, Mode=OneWay}" VerticalAlignment="Center" Margin="5,0" TextWrapping="WrapWholeWords"/>
|
||||
</Border>
|
||||
<Button Name="ReadAgainButton" Content="Read Again" Click="{x:Bind ViewModel.Characteristic.ReadValueAsync}" Visibility="{x:Bind ViewModel.CharacteristicCanBeRead, Converter={StaticResource VisibleWhenTrueConverter}}" Margin="5" Width="100" Height="32" d:LayoutOverrides="LeftMargin, RightMargin" />
|
||||
|
||||
<!-- The radio buttons need to be in different groups because they are all bound to the same property, so it takes care of which ones stays on vs off. If there is no group then the stackpanel container -->
|
||||
<!-- groups them and it conflicts with the value converter -->
|
||||
<StackPanel Name="DisplayType" Orientation="Horizontal" Margin="5" >
|
||||
<RadioButton x:Name="radioButton" Content="Decimal" IsChecked="{Binding Characteristic.DisplayType, Mode=TwoWay, Converter={StaticResource DisplayTypeToBooleanConverter}, ConverterParameter=Decimal }" Width="100" GroupName="a"/>
|
||||
<RadioButton x:Name="radioButton1" Content="Hex" IsChecked="{Binding Characteristic.DisplayType, Mode=TwoWay, Converter={StaticResource DisplayTypeToBooleanConverter}, ConverterParameter=Hex }" Width="100" GroupName="b"/>
|
||||
<RadioButton x:Name="radioButton2" Content="UTF8" IsChecked="{Binding Characteristic.DisplayType, Mode=TwoWay, Converter={StaticResource DisplayTypeToBooleanConverter}, ConverterParameter=UTF8 }" Width="100" GroupName="c"/>
|
||||
<RadioButton x:Name="radioButton3" Content="UTF16" IsChecked="{Binding Characteristic.DisplayType, Mode=TwoWay, Converter={StaticResource DisplayTypeToBooleanConverter}, ConverterParameter=UTF16 }" Width="100" GroupName="d"/>
|
||||
</StackPanel>
|
||||
</RelativePanel>
|
||||
</StackPanel>
|
||||
|
||||
<RelativePanel Grid.Row="2" VerticalAlignment="Center" Visibility="{x:Bind ViewModel.CharacteristicCanWrite, Converter={StaticResource VisibleWhenTrueConverter}}">
|
||||
<TextBlock x:Name="WriteValueText" Text="Write Value: " Margin="0,5,5,5" VerticalAlignment="Center" Width="100" />
|
||||
<TextBox x:Name="WriteValue" Text="{x:Bind ViewModel.ValueToWrite, Mode=TwoWay}" VerticalAlignment="Center" Width="75" Margin="5"
|
||||
RelativePanel.RightOf="WriteValueText" TextChanging="WriteValue_TextChanging"
|
||||
/>
|
||||
<Button x:Name="WriteButton" Content="Write" Click="{x:Bind ViewModel.WriteValue}" Margin="5" RelativePanel.RightOf="WriteValue" />
|
||||
<!-- The radio buttons need to be in different groups because they are all bound to the same property, so it takes care of which ones stays on vs off. If there is no group then the stackpanel container -->
|
||||
<!-- groups them and it conflicts with the value converter -->
|
||||
<StackPanel Name="WriteType" Orientation="Horizontal" Margin="5">
|
||||
<RadioButton x:Name="radioButton4" Content="Decimal" IsChecked="{Binding WriteType, Mode=TwoWay, Converter={StaticResource WriteTypeToBooleanConverter}, ConverterParameter=Decimal }" Width="100" GroupName="e" />
|
||||
<RadioButton x:Name="radioButton5" Content="Hex" IsChecked="{Binding WriteType, Mode=TwoWay, Converter={StaticResource WriteTypeToBooleanConverter}, ConverterParameter=Hex }" Width="100" GroupName="f" Checked="radioButton5_Checked" Unchecked="radioButton5_Unchecked" />
|
||||
<RadioButton x:Name="radioButton6" Content="UTF8" IsChecked="{Binding WriteType, Mode=TwoWay, Converter={StaticResource WriteTypeToBooleanConverter}, ConverterParameter=UTF8 }" Width="100" GroupName="g" />
|
||||
</StackPanel>
|
||||
</RelativePanel>
|
||||
<TextBlock x:Name="TextProperties" Grid.Row="3" TextWrapping="WrapWholeWords">
|
||||
<Run Text="Properties: " FontWeight="Bold" />
|
||||
<Run Text="{x:Bind ViewModel.Properties}" />
|
||||
</TextBlock>
|
||||
|
||||
<StackPanel Grid.Row="4">
|
||||
<CheckBox IsChecked="{x:Bind ViewModel.UseWindowsNotifications, Mode=TwoWay}" Content="Use Windows notifications" Visibility="{x:Bind ViewModel.CharacteristicCanNotifyOrIndicate, Converter={StaticResource VisibleWhenTrueConverter}}" />
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Visibility="{x:Bind ViewModel.CharacteristicCanNotify, Converter={StaticResource VisibleWhenTrueConverter}}">
|
||||
<ToggleSwitch x:Name="NotifySwitch" Header="Notify"
|
||||
OffContent="Off" OnContent="On" IsOn="{x:Bind ViewModel.Notify, Mode=TwoWay}" />
|
||||
<ProgressRing Visibility="{x:Bind ViewModel.NotifyProgress, Mode=OneWay, Converter={StaticResource VisibleWhenTrueConverter}}"
|
||||
IsActive="{x:Bind ViewModel.NotifyProgress, Mode=OneWay}" />
|
||||
<TextBlock Text="Error setting notification" Foreground="Red"
|
||||
Margin="-60, 30, 0, 0"
|
||||
Visibility="{x:Bind ViewModel.NotifyError, Mode=OneWay, Converter={StaticResource VisibleWhenTrueConverter}}" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Visibility="{x:Bind ViewModel.CharacteristicCanIndicate, Converter={StaticResource VisibleWhenTrueConverter}}">
|
||||
<ToggleSwitch x:Name="IndicateSwitch" Header="Indicate"
|
||||
OffContent="Off" OnContent="On" IsOn="{x:Bind ViewModel.Indicate, Mode=TwoWay}"/>
|
||||
<ProgressRing Visibility="{x:Bind ViewModel.IndicateProgress, Mode=OneWay, Converter={StaticResource VisibleWhenTrueConverter}}"
|
||||
IsActive="{x:Bind ViewModel.NotifyProgress, Mode=OneWay}"/>
|
||||
<TextBlock x:Name="IndicateError" Text="Error setting notification" Foreground="Red"
|
||||
Margin="-60, 30, 0, 0"
|
||||
Visibility="{x:Bind ViewModel.IndicateError, Mode=OneWay, Converter={StaticResource VisibleWhenTrueConverter}}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<RelativePanel x:Name="Notify" Background="LightBlue" Height="60"
|
||||
RelativePanel.AlignLeftWithPanel="True"
|
||||
RelativePanel.AlignRightWithPanel="True"
|
||||
RelativePanel.AlignBottomWithPanel="True">
|
||||
|
||||
<ScrollViewer Padding="10,0,0,0"
|
||||
RelativePanel.AlignLeftWithPanel="True"
|
||||
RelativePanel.AlignTopWithPanel="True"
|
||||
RelativePanel.AlignRightWithPanel="True"
|
||||
RelativePanel.AlignBottomWithPanel="True">
|
||||
|
||||
<ItemsControl Padding="10,0,10,0" ItemsSource="{x:Bind ViewModel.NotifyUser}" />
|
||||
</ScrollViewer>
|
||||
</RelativePanel>
|
||||
<!--<Rectangle x:Name="Notify" Fill="Orange" Margin="-10,0,0,-10" Height="60"
|
||||
RelativePanel.AlignLeftWithPanel="True"
|
||||
RelativePanel.AlignRightWithPanel="True"
|
||||
RelativePanel.AlignBottomWithPanel="True"/>-->
|
||||
|
||||
</RelativePanel>
|
||||
</Page>
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
// <copyright file="CharacteristicPage.xaml.cs" company="Microsoft Corporation">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//----------------------------------------------------------------------------------------------
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
|
||||
namespace BluetoothLEExplorer.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// Characteristic Page
|
||||
/// </summary>
|
||||
public sealed partial class CharacteristicPage : Page
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CharacteristicPage" /> class.
|
||||
/// </summary>
|
||||
public CharacteristicPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
NavigationCacheMode = NavigationCacheMode.Disabled;
|
||||
}
|
||||
|
||||
private void WriteValue_TextChanging(TextBox sender, TextBoxTextChangingEventArgs args)
|
||||
{
|
||||
WriteBoxBackgroundCheck();
|
||||
}
|
||||
|
||||
private void radioButton5_Checked(object sender, Windows.UI.Xaml.RoutedEventArgs e)
|
||||
{
|
||||
WriteBoxBackgroundCheck();
|
||||
}
|
||||
|
||||
private void radioButton5_Unchecked(object sender, Windows.UI.Xaml.RoutedEventArgs e)
|
||||
{
|
||||
WriteBoxBackgroundCheck();
|
||||
}
|
||||
|
||||
private void WriteBoxBackgroundCheck()
|
||||
{
|
||||
if (ViewModel.WriteType == ViewModels.CharacteristicPageViewModel.WriteTypes.Hex)
|
||||
{
|
||||
int buf;
|
||||
if ((WriteValue.Text != string.Empty) &&
|
||||
(int.TryParse(WriteValue.Text, System.Globalization.NumberStyles.HexNumber, null, out buf) == false))
|
||||
{
|
||||
WriteValue.Background = new SolidColorBrush(Windows.UI.Colors.Red);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteValue.Background = new SolidColorBrush(Windows.UI.Colors.White);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteValue.Background = new SolidColorBrush(Windows.UI.Colors.White);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,153 @@
|
|||
<Page x:Class="BluetoothLEExplorer.Views.DeviceServicesPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:Behaviors="using:Template10.Behaviors"
|
||||
xmlns:Core="using:Microsoft.Xaml.Interactions.Core"
|
||||
xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
|
||||
xmlns:controls="using:Template10.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:BluetoothLEExplorer.Views"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:converters="using:Template10.Converters"
|
||||
xmlns:vm="using:BluetoothLEExplorer.ViewModels" x:Name="ThisPage"
|
||||
xmlns:models="using:BluetoothLEExplorer.Models"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Page.DataContext>
|
||||
<vm:DeviceServicesPageViewModel x:Name="ViewModel" />
|
||||
</Page.DataContext>
|
||||
|
||||
<Page.Resources>
|
||||
<converters:ValueWhenConverter x:Key="VisibleWhenBlankConverter" When="">
|
||||
<converters:ValueWhenConverter.Value>
|
||||
<Visibility>Collapsed</Visibility>
|
||||
</converters:ValueWhenConverter.Value>
|
||||
<converters:ValueWhenConverter.Otherwise>
|
||||
<Visibility>Visible</Visibility>
|
||||
</converters:ValueWhenConverter.Otherwise>
|
||||
</converters:ValueWhenConverter>
|
||||
|
||||
<converters:ValueWhenConverter x:Key="VisibleWhenTrueConverter">
|
||||
<converters:ValueWhenConverter.When>
|
||||
<x:Boolean>True</x:Boolean>
|
||||
</converters:ValueWhenConverter.When>
|
||||
<converters:ValueWhenConverter.Value>
|
||||
<Visibility>Visible</Visibility>
|
||||
</converters:ValueWhenConverter.Value>
|
||||
<converters:ValueWhenConverter.Otherwise>
|
||||
<Visibility>Collapsed</Visibility>
|
||||
</converters:ValueWhenConverter.Otherwise>
|
||||
</converters:ValueWhenConverter>
|
||||
</Page.Resources>
|
||||
|
||||
<RelativePanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="AdaptiveVisualStateGroup">
|
||||
<VisualState x:Name="VisualStateNarrow">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="{StaticResource NarrowMinWidth}" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<!-- TODO: change properties for narrow view -->
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="VisualStateNormal">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="{StaticResource NormalMinWidth}" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<!-- TODO: change properties for normal view -->
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="VisualStateWide">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="{StaticResource WideMinWidth}" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<!-- TODO: change properties for wide view -->
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
|
||||
<!-- header -->
|
||||
<controls:PageHeader x:Name="pageHeader" Frame="{x:Bind Frame}"
|
||||
RelativePanel.AlignLeftWithPanel="True"
|
||||
RelativePanel.AlignRightWithPanel="True"
|
||||
RelativePanel.AlignTopWithPanel="True" Text="Device Services Page" />
|
||||
|
||||
<!-- content -->
|
||||
|
||||
<Grid RelativePanel.Below="pageHeader" Padding="10" RelativePanel.AlignLeftWithPanel="True" RelativePanel.AlignRightWithPanel="True">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<StackPanel Grid.Row="0">
|
||||
<TextBlock Text="{x:Bind ViewModel.Device.ErrorText}" Visibility="{x:Bind ViewModel.Device.ErrorText, Converter={StaticResource VisibleWhenBlankConverter}}" />
|
||||
<TextBlock>
|
||||
<Run Text="BT Address: " />
|
||||
<Run Text="{x:Bind ViewModel.Device.BluetoothAddressAsString, Mode=OneWay}"/>
|
||||
</TextBlock>
|
||||
<TextBlock>
|
||||
<Run Text="Number of Services:" />
|
||||
<Run Text="{x:Bind ViewModel.Device.Services.Count, Mode=OneWay}" />
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
|
||||
<ScrollViewer Grid.Row="1" Margin="0,10,0,0" ScrollViewer.VerticalScrollBarVisibility="Auto" >
|
||||
<ItemsControl x:Name="ServicesListView"
|
||||
ItemsSource="{x:Bind ViewModel.Device.Services, Mode=OneWay}" >
|
||||
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate x:DataType="models:ObservableGattDeviceService" >
|
||||
<StackPanel Margin="0,0,0,15">
|
||||
<TextBlock >
|
||||
<Run Text="Service Name: " />
|
||||
<Run Text="{x:Bind Name}" FontWeight="Bold" />
|
||||
</TextBlock>
|
||||
<TextBlock FontSize="12">
|
||||
<Run Text="Service UUID: " />
|
||||
<Run Text="{x:Bind UUID}" FontWeight="Bold" />
|
||||
</TextBlock>
|
||||
<ListView x:Name="CharacteristicsListView"
|
||||
IsItemClickEnabled="True"
|
||||
SelectionMode="Single"
|
||||
ItemsSource="{x:Bind Characteristics}"
|
||||
ItemClick="CharacteristicsListView_ItemClick" >
|
||||
<ListView.ItemTemplate >
|
||||
<DataTemplate x:DataType="models:ObservableGattCharacteristics" >
|
||||
<TextBlock TextWrapping="WrapWholeWords">
|
||||
<Run x:Name="CharacteristicText" Text="Characteristic Name:" />
|
||||
<Run Text="{x:Bind Name, Mode=OneWay}" FontWeight="Bold" />
|
||||
<Run Text=" - " />
|
||||
<Run x:Name="CharacteristicHandleText" Text="Handle: " />
|
||||
<Run Text="{x:Bind Characteristic.AttributeHandle, Mode=OneWay}" FontWeight="Bold" />
|
||||
<Run Text=" - " />
|
||||
<Run x:Name="CharValueText" Text="Value: " />
|
||||
<Run Text="{x:Bind Value, Mode=OneWay}" FontWeight="Bold" />
|
||||
</TextBlock>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
|
||||
</ListView>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
<SemanticZoom Margin="0,9,-118,0">
|
||||
<SemanticZoom.ZoomedInView>
|
||||
<GridView ScrollViewer.IsHorizontalScrollChainingEnabled="False" ScrollViewer.IsVerticalScrollChainingEnabled="False"/>
|
||||
</SemanticZoom.ZoomedInView>
|
||||
<SemanticZoom.ZoomedOutView>
|
||||
<GridView ScrollViewer.IsHorizontalScrollChainingEnabled="False" ScrollViewer.IsVerticalScrollChainingEnabled="False"/>
|
||||
</SemanticZoom.ZoomedOutView>
|
||||
</SemanticZoom>
|
||||
|
||||
</RelativePanel>
|
||||
</Page>
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
// <copyright file="DeviceServicesPage.xaml.cs" company="Microsoft Corporation">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//----------------------------------------------------------------------------------------------
|
||||
using BluetoothLEExplorer.Models;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
|
||||
namespace BluetoothLEExplorer.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// Device Service page
|
||||
/// </summary>
|
||||
public sealed partial class DeviceServicesPage : Page
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DeviceServicesPage" /> class.
|
||||
/// </summary>
|
||||
public DeviceServicesPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
NavigationCacheMode = NavigationCacheMode.Disabled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the view model with the just selected characteristic
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void CharacteristicsListView_ItemClick(object sender, ItemClickEventArgs e)
|
||||
{
|
||||
ViewModel.SelectedCharacteristic = (ObservableGattCharacteristics)e.ClickedItem;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,244 @@
|
|||
<Page x:Class="BluetoothLEExplorer.Views.Discover"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:Behaviors="using:Template10.Behaviors"
|
||||
xmlns:Core="using:Microsoft.Xaml.Interactions.Core"
|
||||
xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
|
||||
xmlns:controls="using:Template10.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:BluetoothLEExplorer.Views"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:converters="using:Template10.Converters"
|
||||
xmlns:vm="using:BluetoothLEExplorer.ViewModels" mc:Ignorable="d"
|
||||
xmlns:models="using:BluetoothLEExplorer.Models">
|
||||
|
||||
<Page.DataContext>
|
||||
<vm:DiscoverViewModel x:Name="ViewModel" />
|
||||
</Page.DataContext>
|
||||
|
||||
<Page.Resources>
|
||||
<converters:ValueWhenConverter x:Key="VisibleWhenTrueConverter">
|
||||
<converters:ValueWhenConverter.When>
|
||||
<x:Boolean>True</x:Boolean>
|
||||
</converters:ValueWhenConverter.When>
|
||||
<converters:ValueWhenConverter.Value>
|
||||
<Visibility>Visible</Visibility>
|
||||
</converters:ValueWhenConverter.Value>
|
||||
<converters:ValueWhenConverter.Otherwise>
|
||||
<Visibility>Collapsed</Visibility>
|
||||
</converters:ValueWhenConverter.Otherwise>
|
||||
</converters:ValueWhenConverter>
|
||||
|
||||
<converters:ValueWhenConverter x:Key="VisibleWhenFalseConverter">
|
||||
<converters:ValueWhenConverter.When>
|
||||
<x:Boolean>False</x:Boolean>
|
||||
</converters:ValueWhenConverter.When>
|
||||
<converters:ValueWhenConverter.Value>
|
||||
<Visibility>Visible</Visibility>
|
||||
</converters:ValueWhenConverter.Value>
|
||||
<converters:ValueWhenConverter.Otherwise>
|
||||
<Visibility>Collapsed</Visibility>
|
||||
</converters:ValueWhenConverter.Otherwise>
|
||||
</converters:ValueWhenConverter>
|
||||
|
||||
<converters:ValueWhenConverter x:Key="EnumerationBoolToStringConverter">
|
||||
<converters:ValueWhenConverter.When>
|
||||
<x:Boolean>True</x:Boolean>
|
||||
</converters:ValueWhenConverter.When>
|
||||
<converters:ValueWhenConverter.Value>
|
||||
<x:String>Stop</x:String>
|
||||
</converters:ValueWhenConverter.Value>
|
||||
<converters:ValueWhenConverter.Otherwise>
|
||||
<x:String>Start</x:String>
|
||||
</converters:ValueWhenConverter.Otherwise>
|
||||
</converters:ValueWhenConverter>
|
||||
|
||||
<converters:ValueWhenConverter x:Key="CollapsedWhenZero">
|
||||
<converters:ValueWhenConverter.When>
|
||||
<x:Int32>0</x:Int32>
|
||||
</converters:ValueWhenConverter.When>
|
||||
<converters:ValueWhenConverter.Value>
|
||||
<Visibility>Collapsed</Visibility>
|
||||
</converters:ValueWhenConverter.Value>
|
||||
<converters:ValueWhenConverter.Otherwise>
|
||||
<Visibility>Visible</Visibility>
|
||||
</converters:ValueWhenConverter.Otherwise>
|
||||
</converters:ValueWhenConverter>
|
||||
|
||||
<converters:ValueWhenConverter x:Key="CollapsedWhenNegOne">
|
||||
<converters:ValueWhenConverter.When>
|
||||
<x:Int32>-1</x:Int32>
|
||||
</converters:ValueWhenConverter.When>
|
||||
<converters:ValueWhenConverter.Value>
|
||||
<Visibility>Collapsed</Visibility>
|
||||
</converters:ValueWhenConverter.Value>
|
||||
<converters:ValueWhenConverter.Otherwise>
|
||||
<Visibility>Visible</Visibility>
|
||||
</converters:ValueWhenConverter.Otherwise>
|
||||
</converters:ValueWhenConverter>
|
||||
|
||||
<CollectionViewSource x:Name="SortedDevices" IsSourceGrouped="False" Source="{x:Bind ViewModel.DeviceList, Mode=OneWay}"/>
|
||||
|
||||
</Page.Resources>
|
||||
|
||||
<RelativePanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="AdaptiveVisualStateGroup">
|
||||
<VisualState x:Name="VisualStateNarrow">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="{StaticResource NarrowMinWidth}" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<!-- TODO: change properties for narrow view -->
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="VisualStateNormal">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="{StaticResource NormalMinWidth}" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<!-- TODO: change properties for normal view -->
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="VisualStateWide">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="{StaticResource WideMinWidth}" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<!-- TODO: change properties for wide view -->
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
|
||||
|
||||
|
||||
<controls:PageHeader x:Name="pageHeader" RelativePanel.AlignLeftWithPanel="True"
|
||||
RelativePanel.AlignRightWithPanel="True"
|
||||
RelativePanel.AlignTopWithPanel="True" Text="Discover">
|
||||
|
||||
<!-- secondary commands -->
|
||||
<controls:PageHeader.SecondaryCommands>
|
||||
<AppBarButton Click="{x:Bind ViewModel.GotoSettings}" Label="Settings" />
|
||||
<AppBarButton Click="{x:Bind ViewModel.GotoPrivacy}" Label="Privacy" />
|
||||
<AppBarButton Click="{x:Bind ViewModel.GotoAbout}" Label="About" />
|
||||
</controls:PageHeader.SecondaryCommands>
|
||||
|
||||
</controls:PageHeader>
|
||||
|
||||
<RelativePanel RelativePanel.Below="pageHeader" Padding="10" Visibility="{x:Bind ViewModel.IsCentralRoleSupported, Converter={StaticResource VisibleWhenFalseConverter}}">
|
||||
<TextBlock Text="Center role is not supported on this device" />
|
||||
</RelativePanel>
|
||||
|
||||
<ProgressBar x:Name="progressBar"
|
||||
IsEnabled="{x:Bind ViewModel.IsEnumerating, Mode=OneWay }"
|
||||
IsIndeterminate="{x:Bind ViewModel.IsEnumerating, Mode=OneWay }"
|
||||
RelativePanel.Below="pageHeader"
|
||||
RelativePanel.AlignLeftWithPanel="True" RelativePanel.AlignRightWithPanel="True"
|
||||
Visibility="{x:Bind ViewModel.IsCentralRoleSupported, Converter={StaticResource VisibleWhenTrueConverter}}"/>
|
||||
|
||||
<Grid RelativePanel.Below="progressBar" Padding="10" Visibility="{x:Bind ViewModel.IsCentralRoleSupported, Converter={StaticResource VisibleWhenTrueConverter}}" >
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<StackPanel Grid.Row="0">
|
||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" >
|
||||
<Button Content="{x:Bind ViewModel.IsEnumerating, Mode=OneWay, Converter={StaticResource EnumerationBoolToStringConverter}}" Click="{x:Bind ViewModel.ToggleEnumeration}" />
|
||||
<TextBlock Text="Enumeration finished" Visibility="{x:Bind ViewModel.EnumerationFinished, Mode=OneWay, Converter={StaticResource VisibleWhenTrueConverter}}" Margin="10,0,0,0" VerticalAlignment="Center" />
|
||||
</StackPanel>
|
||||
<CheckBox Content="Continuous Enumeration" IsChecked="{x:Bind ViewModel.ContinuousEnumeration, Mode=TwoWay}" />
|
||||
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="0,5">
|
||||
<TextBlock Text="Filter: " VerticalAlignment="Center" Margin="0, 0, 5, 0"/>
|
||||
<TextBox Text="{Binding GridFilter, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock>
|
||||
<Run Text="Total Device Count:" />
|
||||
<Run Text="{x:Bind ViewModel.Context.BluetoothLEDevices.Count, Mode=OneWay}" />
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
|
||||
<GridView Grid.Row="1" x:Name="DevicesListView"
|
||||
ItemsSource="{x:Bind ViewModel.DeviceList, Mode=OneWay}"
|
||||
SelectedItem="{x:Bind ViewModel.SelectedDevice, Mode=TwoWay}" Margin="0,20,0,0">
|
||||
<GridView.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<ItemsWrapGrid Orientation="Horizontal">
|
||||
<ItemsWrapGrid.ChildrenTransitions>
|
||||
<TransitionCollection>
|
||||
<AddDeleteThemeTransition/>
|
||||
<RepositionThemeTransition/>
|
||||
<ReorderThemeTransition/>
|
||||
</TransitionCollection>
|
||||
</ItemsWrapGrid.ChildrenTransitions>
|
||||
</ItemsWrapGrid>
|
||||
</ItemsPanelTemplate>
|
||||
</GridView.ItemsPanel>
|
||||
|
||||
<GridView.ItemTemplate>
|
||||
<DataTemplate x:DataType="models:ObservableBluetoothLEDevice" >
|
||||
<Grid Height="200" Width="150" Padding="5" Holding="Grid_Holding" RightTapped="Grid_RightTapped" Background="{ThemeResource CustomColor}">
|
||||
<FlyoutBase.AttachedFlyout>
|
||||
<MenuFlyout>
|
||||
<MenuFlyoutItem Text="Pair" Click="{x:Bind DoInAppPairing}" />
|
||||
</MenuFlyout>
|
||||
</FlyoutBase.AttachedFlyout>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid Grid.Row="0" Grid.Column="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Border BorderBrush="Black" BorderThickness="2" >
|
||||
<Image Grid.Column="0" x:Name="Glyph" Source="{Binding Glyph}" Height="40" Width="40" Margin="5" />
|
||||
</Border>
|
||||
<StackPanel Grid.Column="1" >
|
||||
<TextBlock Margin="5,0,0,0">
|
||||
RSSI: <Run Text="{x:Bind RSSI, Mode=OneWay}" />
|
||||
</TextBlock>
|
||||
<TextBlock Margin="5,0,0,0" Visibility="{x:Bind BatteryLevel, Mode=OneWay, Converter={StaticResource CollapsedWhenNegOne}}">
|
||||
Battery: <Run Text="{x:Bind BatteryLevel, Mode=OneWay}" />
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<StackPanel Grid.Row="1" Grid.ColumnSpan="2">
|
||||
<TextBlock TextWrapping="Wrap">
|
||||
Name: <Run Text="{x:Bind Name, Mode=OneWay}" />
|
||||
</TextBlock>
|
||||
<TextBlock TextWrapping="WrapWholeWords" Foreground="Black">
|
||||
BT Address: <Run Text="{x:Bind BluetoothAddressAsString, Mode=OneTime}" />
|
||||
</TextBlock>
|
||||
<TextBlock Foreground="Black">
|
||||
Connected: <Run Text="{x:Bind IsConnected, Mode=OneWay}"/>
|
||||
</TextBlock>
|
||||
<TextBlock Foreground="Black">
|
||||
Paired: <Run Text="{x:Bind IsPaired, Mode=OneWay}"/>
|
||||
</TextBlock>
|
||||
<TextBlock Visibility="{x:Bind ServiceCount, Converter={StaticResource CollapsedWhenZero}}" Foreground="Black" >
|
||||
Service Count: <Run Text="{x:Bind ServiceCount, Mode=OneWay}"/>
|
||||
</TextBlock>
|
||||
<TextBlock>
|
||||
RSSI: <Run Text="{x:Bind RSSI, Mode=OneWay}"/>
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</GridView.ItemTemplate>
|
||||
</GridView>
|
||||
</Grid>
|
||||
</RelativePanel>
|
||||
</Page>
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
// <copyright file="Discover.xaml.cs" company="Microsoft Corporation">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//----------------------------------------------------------------------------------------------
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Controls.Primitives;
|
||||
|
||||
//// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
|
||||
|
||||
namespace BluetoothLEExplorer.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
public sealed partial class Discover : Page
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Discover" /> class.
|
||||
/// </summary>
|
||||
public Discover()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
|
||||
private void Grid_Holding(object sender, Windows.UI.Xaml.Input.HoldingRoutedEventArgs e)
|
||||
{
|
||||
FlyoutBase.ShowAttachedFlyout(sender as FrameworkElement);
|
||||
}
|
||||
|
||||
private void Grid_RightTapped(object sender, Windows.UI.Xaml.Input.RightTappedRoutedEventArgs e)
|
||||
{
|
||||
FlyoutBase.ShowAttachedFlyout(sender as FrameworkElement);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
<Page x:Class="BluetoothLEExplorer.Views.BatteryServicePage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:Behaviors="using:Template10.Behaviors"
|
||||
xmlns:Core="using:Microsoft.Xaml.Interactions.Core"
|
||||
xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
|
||||
xmlns:controls="using:Template10.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:BluetoothLEExplorer.Views"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:vm="using:BluetoothLEExplorer.ViewModels"
|
||||
xmlns:converters="using:Template10.Converters"
|
||||
xmlns:customControls="using:BluetoothLEExplorer.CustomControls"
|
||||
mc:Ignorable="d"
|
||||
xmlns:models="using:BluetoothLEExplorer.Models">
|
||||
|
||||
<Page.DataContext>
|
||||
<vm:BatteryServicePageViewModel x:Name="ViewModel" />
|
||||
</Page.DataContext>
|
||||
|
||||
<Page.Resources>
|
||||
<converters:ValueWhenConverter x:Key="VisibleWhenTrueConverter">
|
||||
<converters:ValueWhenConverter.When>
|
||||
<x:Boolean>True</x:Boolean>
|
||||
</converters:ValueWhenConverter.When>
|
||||
<converters:ValueWhenConverter.Value>
|
||||
<Visibility>Visible</Visibility>
|
||||
</converters:ValueWhenConverter.Value>
|
||||
<converters:ValueWhenConverter.Otherwise>
|
||||
<Visibility>Collapsed</Visibility>
|
||||
</converters:ValueWhenConverter.Otherwise>
|
||||
</converters:ValueWhenConverter>
|
||||
|
||||
<converters:ValueWhenConverter x:Key="notBool">
|
||||
<converters:ValueWhenConverter.When>
|
||||
<x:Boolean>True</x:Boolean>
|
||||
</converters:ValueWhenConverter.When>
|
||||
<converters:ValueWhenConverter.Value>
|
||||
<x:Boolean>False</x:Boolean>
|
||||
</converters:ValueWhenConverter.Value>
|
||||
<converters:ValueWhenConverter.Otherwise>
|
||||
<x:Boolean>True</x:Boolean>
|
||||
</converters:ValueWhenConverter.Otherwise>
|
||||
</converters:ValueWhenConverter>
|
||||
|
||||
|
||||
</Page.Resources>
|
||||
|
||||
<RelativePanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" >
|
||||
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="AdaptiveVisualStateGroup">
|
||||
<VisualState x:Name="VisualStateDisplayType">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="{StaticResource MinDisplayTypeWidth}" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<!-- TODO: change properties for narrow view -->
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="VisualStateNarrow">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="{StaticResource NarrowMinWidth}" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<!-- TODO: change properties for narrow view -->
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="VisualStateNormal">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="{StaticResource NormalMinWidth}" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<!-- TODO: change properties for normal view -->
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="VisualStateWide">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="{StaticResource WideMinWidth}" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<!-- TODO: change properties for wide view -->
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
|
||||
<controls:PageHeader x:Name="pageHeader" RelativePanel.AlignLeftWithPanel="True"
|
||||
RelativePanel.AlignRightWithPanel="True"
|
||||
RelativePanel.AlignTopWithPanel="True" Text="{x:Bind ViewModel.Service.Name}">
|
||||
|
||||
<!-- secondary commands -->
|
||||
<controls:PageHeader.SecondaryCommands>
|
||||
<AppBarButton Click="{x:Bind ViewModel.GotoSettings}" Label="Settings" />
|
||||
<AppBarButton Click="{x:Bind ViewModel.GotoPrivacy}" Label="Privacy" />
|
||||
<AppBarButton Click="{x:Bind ViewModel.GotoAbout}" Label="About" />
|
||||
</controls:PageHeader.SecondaryCommands>
|
||||
|
||||
</controls:PageHeader>
|
||||
|
||||
<StackPanel RelativePanel.Below="pageHeader" Padding="10">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<ToggleSwitch x:Name="Switch" RelativePanel.Below="pageHeader" Header="Discoverable and Connectable"
|
||||
IsOn="{x:Bind ViewModel.ServiceVM.IsPublishing, Mode=TwoWay}" Margin="5" />
|
||||
|
||||
<!--<ProgressRing Visibility="{x:Bind ViewModel.IsPublishingProgress, Mode=OneWay, Converter={StaticResource VisibleWhenTrueConverter}}"
|
||||
IsActive="{x:Bind ViewModel.IsPublishingProgress, Mode=OneWay}" Margin="-200,0,0,-20"/>-->
|
||||
|
||||
</StackPanel>
|
||||
<customControls:GattLocalCharacteristicControl Characteristic="{x:Bind ViewModel.BatteryLevel, Mode=OneWay}" Margin="5" />
|
||||
|
||||
</StackPanel>
|
||||
</RelativePanel>
|
||||
</Page>
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
// <copyright file="BatteryServicePage.xaml.cs" company="Microsoft Corporation">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//----------------------------------------------------------------------------------------------
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
//// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
|
||||
|
||||
namespace BluetoothLEExplorer.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
public sealed partial class BatteryServicePage : Page
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BatteryServicePage" /> class.
|
||||
/// </summary>
|
||||
public BatteryServicePage()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
<Page x:Class="BluetoothLEExplorer.Views.BloodPressureServicePage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:Behaviors="using:Template10.Behaviors"
|
||||
xmlns:Core="using:Microsoft.Xaml.Interactions.Core"
|
||||
xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
|
||||
xmlns:controls="using:Template10.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:BluetoothLEExplorer.Views"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:vm="using:BluetoothLEExplorer.ViewModels"
|
||||
xmlns:converters="using:Template10.Converters"
|
||||
xmlns:customControls="using:BluetoothLEExplorer.CustomControls"
|
||||
mc:Ignorable="d"
|
||||
xmlns:models="using:BluetoothLEExplorer.Models">
|
||||
|
||||
<Page.DataContext>
|
||||
<vm:BloodPressureServicePageViewModel x:Name="ViewModel" />
|
||||
</Page.DataContext>
|
||||
|
||||
<Page.Resources>
|
||||
<converters:ValueWhenConverter x:Key="VisibleWhenTrueConverter">
|
||||
<converters:ValueWhenConverter.When>
|
||||
<x:Boolean>True</x:Boolean>
|
||||
</converters:ValueWhenConverter.When>
|
||||
<converters:ValueWhenConverter.Value>
|
||||
<Visibility>Visible</Visibility>
|
||||
</converters:ValueWhenConverter.Value>
|
||||
<converters:ValueWhenConverter.Otherwise>
|
||||
<Visibility>Collapsed</Visibility>
|
||||
</converters:ValueWhenConverter.Otherwise>
|
||||
</converters:ValueWhenConverter>
|
||||
|
||||
<converters:ValueWhenConverter x:Key="notBool">
|
||||
<converters:ValueWhenConverter.When>
|
||||
<x:Boolean>True</x:Boolean>
|
||||
</converters:ValueWhenConverter.When>
|
||||
<converters:ValueWhenConverter.Value>
|
||||
<x:Boolean>False</x:Boolean>
|
||||
</converters:ValueWhenConverter.Value>
|
||||
<converters:ValueWhenConverter.Otherwise>
|
||||
<x:Boolean>True</x:Boolean>
|
||||
</converters:ValueWhenConverter.Otherwise>
|
||||
</converters:ValueWhenConverter>
|
||||
|
||||
|
||||
</Page.Resources>
|
||||
|
||||
<RelativePanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" >
|
||||
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="AdaptiveVisualStateGroup">
|
||||
<VisualState x:Name="VisualStateDisplayType">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="{StaticResource MinDisplayTypeWidth}" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<!-- TODO: change properties for narrow view -->
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="VisualStateNarrow">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="{StaticResource NarrowMinWidth}" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<!-- TODO: change properties for narrow view -->
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="VisualStateNormal">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="{StaticResource NormalMinWidth}" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<!-- TODO: change properties for normal view -->
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="VisualStateWide">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="{StaticResource WideMinWidth}" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<!-- TODO: change properties for wide view -->
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
|
||||
<controls:PageHeader x:Name="pageHeader" RelativePanel.AlignLeftWithPanel="True"
|
||||
RelativePanel.AlignRightWithPanel="True"
|
||||
RelativePanel.AlignTopWithPanel="True" Text="{x:Bind ViewModel.Service.Name}">
|
||||
|
||||
<!-- secondary commands -->
|
||||
<controls:PageHeader.SecondaryCommands>
|
||||
<AppBarButton Click="{x:Bind ViewModel.GotoSettings}" Label="Settings" />
|
||||
<AppBarButton Click="{x:Bind ViewModel.GotoPrivacy}" Label="Privacy" />
|
||||
<AppBarButton Click="{x:Bind ViewModel.GotoAbout}" Label="About" />
|
||||
</controls:PageHeader.SecondaryCommands>
|
||||
|
||||
</controls:PageHeader>
|
||||
|
||||
<StackPanel RelativePanel.Below="pageHeader" Padding="10">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<ToggleSwitch x:Name="Switch" RelativePanel.Below="pageHeader" Header="Discoverable and Connectable"
|
||||
IsOn="{x:Bind ViewModel.ServiceVM.IsPublishing, Mode=TwoWay}" Margin="5" />
|
||||
</StackPanel>
|
||||
<customControls:GattLocalCharacteristicControl Characteristic="{x:Bind ViewModel.BloodPressureMeasurement, Mode=OneWay}" Margin="5" />
|
||||
<customControls:GattLocalCharacteristicControl Characteristic="{x:Bind ViewModel.BloodPressureFeature, Mode=OneWay}" Margin="5" />
|
||||
|
||||
</StackPanel>
|
||||
</RelativePanel>
|
||||
</Page>
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
// <copyright file="BloodPressureServicePage.xaml.cs" company="Microsoft Corporation">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//----------------------------------------------------------------------------------------------
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
//// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
|
||||
|
||||
namespace BluetoothLEExplorer.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
public sealed partial class BloodPressureServicePage : Page
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BloodPressureServicePage" /> class.
|
||||
/// </summary>
|
||||
public BloodPressureServicePage()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
<Page x:Class="BluetoothLEExplorer.Views.HeartRateServicePage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:Behaviors="using:Template10.Behaviors"
|
||||
xmlns:Core="using:Microsoft.Xaml.Interactions.Core"
|
||||
xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
|
||||
xmlns:controls="using:Template10.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:BluetoothLEExplorer.Views"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:vm="using:BluetoothLEExplorer.ViewModels"
|
||||
xmlns:converters="using:Template10.Converters"
|
||||
xmlns:customControls="using:BluetoothLEExplorer.CustomControls"
|
||||
mc:Ignorable="d"
|
||||
xmlns:models="using:BluetoothLEExplorer.Models">
|
||||
|
||||
<Page.DataContext>
|
||||
<vm:HeartRateServicePageViewModel x:Name="ViewModel" />
|
||||
</Page.DataContext>
|
||||
|
||||
<Page.Resources>
|
||||
<converters:ValueWhenConverter x:Key="VisibleWhenTrueConverter">
|
||||
<converters:ValueWhenConverter.When>
|
||||
<x:Boolean>True</x:Boolean>
|
||||
</converters:ValueWhenConverter.When>
|
||||
<converters:ValueWhenConverter.Value>
|
||||
<Visibility>Visible</Visibility>
|
||||
</converters:ValueWhenConverter.Value>
|
||||
<converters:ValueWhenConverter.Otherwise>
|
||||
<Visibility>Collapsed</Visibility>
|
||||
</converters:ValueWhenConverter.Otherwise>
|
||||
</converters:ValueWhenConverter>
|
||||
|
||||
<converters:ValueWhenConverter x:Key="notBool">
|
||||
<converters:ValueWhenConverter.When>
|
||||
<x:Boolean>True</x:Boolean>
|
||||
</converters:ValueWhenConverter.When>
|
||||
<converters:ValueWhenConverter.Value>
|
||||
<x:Boolean>False</x:Boolean>
|
||||
</converters:ValueWhenConverter.Value>
|
||||
<converters:ValueWhenConverter.Otherwise>
|
||||
<x:Boolean>True</x:Boolean>
|
||||
</converters:ValueWhenConverter.Otherwise>
|
||||
</converters:ValueWhenConverter>
|
||||
|
||||
|
||||
</Page.Resources>
|
||||
|
||||
<RelativePanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" >
|
||||
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="AdaptiveVisualStateGroup">
|
||||
<VisualState x:Name="VisualStateDisplayType">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="{StaticResource MinDisplayTypeWidth}" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<!-- TODO: change properties for narrow view -->
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="VisualStateNarrow">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="{StaticResource NarrowMinWidth}" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<!-- TODO: change properties for narrow view -->
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="VisualStateNormal">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="{StaticResource NormalMinWidth}" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<!-- TODO: change properties for normal view -->
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="VisualStateWide">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="{StaticResource WideMinWidth}" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<!-- TODO: change properties for wide view -->
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
|
||||
<controls:PageHeader x:Name="pageHeader" RelativePanel.AlignLeftWithPanel="True"
|
||||
RelativePanel.AlignRightWithPanel="True"
|
||||
RelativePanel.AlignTopWithPanel="True" Text="{x:Bind ViewModel.Service.Name}">
|
||||
|
||||
<!-- secondary commands -->
|
||||
<controls:PageHeader.SecondaryCommands>
|
||||
<AppBarButton Click="{x:Bind ViewModel.GotoSettings}" Label="Settings" />
|
||||
<AppBarButton Click="{x:Bind ViewModel.GotoPrivacy}" Label="Privacy" />
|
||||
<AppBarButton Click="{x:Bind ViewModel.GotoAbout}" Label="About" />
|
||||
</controls:PageHeader.SecondaryCommands>
|
||||
|
||||
</controls:PageHeader>
|
||||
|
||||
<StackPanel RelativePanel.Below="pageHeader" Padding="10">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<ToggleSwitch x:Name="Switch" RelativePanel.Below="pageHeader" Header="Discoverable and Connectable"
|
||||
IsOn="{x:Bind ViewModel.ServiceVM.IsPublishing, Mode=TwoWay}" Margin="5" />
|
||||
</StackPanel>
|
||||
<customControls:GattLocalCharacteristicControl Characteristic="{x:Bind ViewModel.HeartRateMeasurement, Mode=OneWay}" Margin="5" />
|
||||
|
||||
</StackPanel>
|
||||
</RelativePanel>
|
||||
</Page>
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
// <copyright file="HeartRateServicePage.xaml.cs" company="Microsoft Corporation">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//----------------------------------------------------------------------------------------------
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
//// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
|
||||
|
||||
namespace BluetoothLEExplorer.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
public sealed partial class HeartRateServicePage : Page
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="HeartRateServicePage" /> class.
|
||||
/// </summary>
|
||||
public HeartRateServicePage()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,157 @@
|
|||
<Page x:Name="page" x:Class="BluetoothLEExplorer.Views.MicrosoftServicePage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:Behaviors="using:Template10.Behaviors"
|
||||
xmlns:Core="using:Microsoft.Xaml.Interactions.Core"
|
||||
xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
|
||||
xmlns:controls="using:Template10.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:BluetoothLEExplorer.Views"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:vm="using:BluetoothLEExplorer.ViewModels"
|
||||
xmlns:customControls="using:BluetoothLEExplorer.CustomControls"
|
||||
mc:Ignorable="d"
|
||||
xmlns:models="using:BluetoothLEExplorer.Models">
|
||||
|
||||
<Page.DataContext>
|
||||
<vm:MicrosoftServicePageViewModel x:Name="ViewModel" />
|
||||
</Page.DataContext>
|
||||
|
||||
<RelativePanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" >
|
||||
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="AdaptiveVisualStateGroup">
|
||||
<VisualState x:Name="VisualStateNarrow">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="{StaticResource NarrowMinWidth}" /> <!-- 0 -->
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<!-- TODO: change properties for narrow view -->
|
||||
<Setter Target="c5.(RelativePanel.Below)" Value="c4"/>
|
||||
<Setter Target="c4.(RelativePanel.Below)" Value="c3"/>
|
||||
<Setter Target="c4.(RelativePanel.AlignLeftWithPanel)" Value="True"/>
|
||||
<Setter Target="c3.(RelativePanel.Below)" Value="c2"/>
|
||||
<Setter Target="c2.(RelativePanel.Below)" Value="c1"/>
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="VisualStateTileNarrow">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="{StaticResource NarrowMinWidth}" />
|
||||
<!-- 0 -->
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<!-- TODO: change properties for narrow view -->
|
||||
<Setter Target="c5.(RelativePanel.Below)" Value="c4"/>
|
||||
<Setter Target="c4.(RelativePanel.Below)" Value="c3"/>
|
||||
<Setter Target="c4.(RelativePanel.AlignLeftWithPanel)" Value="True"/>
|
||||
<Setter Target="c2.(RelativePanel.Below)" Value="c1"/>
|
||||
<Setter Target="c3.(RelativePanel.Below)" Value="c2"/>
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="VisualStateNormal">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="885" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<!-- TODO: change properties for normal view -->
|
||||
<Setter Target="c5.(RelativePanel.Below)" Value="c3"/>
|
||||
<Setter Target="c4.(RelativePanel.Below)" Value="c1"/>
|
||||
<Setter Target="c3.(RelativePanel.Below)" Value="c1"/>
|
||||
<Setter Target="c4.(RelativePanel.RightOf)" Value="c3"/>
|
||||
<Setter Target="c2.(RelativePanel.RightOf)" Value="c1"/>
|
||||
<Setter Target="c2.(RelativePanel.AlignTopWith)" Value="c1"/>
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="VisualStateWide">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="{StaticResource WideMinWidth}" /> <!-- 1200 -->
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<!-- TODO: change properties for wide view -->
|
||||
<Setter Target="c5.(RelativePanel.Below)" Value="c3"/>
|
||||
<Setter Target="c4.(RelativePanel.Below)" Value="c1"/>
|
||||
<Setter Target="c3.(RelativePanel.Below)" Value="c1"/>
|
||||
<Setter Target="c4.(RelativePanel.RightOf)" Value="c3"/>
|
||||
<Setter Target="c2.(RelativePanel.RightOf)" Value="c1"/>
|
||||
<Setter Target="c2.(RelativePanel.AlignTopWith)" Value="c1"/>
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="VisualStatTileWide">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="1475" />
|
||||
<!-- 1200 -->
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<!-- TODO: change properties for wide view -->
|
||||
<Setter Target="c5.(RelativePanel.RightOf)" Value="c4"/>
|
||||
<Setter Target="c5.(RelativePanel.Below)" Value="c2"/>
|
||||
<Setter Target="c4.(RelativePanel.Below)" Value="c1"/>
|
||||
<Setter Target="c2.(RelativePanel.RightOf)" Value="c1"/>
|
||||
<Setter Target="c2.(RelativePanel.AlignTopWith)" Value="c1"/>
|
||||
<Setter Target="c3.(RelativePanel.RightOf)" Value="c2"/>
|
||||
<Setter Target="c3.(RelativePanel.AlignTopWith)" Value="c1"/>
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
|
||||
<VisualState x:Name="VisualStateTileReallyWide">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="1900" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<!-- TODO: change properties for wide view -->
|
||||
<Setter Target="c2.(RelativePanel.LeftOf)" Value=""/>
|
||||
<Setter Target="c2.(RelativePanel.RightOf)" Value="c1"/>
|
||||
<Setter Target="c2.(RelativePanel.AlignTopWith)" Value="c1"/>
|
||||
<Setter Target="c3.(RelativePanel.RightOf)" Value="c2"/>
|
||||
<Setter Target="c3.(RelativePanel.AlignTopWith)" Value="c1"/>
|
||||
<Setter Target="c4.(RelativePanel.RightOf)" Value="c3"/>
|
||||
<Setter Target="c4.(RelativePanel.AlignTopWith)" Value="c1"/>
|
||||
<Setter Target="c4.(RelativePanel.Below)" Value=""/>
|
||||
<Setter Target="c5.(RelativePanel.RightOf)" Value="c4"/>
|
||||
<Setter Target="c5.(RelativePanel.AlignTopWith)" Value="c1"/>
|
||||
<Setter Target="c5.(RelativePanel.Below)" Value=""/>
|
||||
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
|
||||
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
|
||||
<controls:PageHeader x:Name="pageHeader" RelativePanel.AlignLeftWithPanel="True"
|
||||
RelativePanel.AlignRightWithPanel="True"
|
||||
RelativePanel.AlignTopWithPanel="True" Text="Microsoft Service">
|
||||
|
||||
<!-- secondary commands -->
|
||||
<controls:PageHeader.SecondaryCommands>
|
||||
<AppBarButton Click="{x:Bind ViewModel.GotoSettings}" Label="Settings" />
|
||||
<AppBarButton Click="{x:Bind ViewModel.GotoPrivacy}" Label="Privacy" />
|
||||
<AppBarButton Click="{x:Bind ViewModel.GotoAbout}" Label="About" />
|
||||
</controls:PageHeader.SecondaryCommands>
|
||||
|
||||
</controls:PageHeader>
|
||||
|
||||
<StackPanel x:Name="SwitchHeader" RelativePanel.Below="pageHeader" Padding="10">
|
||||
<TextBlock Text="{x:Bind ViewModel.Service.Name}" />
|
||||
<ToggleSwitch x:Name="Switch" RelativePanel.Below="pageHeader" Header="Discoverable and Connectable" IsOn="{x:Bind ViewModel.ServiceVM.IsPublishing, Mode=TwoWay}" Margin="5"/>
|
||||
</StackPanel>
|
||||
|
||||
<ScrollViewer
|
||||
RelativePanel.Below="SwitchHeader"
|
||||
RelativePanel.AlignLeftWithPanel="True"
|
||||
RelativePanel.AlignRightWithPanel="True"
|
||||
RelativePanel.AlignBottomWithPanel="True">
|
||||
<RelativePanel>
|
||||
<customControls:GattLocalCharacteristicControl x:Name="c1" Characteristic="{x:Bind ViewModel.ReadCharacteristic, Mode=OneWay}" Margin="5"
|
||||
RelativePanel.AlignTopWithPanel="True" RelativePanel.AlignLeftWithPanel="True"/>
|
||||
<customControls:GattLocalCharacteristicControl x:Name="c2" Characteristic="{x:Bind ViewModel.WriteCharacteristic, Mode=OneWay}" Margin="5"/>
|
||||
<customControls:GattLocalCharacteristicControl x:Name="c3" Characteristic="{x:Bind ViewModel.NotifyCharacteristic, Mode=OneWay}" Margin="5" IsReadOnly="False"/>
|
||||
<customControls:GattLocalCharacteristicControl x:Name="c4" Characteristic="{x:Bind ViewModel.IndicateCharacteristic, Mode=OneWay}" Margin="5" IsReadOnly="False"/>
|
||||
<customControls:GattLocalCharacteristicControl x:Name="c5" Characteristic="{x:Bind ViewModel.ReadLongCharacteristic, Mode=OneWay}" Margin="5"/>
|
||||
</RelativePanel>
|
||||
</ScrollViewer>
|
||||
</RelativePanel>
|
||||
</Page>
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
// <copyright file="MicrosoftServicePage.xaml.cs" company="Microsoft Corporation">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//----------------------------------------------------------------------------------------------
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace BluetoothLEExplorer.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
public sealed partial class MicrosoftServicePage : Page
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MicrosoftServicePage" /> class.
|
||||
/// </summary>
|
||||
public MicrosoftServicePage()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
<Page x:Class="BluetoothLEExplorer.Views.SettingsPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:Behaviors="using:Template10.Behaviors"
|
||||
xmlns:Core="using:Microsoft.Xaml.Interactions.Core"
|
||||
xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
|
||||
xmlns:controls="using:Template10.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:BluetoothLEExplorer.Views"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:vm="using:BluetoothLEExplorer.ViewModels" mc:Ignorable="d">
|
||||
|
||||
<Page.DataContext>
|
||||
<vm:SettingsPageViewModel x:Name="ViewModel" />
|
||||
</Page.DataContext>
|
||||
|
||||
|
||||
<RelativePanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="AdaptiveVisualStateGroup">
|
||||
<VisualState x:Name="VisualStateNarrow">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="{StaticResource NarrowMinWidth}" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<!-- TODO: change properties for narrow view -->
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="VisualStateNormal">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="{StaticResource NormalMinWidth}" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<!-- TODO: change properties for normal view -->
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="VisualStateWide">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="{StaticResource WideMinWidth}" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<!-- TODO: change properties for wide view -->
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
|
||||
<!-- header -->
|
||||
<controls:PageHeader x:Name="pageHeader" Content="Settings Page"
|
||||
Frame="{x:Bind Frame}"
|
||||
RelativePanel.AlignLeftWithPanel="True"
|
||||
RelativePanel.AlignRightWithPanel="True"
|
||||
RelativePanel.AlignTopWithPanel="True" />
|
||||
|
||||
<!-- content -->
|
||||
<Pivot x:Name="MyPivot" Grid.Row="1"
|
||||
RelativePanel.AlignBottomWithPanel="True"
|
||||
RelativePanel.AlignLeftWithPanel="True"
|
||||
RelativePanel.AlignRightWithPanel="True"
|
||||
RelativePanel.Below="pageHeader">
|
||||
|
||||
<!-- settings -->
|
||||
<PivotItem DataContext="{Binding SettingsPartViewModel}" Header="Settings">
|
||||
<ScrollViewer Margin="0,12,-12,0" HorizontalContentAlignment="Left"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<RelativePanel HorizontalAlignment="Left">
|
||||
|
||||
<ToggleSwitch x:Name="UseShellDrawnBackButtonToggleSwtich"
|
||||
Header="Use shell-drawn back button"
|
||||
IsOn="{Binding UseShellBackButton, Mode=TwoWay}"
|
||||
OffContent="Back button in page header"
|
||||
OnContent="Back button in titlebar or taskbar"
|
||||
RelativePanel.AlignLeftWithPanel="True"
|
||||
RelativePanel.AlignTopWithPanel="True" />
|
||||
|
||||
<ToggleSwitch x:Name="UseLightThemeToggleSwitch" Header="Use Light Theme"
|
||||
IsOn="{Binding UseLightThemeButton, Mode=TwoWay}"
|
||||
OffContent="Dark theme" OnContent="Light theme"
|
||||
RelativePanel.AlignLeftWithPanel="True"
|
||||
RelativePanel.Below="UseShellDrawnBackButtonToggleSwtich" />
|
||||
|
||||
</RelativePanel>
|
||||
|
||||
</ScrollViewer>
|
||||
</PivotItem>
|
||||
|
||||
<!-- privacy -->
|
||||
<PivotItem DataContext="{Binding SettingsPartViewModel}"
|
||||
Header="Privacy statement" Padding="0" VerticalAlignment="Top">
|
||||
<HyperlinkButton NavigateUri="http://go.microsoft.com/fwlink/?LinkID=519393">Privacy Statement</HyperlinkButton>
|
||||
</PivotItem>
|
||||
|
||||
<!-- about -->
|
||||
<PivotItem Header="About">
|
||||
<RelativePanel DataContext="{Binding AboutPartViewModel}">
|
||||
|
||||
<Border x:Name="LogoImage" HorizontalAlignment="Right"
|
||||
VerticalAlignment="Top" BorderBrush="DimGray"
|
||||
BorderThickness="1" IsHitTestVisible="False"
|
||||
RelativePanel.AlignRightWithPanel="True"
|
||||
RelativePanel.AlignTopWithPanel="True">
|
||||
<Border.Background>
|
||||
<SolidColorBrush Color="{ThemeResource SystemAccentColor}" />
|
||||
</Border.Background>
|
||||
<Image Margin="12" Source="{Binding Logo}"
|
||||
Stretch="None" />
|
||||
</Border>
|
||||
|
||||
<TextBlock x:Name="DisplayNameTextBlock" Margin="0,0,0,12"
|
||||
FontSize="24" RelativePanel.AlignLeftWithPanel="True"
|
||||
RelativePanel.AlignRightWith="LogoImage"
|
||||
RelativePanel.AlignTopWithPanel="True"
|
||||
Text="{Binding DisplayName}" />
|
||||
|
||||
<TextBlock x:Name="PublisherTextBlock"
|
||||
RelativePanel.AlignLeftWithPanel="True"
|
||||
RelativePanel.Below="DisplayNameTextBlock"
|
||||
Text="{Binding Publisher}" />
|
||||
|
||||
<TextBlock x:Name="VersionTextBlock"
|
||||
RelativePanel.AlignLeftWithPanel="True"
|
||||
RelativePanel.Below="PublisherTextBlock">
|
||||
<Run>Version</Run>
|
||||
<Run Text="{Binding Version}" />
|
||||
</TextBlock>
|
||||
|
||||
</RelativePanel>
|
||||
</PivotItem>
|
||||
</Pivot>
|
||||
|
||||
</RelativePanel>
|
||||
|
||||
</Page>
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
// <copyright file="SettingsPage.xaml.cs" company="Microsoft Corporation">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//----------------------------------------------------------------------------------------------
|
||||
using Windows.UI.Xaml.Controls;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
|
||||
namespace BluetoothLEExplorer.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// Settings page
|
||||
/// </summary>
|
||||
public sealed partial class SettingsPage : Page
|
||||
{
|
||||
/// <summary>
|
||||
/// Serialization service
|
||||
/// </summary>
|
||||
private Template10.Services.SerializationService.ISerializationService serializationService;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SettingsPage" /> class.
|
||||
/// </summary>
|
||||
public SettingsPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
serializationService = Template10.Services.SerializationService.SerializationService.Json;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes when navigating to settings page
|
||||
/// </summary>
|
||||
/// <param name="e"></param>
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
var index = int.Parse(serializationService.Deserialize(e.Parameter?.ToString()).ToString());
|
||||
MyPivot.SelectedIndex = index;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
<Page x:Class="BluetoothLEExplorer.Views.Shell"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:Controls="using:Template10.Controls"
|
||||
xmlns:Core="using:Microsoft.Xaml.Interactions.Core"
|
||||
xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:BluetoothLEExplorer.Views"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:views="using:BluetoothLEExplorer.Views"
|
||||
xmlns:vm="using:BluetoothLEExplorer.ViewModels"
|
||||
xmlns:myconverters="using:BluetoothLEExplorer.Services.Converters"
|
||||
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Page.DataContext>
|
||||
<vm:ShellViewModel x:Name="ViewModel" />
|
||||
</Page.DataContext>
|
||||
|
||||
<Page.Resources>
|
||||
<myconverters:ShellFontConverter x:Key="StrikethroughConverter" />
|
||||
</Page.Resources>
|
||||
|
||||
<Controls:HamburgerMenu x:Name="MyHamburgerMenu"
|
||||
VisualStateNarrowMinWidth="{StaticResource NarrowMinWidth}"
|
||||
VisualStateNormalMinWidth="{StaticResource NormalMinWidth}"
|
||||
>
|
||||
|
||||
|
||||
|
||||
<Controls:HamburgerMenu.PrimaryButtons>
|
||||
<!-- mainpage button -->
|
||||
<Controls:HamburgerButtonInfo ClearHistory="True" PageType="views:Discover" >
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<SymbolIcon Width="48" Height="48"
|
||||
Symbol="Home" />
|
||||
<TextBlock Margin="12,0,0,0" VerticalAlignment="Center"
|
||||
Text="Discover and Pair" TextDecorations="{x:Bind ViewModel.IsCentralRoleSupported, Converter={StaticResource StrikethroughConverter}}" />
|
||||
</StackPanel>
|
||||
</Controls:HamburgerButtonInfo>
|
||||
<Controls:HamburgerButtonInfo ClearHistory="True" PageType="views:VirtualPeripheralPage" >
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<SymbolIcon Width="48" Height="48"
|
||||
Symbol="Home" />
|
||||
<TextBlock Margin="12,0,0,0" VerticalAlignment="Center"
|
||||
Text="Virtual Peripheral" TextDecorations="{x:Bind ViewModel.IsPeripheralRoleSupported, Converter={StaticResource StrikethroughConverter}}" />
|
||||
</StackPanel>
|
||||
</Controls:HamburgerButtonInfo>
|
||||
<Controls:HamburgerButtonInfo ClearHistory="True" PageType="views:Beacon" Visibility="Collapsed">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<SymbolIcon Width="48" Height="48"
|
||||
Symbol="Home" />
|
||||
<TextBlock Margin="12,0,0,0" VerticalAlignment="Center"
|
||||
Text="Beacon"/>
|
||||
</StackPanel>
|
||||
</Controls:HamburgerButtonInfo>
|
||||
</Controls:HamburgerMenu.PrimaryButtons>
|
||||
|
||||
<Controls:HamburgerMenu.SecondaryButtons>
|
||||
<!-- settingspage button -->
|
||||
<Controls:HamburgerButtonInfo x:Name="SettingsButton" PageParameter="0"
|
||||
PageType="views:SettingsPage">
|
||||
<Controls:HamburgerButtonInfo.NavigationTransitionInfo>
|
||||
<SuppressNavigationTransitionInfo />
|
||||
</Controls:HamburgerButtonInfo.NavigationTransitionInfo>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<SymbolIcon Width="48" Height="48"
|
||||
Symbol="Setting" />
|
||||
<TextBlock Margin="12,0,0,0" VerticalAlignment="Center"
|
||||
Text="Settings" />
|
||||
</StackPanel>
|
||||
</Controls:HamburgerButtonInfo>
|
||||
</Controls:HamburgerMenu.SecondaryButtons>
|
||||
|
||||
</Controls:HamburgerMenu>
|
||||
|
||||
</Page>
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
// <copyright file="Shell.xaml.cs" company="Microsoft Corporation">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//----------------------------------------------------------------------------------------------
|
||||
using Template10.Controls;
|
||||
using Template10.Services.NavigationService;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace BluetoothLEExplorer.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// Shell page
|
||||
/// </summary>
|
||||
public sealed partial class Shell : Page
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the shell instance
|
||||
/// </summary>
|
||||
public static Shell Instance { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Hamburger menu instance
|
||||
/// </summary>
|
||||
public static HamburgerMenu HamburgerMenu => Instance.MyHamburgerMenu;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Shell" /> class.
|
||||
/// </summary>
|
||||
public Shell()
|
||||
{
|
||||
Instance = this;
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Shell" /> class.
|
||||
/// </summary>
|
||||
/// <param name="navigationService"></param>
|
||||
public Shell(INavigationService navigationService) : this()
|
||||
{
|
||||
SetNavigationService(navigationService);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the navigation service
|
||||
/// </summary>
|
||||
/// <param name="navigationService"></param>
|
||||
public void SetNavigationService(INavigationService navigationService)
|
||||
{
|
||||
MyHamburgerMenu.NavigationService = navigationService;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
<UserControl x:Class="BluetoothLEExplorer.Views.Splash"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:Core="using:Microsoft.Xaml.Interactions.Core"
|
||||
xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
|
||||
xmlns:Media="using:Microsoft.Xaml.Interactions.Media"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:BluetoothLEExplorer.Views"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid Background="{StaticResource ExtendedSplashBackground}">
|
||||
|
||||
<Canvas x:Name="rootCanvas" HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<Canvas.Background>
|
||||
<ImageBrush ImageSource="ms-appx:///Assets/SplashScreen.png" Stretch="Uniform">
|
||||
<ImageBrush.Transform>
|
||||
<TranslateTransform Y="10" />
|
||||
</ImageBrush.Transform>
|
||||
</ImageBrush>
|
||||
</Canvas.Background>
|
||||
<Viewbox x:Name="splashImage"
|
||||
Width="{Binding ActualWidth, ElementName=MyCanvas, Mode=OneWay}"
|
||||
Height="{Binding ActualHeight, ElementName=MyCanvas, Mode=OneWay}">
|
||||
<Image Source="ms-appx:///Assets/SplashScreen.png" />
|
||||
</Viewbox>
|
||||
</Canvas>
|
||||
|
||||
<ProgressRing x:Name="progressRing" Width="50"
|
||||
Height="50" HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{StaticResource ExtendedSplashForeground}"
|
||||
IsActive="True" RenderTransformOrigin="0.5,0.5">
|
||||
<ProgressRing.RenderTransform>
|
||||
<CompositeTransform x:Name="ProgressTransform" TranslateY="120" />
|
||||
</ProgressRing.RenderTransform>
|
||||
</ProgressRing>
|
||||
|
||||
</Grid>
|
||||
|
||||
</UserControl>
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
// <copyright file="Splash.xaml.cs" company="Microsoft Corporation">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//----------------------------------------------------------------------------------------------
|
||||
using Windows.ApplicationModel.Activation;
|
||||
using Windows.UI.Xaml;
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
namespace BluetoothLEExplorer.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// Splash screen
|
||||
/// </summary>
|
||||
public sealed partial class Splash : UserControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Splash" /> class.
|
||||
/// </summary>
|
||||
/// <param name="splashScreen"></param>
|
||||
public Splash(SplashScreen splashScreen)
|
||||
{
|
||||
InitializeComponent();
|
||||
Window.Current.SizeChanged += (s, e) => Resize(splashScreen);
|
||||
Resize(splashScreen);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resizes the splash screen
|
||||
/// </summary>
|
||||
/// <param name="splashScreen"></param>
|
||||
private void Resize(SplashScreen splashScreen)
|
||||
{
|
||||
if (splashScreen.ImageLocation.Top == 0)
|
||||
{
|
||||
splashImage.Visibility = Visibility.Collapsed;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
rootCanvas.Background = null;
|
||||
splashImage.Visibility = Visibility.Visible;
|
||||
}
|
||||
|
||||
splashImage.Height = splashScreen.ImageLocation.Height;
|
||||
splashImage.Width = splashScreen.ImageLocation.Width;
|
||||
splashImage.SetValue(Canvas.TopProperty, splashScreen.ImageLocation.Top);
|
||||
splashImage.SetValue(Canvas.LeftProperty, splashScreen.ImageLocation.Left);
|
||||
ProgressTransform.TranslateY = splashImage.Height / 2;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
<Page x:Class="BluetoothLEExplorer.Views.VirtualPeripheralPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:Behaviors="using:Template10.Behaviors"
|
||||
xmlns:Core="using:Microsoft.Xaml.Interactions.Core"
|
||||
xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
|
||||
xmlns:controls="using:Template10.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:BluetoothLEExplorer.Views"
|
||||
xmlns:converters="using:Template10.Converters"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ggs="using:GattServicesLibrary"
|
||||
xmlns:vm="using:BluetoothLEExplorer.ViewModels" mc:Ignorable="d">
|
||||
|
||||
<Page.DataContext>
|
||||
<vm:VirtualPeripheralPageViewModel x:Name="ViewModel" />
|
||||
</Page.DataContext>
|
||||
<Page.Resources>
|
||||
<converters:ValueWhenConverter x:Key="VisibleWhenTrueConverter">
|
||||
<converters:ValueWhenConverter.When>
|
||||
<x:Boolean>True</x:Boolean>
|
||||
</converters:ValueWhenConverter.When>
|
||||
<converters:ValueWhenConverter.Value>
|
||||
<Visibility>Visible</Visibility>
|
||||
</converters:ValueWhenConverter.Value>
|
||||
<converters:ValueWhenConverter.Otherwise>
|
||||
<Visibility>Collapsed</Visibility>
|
||||
</converters:ValueWhenConverter.Otherwise>
|
||||
</converters:ValueWhenConverter>
|
||||
|
||||
<converters:ValueWhenConverter x:Key="VisibleWhenFalseConverter">
|
||||
<converters:ValueWhenConverter.When>
|
||||
<x:Boolean>False</x:Boolean>
|
||||
</converters:ValueWhenConverter.When>
|
||||
<converters:ValueWhenConverter.Value>
|
||||
<Visibility>Visible</Visibility>
|
||||
</converters:ValueWhenConverter.Value>
|
||||
<converters:ValueWhenConverter.Otherwise>
|
||||
<Visibility>Collapsed</Visibility>
|
||||
</converters:ValueWhenConverter.Otherwise>
|
||||
</converters:ValueWhenConverter>
|
||||
</Page.Resources>
|
||||
|
||||
<RelativePanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="AdaptiveVisualStateGroup">
|
||||
<VisualState x:Name="VisualStateNarrow">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="{StaticResource NarrowMinWidth}" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<!-- TODO: change properties for narrow view -->
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="VisualStateNormal">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="{StaticResource NormalMinWidth}" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<!-- TODO: change properties for normal view -->
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="VisualStateWide">
|
||||
<VisualState.StateTriggers>
|
||||
<AdaptiveTrigger MinWindowWidth="{StaticResource WideMinWidth}" />
|
||||
</VisualState.StateTriggers>
|
||||
<VisualState.Setters>
|
||||
<!-- TODO: change properties for wide view -->
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
|
||||
<controls:PageHeader x:Name="pageHeader" RelativePanel.AlignLeftWithPanel="True"
|
||||
RelativePanel.AlignRightWithPanel="True"
|
||||
RelativePanel.AlignTopWithPanel="True" Text="Virtual Peripheral">
|
||||
|
||||
<!-- secondary commands -->
|
||||
<controls:PageHeader.SecondaryCommands>
|
||||
<AppBarButton Click="{x:Bind ViewModel.GotoSettings}" Label="Settings" />
|
||||
<AppBarButton Click="{x:Bind ViewModel.GotoPrivacy}" Label="Privacy" />
|
||||
<AppBarButton Click="{x:Bind ViewModel.GotoAbout}" Label="About" />
|
||||
</controls:PageHeader.SecondaryCommands>
|
||||
|
||||
</controls:PageHeader>
|
||||
|
||||
<RelativePanel RelativePanel.Below="pageHeader" Padding="10" Visibility="{x:Bind ViewModel.IsPeripheralRoleSupported, Converter={StaticResource VisibleWhenFalseConverter}}">
|
||||
<TextBlock Text="Peripheral mode is not supported on this device" />
|
||||
</RelativePanel>
|
||||
|
||||
<RelativePanel RelativePanel.Below="pageHeader" Padding="10" Visibility="{x:Bind ViewModel.IsPeripheralRoleSupported, Converter={StaticResource VisibleWhenTrueConverter}}">
|
||||
<StackPanel x:Name="NewService" Orientation="Horizontal" >
|
||||
<ComboBox ItemsSource="{x:Bind ViewModel.AvailableServices}" SelectedValue="{x:Bind ViewModel.NewSelectedService, Mode=TwoWay}" Margin="0,0,5,0"/>
|
||||
<Button Content="Add Service" Click="{x:Bind ViewModel.CreateService}" Margin="5,0,0,0"/>
|
||||
</StackPanel>
|
||||
<GridView RelativePanel.Below="NewService"
|
||||
ItemsSource="{x:Bind ViewModel.Context.CreatedServices, Mode=OneWay}"
|
||||
Padding="10" SelectedItem="{x:Bind ViewModel.Context.SelectedGattServerService, Mode=TwoWay}"
|
||||
SelectionChanged="{x:Bind ViewModel.NavigateToService, Mode=OneWay}" >
|
||||
<GridView.ItemTemplate>
|
||||
<DataTemplate x:DataType="vm:GenericGattServiceViewModel">
|
||||
<RelativePanel Height="200" Width="150" Padding="5" BorderBrush="Black" BorderThickness="2"
|
||||
VerticalAlignment="Top" HorizontalAlignment="Left">
|
||||
<TextBlock x:Name="Name" Text="{x:Bind Service.Name}" RelativePanel.AlignLeftWithPanel="True" RelativePanel.AlignTopWithPanel="True"/>
|
||||
<ToggleSwitch RelativePanel.Below="Name" x:Name="Switch" Header="Publishing" IsOn="{x:Bind IsPublishing, Mode=TwoWay}" />
|
||||
<Button x:Name="Close" Content="X" Click="{x:Bind RemoveThisFromContext}" RelativePanel.AlignTopWithPanel="True" RelativePanel.AlignRightWithPanel="True" />
|
||||
</RelativePanel>
|
||||
</DataTemplate>
|
||||
</GridView.ItemTemplate>
|
||||
</GridView>
|
||||
</RelativePanel>
|
||||
|
||||
</RelativePanel>
|
||||
</Page>
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
// <copyright file="VirtualPeripheralPage.xaml.cs" company="Microsoft Corporation">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//----------------------------------------------------------------------------------------------
|
||||
using Windows.UI.Xaml.Controls;
|
||||
|
||||
//// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
|
||||
|
||||
namespace BluetoothLEExplorer.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
public sealed partial class VirtualPeripheralPage : Page
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="VirtualPeripheralPage" /> class.
|
||||
/// </summary>
|
||||
public VirtualPeripheralPage()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
Двоичные данные
BluetoothLEExplorer/BluetoothLEExplorerUnitTests/Assets/LockScreenLogo.scale-200.png
Normal file
После Ширина: | Высота: | Размер: 1.4 KiB |
Двоичные данные
BluetoothLEExplorer/BluetoothLEExplorerUnitTests/Assets/SplashScreen.scale-200.png
Normal file
После Ширина: | Высота: | Размер: 7.5 KiB |
Двоичные данные
BluetoothLEExplorer/BluetoothLEExplorerUnitTests/Assets/Square150x150Logo.scale-200.png
Normal file
После Ширина: | Высота: | Размер: 2.9 KiB |
Двоичные данные
BluetoothLEExplorer/BluetoothLEExplorerUnitTests/Assets/Square44x44Logo.scale-200.png
Normal file
После Ширина: | Высота: | Размер: 1.6 KiB |
Двоичные данные
BluetoothLEExplorer/BluetoothLEExplorerUnitTests/Assets/Square44x44Logo.targetsize-24_altform-unplated.png
Normal file
После Ширина: | Высота: | Размер: 1.2 KiB |
После Ширина: | Высота: | Размер: 1.4 KiB |
Двоичные данные
BluetoothLEExplorer/BluetoothLEExplorerUnitTests/Assets/Wide310x150Logo.scale-200.png
Normal file
После Ширина: | Высота: | Размер: 3.1 KiB |
|
@ -0,0 +1,149 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
|
||||
<ProjectGuid>{6F503DF9-71C9-4340-90BB-D9AA14ADB686}</ProjectGuid>
|
||||
<OutputType>AppContainerExe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>BluetoothLEExplorerUnitTests</RootNamespace>
|
||||
<AssemblyName>BluetoothLEExplorerUnitTests</AssemblyName>
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
|
||||
<TargetPlatformVersion>10.0.15063.0</TargetPlatformVersion>
|
||||
<TargetPlatformMinVersion>10.0.15063.0</TargetPlatformMinVersion>
|
||||
<MinimumVisualStudioVersion>14</MinimumVisualStudioVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<UnitTestPlatformVersion Condition="'$(UnitTestPlatformVersion)' == ''">$(VisualStudioVersion)</UnitTestPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>UnitTest\bin\x86\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||
<OutputPath>UnitTest\bin\x86\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>UnitTest\bin\ARM\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>ARM</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|ARM'">
|
||||
<OutputPath>UnitTest\bin\ARM\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>ARM</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>UnitTest\bin\x64\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
<OutputPath>UnitTest\bin\x64\Release\</OutputPath>
|
||||
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>true</Prefer32Bit>
|
||||
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<SDKReference Include="TestPlatform.Universal, Version=$(UnitTestPlatformVersion)" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="UnitTestApp.xaml.cs">
|
||||
<DependentUpon>UnitTestApp.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="UnitTest.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ApplicationDefinition Include="UnitTestApp.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</ApplicationDefinition>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AppxManifest Include="Package.appxmanifest">
|
||||
<SubType>Designer</SubType>
|
||||
</AppxManifest>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Assets\SplashScreen.scale-200.png" />
|
||||
<Content Include="Properties\UnitTestApp.rd.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
|
||||
<Version>5.3.3</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="MSTest.TestAdapter">
|
||||
<Version>1.1.17</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="MSTest.TestFramework">
|
||||
<Version>1.1.17</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\GattHelper\GattHelper.csproj">
|
||||
<Project>{37c63fdd-d995-4cc2-b014-4fb323194001}</Project>
|
||||
<Name>GattHelper</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' < '14.0' ">
|
||||
<VisualStudioVersion>14.0</VisualStudioVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" IgnorableNamespaces="uap mp">
|
||||
<Identity Name="9f6d1caa-64c3-4d5e-9b27-4d2a2a02d502" Publisher="CN=stfro" Version="1.0.0.0" />
|
||||
<mp:PhoneIdentity PhoneProductId="9f6d1caa-64c3-4d5e-9b27-4d2a2a02d502" PhonePublisherId="00000000-0000-0000-0000-000000000000" />
|
||||
<Properties>
|
||||
<DisplayName>BluetoothLEExplorerUnitTests</DisplayName>
|
||||
<PublisherDisplayName>stfro</PublisherDisplayName>
|
||||
<Logo>Assets\StoreLogo.png</Logo>
|
||||
</Properties>
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.0.0" MaxVersionTested="10.0.0.0" />
|
||||
</Dependencies>
|
||||
<Resources>
|
||||
<Resource Language="x-generate" />
|
||||
</Resources>
|
||||
<Applications>
|
||||
<Application Id="vstest.executionengine.universal.App" Executable="$targetnametoken$.exe" EntryPoint="BluetoothLEExplorerUnitTests.App">
|
||||
<uap:VisualElements DisplayName="BluetoothLEExplorerUnitTests" Square150x150Logo="Assets\Square150x150Logo.png" Square44x44Logo="Assets\Square44x44Logo.png" Description="BluetoothLEExplorerUnitTests" BackgroundColor="transparent">
|
||||
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png">
|
||||
</uap:DefaultTile>
|
||||
<uap:SplashScreen Image="Assets\SplashScreen.png" />
|
||||
</uap:VisualElements>
|
||||
</Application>
|
||||
</Applications>
|
||||
<Capabilities>
|
||||
<Capability Name="internetClientServer" />
|
||||
</Capabilities>
|
||||
</Package>
|
|
@ -0,0 +1,18 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
[assembly: AssemblyTitle("BluetoothLEExplorerUnitTests")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("BluetoothLEExplorerUnitTests")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2017")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: AssemblyMetadata("TargetPlatform","UAP")]
|
||||
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
[assembly: ComVisible(false)]
|