diff --git a/BluetoothLEExplorer/BluetoothLEExplorer.sln b/BluetoothLEExplorer/BluetoothLEExplorer.sln
index c99e463..83fc278 100644
--- a/BluetoothLEExplorer/BluetoothLEExplorer.sln
+++ b/BluetoothLEExplorer/BluetoothLEExplorer.sln
@@ -99,6 +99,7 @@ Global
{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|Any CPU.Build.0 = Debug|x86
+ {6F503DF9-71C9-4340-90BB-D9AA14ADB686}.Debug|Any CPU.Deploy.0 = 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
diff --git a/BluetoothLEExplorer/BluetoothLEExplorer/App.xaml.cs b/BluetoothLEExplorer/BluetoothLEExplorer/App.xaml.cs
index 08371b3..205c80c 100644
--- a/BluetoothLEExplorer/BluetoothLEExplorer/App.xaml.cs
+++ b/BluetoothLEExplorer/BluetoothLEExplorer/App.xaml.cs
@@ -103,7 +103,7 @@ private void App_Resuming(object sender, object e)
}
}
- private void App_UnhandledException(object sender, UnhandledExceptionEventArgs e)
+ private void App_UnhandledException(object sender, Windows.UI.Xaml.UnhandledExceptionEventArgs e)
{
showDialog(e.Exception.Message + "\n\n" + e.Exception.StackTrace);
}
diff --git a/BluetoothLEExplorer/BluetoothLEExplorer/BluetoothLEExplorer.csproj b/BluetoothLEExplorer/BluetoothLEExplorer/BluetoothLEExplorer.csproj
index 632070f..1128987 100644
--- a/BluetoothLEExplorer/BluetoothLEExplorer/BluetoothLEExplorer.csproj
+++ b/BluetoothLEExplorer/BluetoothLEExplorer/BluetoothLEExplorer.csproj
@@ -11,8 +11,8 @@
BluetoothLEExplorer
en-US
UAP
- 10.0.17134.0
- 10.0.15063.0
+ 10.0.19041.0
+ 10.0.18362.0
14
true
512
@@ -113,6 +113,9 @@
+
+
+
@@ -120,6 +123,7 @@
+
@@ -127,6 +131,9 @@
+
+
+
@@ -134,7 +141,7 @@
-
+
@@ -144,6 +151,19 @@
+
+ AdvertisementBeaconDetailsPage.xaml
+
+
+ AdvertisementPage.xaml
+
+
+ AdvertisementMonitorPage.xaml
+
+
+ MSBuild:Compile
+ Designer
+
ServicePage.xaml
@@ -165,8 +185,8 @@
BatteryServicePage.xaml
-
- Beacon.xaml
+
+ AdvertisementBeaconPage.xaml
Busy.xaml
@@ -242,9 +262,17 @@
MSBuild:Compile
PreserveNewest
-
+
+ Designer
MSBuild:Compile
+
+
Designer
+ MSBuild:Compile
+
+
+ Designer
+ MSBuild:Compile
MSBuild:Compile
@@ -270,7 +298,7 @@
MSBuild:Compile
Designer
-
+
MSBuild:Compile
Designer
diff --git a/BluetoothLEExplorer/BluetoothLEExplorer/Models/DisposableObservableCollection.cs b/BluetoothLEExplorer/BluetoothLEExplorer/Models/DisposableObservableCollection.cs
index a2279aa..ed367ad 100644
--- a/BluetoothLEExplorer/BluetoothLEExplorer/Models/DisposableObservableCollection.cs
+++ b/BluetoothLEExplorer/BluetoothLEExplorer/Models/DisposableObservableCollection.cs
@@ -1,12 +1,9 @@
-//
+//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//----------------------------------------------------------------------------------------------
using System;
-using System.Collections.Generic;
using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
using System.Collections.ObjectModel;
namespace BluetoothLEExplorer.Models
@@ -60,4 +57,4 @@ public void Dispose()
temp.Dispose();
}
}
-}
+}
\ No newline at end of file
diff --git a/BluetoothLEExplorer/BluetoothLEExplorer/Models/GattSampleContext.cs b/BluetoothLEExplorer/BluetoothLEExplorer/Models/GattSampleContext.cs
index d7933bc..5587a65 100644
--- a/BluetoothLEExplorer/BluetoothLEExplorer/Models/GattSampleContext.cs
+++ b/BluetoothLEExplorer/BluetoothLEExplorer/Models/GattSampleContext.cs
@@ -18,6 +18,9 @@
using Windows.Foundation.Metadata;
using System.Threading;
+using Template10.Common;
+using Windows.Devices.Bluetooth.GenericAttributeProfile;
+using Windows.Storage.Streams;
namespace BluetoothLEExplorer.Models
{
@@ -46,6 +49,10 @@ public class GattSampleContext : INotifyPropertyChanged
///
public DisposableObservableCollection BluetoothLEDevices { get; set; } = new DisposableObservableCollection();
+ private SemaphoreSlim AdvertisementsLock = new SemaphoreSlim(1, 1);
+
+ public ObservableDictionary Advertisements { get; set; } = new ObservableDictionary();
+
///
/// Gets or sets the selected bluetooth device
///
@@ -56,6 +63,8 @@ public class GattSampleContext : INotifyPropertyChanged
///
public ObservableGattDeviceService SelectedService { get; set; } = null;
+ public ObservableBluetoothLEAdvertisement SelectedAdvertisement { get; set; } = null;
+
///
/// Gets or sets the selected characteristic
///
@@ -91,6 +100,8 @@ public class GattSampleContext : INotifyPropertyChanged
///
private BluetoothLEAdvertisementWatcher advertisementWatcher;
+ private GattReliableWriteTransaction transaction;
+
///
/// 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
@@ -168,6 +179,25 @@ private set
}
}
+ private bool advertisementWatcherStarted = false;
+
+ public bool AdvertisementWatcherStarted
+ {
+ get
+ {
+ return advertisementWatcherStarted;
+ }
+
+ private set
+ {
+ if (advertisementWatcherStarted != value)
+ {
+ advertisementWatcherStarted = value;
+ OnPropertyChanged(new PropertyChangedEventArgs("AdvertisementWatcherStarted"));
+ }
+ }
+ }
+
///
/// Source for
///
@@ -233,6 +263,30 @@ public bool IsSecureConnectionSupported
}
}
+ private bool isTransactionInProgress = false;
+
+ public bool IsTransactionInProgress
+ {
+ get
+ {
+ return isTransactionInProgress;
+ }
+
+ private set
+ {
+ if (isTransactionInProgress != value)
+ {
+ isTransactionInProgress = value;
+ OnPropertyChanged(new PropertyChangedEventArgs("IsTransactionInProgress"));
+ }
+ }
+ }
+
+ public String AdvertisementContentFilter { get; set; } = "";
+
+ public ObservableCollection Beacons { get; set; } = new ObservableCollection();
+ public ObservableBluetoothLEBeacon SelectedBeacon { get; set; } = null;
+
///
/// Prevents a default instance of the class from being created.
///
@@ -277,11 +331,10 @@ private async void Init()
devNodeWatcher.Start();
- return;
+ advertisementWatcher = new BluetoothLEAdvertisementWatcher();
+ advertisementWatcher.Received += AdvertisementWatcher_Received;
}
-
-
private async void DevNodeWatcher_Added(DeviceWatcher sender, DeviceInformation args)
{
try
@@ -434,15 +487,14 @@ public void StartEnumeration()
deviceWatcher.EnumerationCompleted += DeviceWatcher_EnumerationCompleted;
deviceWatcher.Stopped += DeviceWatcher_Stopped;
- advertisementWatcher = new BluetoothLEAdvertisementWatcher();
- advertisementWatcher.Received += AdvertisementWatcher_Received;
-
ClearAllDevices();
deviceWatcher.Start();
- advertisementWatcher.Start();
IsEnumerating = true;
EnumerationFinished = false;
+
+ UpdateAdvertisementFilter(new BluetoothLEAdvertisementFilter());
+ StartAdvertisementWatcher(BluetoothLEScanningMode.Active);
}
///
@@ -450,6 +502,8 @@ public void StartEnumeration()
///
public void StopEnumeration()
{
+ StopAdvertisementWatcher();
+
if (deviceWatcher != null)
{
// Unregister the event handlers.
@@ -459,19 +513,73 @@ public void StopEnumeration()
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;
}
}
+ public void StartAdvertisementWatcher(BluetoothLEScanningMode scanningMode)
+ {
+ if (!AdvertisementWatcherStarted)
+ {
+ Advertisements.Clear();
+ advertisementWatcher.ScanningMode = scanningMode;
+ if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 10))
+ {
+ advertisementWatcher.AllowExtendedAdvertisements = true;
+ }
+ advertisementWatcher.Start();
+ AdvertisementWatcherStarted = true;
+ }
+ }
+
+ public void StopAdvertisementWatcher()
+ {
+ if (AdvertisementWatcherStarted)
+ {
+ advertisementWatcher.Stop();
+ AdvertisementWatcherStarted = false;
+ }
+ }
+
+ public void UpdateAdvertisementFilter(BluetoothLEAdvertisementFilter filter)
+ {
+ advertisementWatcher.AdvertisementFilter = filter;
+ if (AdvertisementWatcherStarted)
+ {
+ advertisementWatcher.Stop();
+ Advertisements.Clear();
+ advertisementWatcher.Start();
+ }
+ }
+
+ public void CreateTransaction()
+ {
+ transaction = new GattReliableWriteTransaction();
+ IsTransactionInProgress = true;
+ }
+
+ public async void CommitTransaction()
+ {
+ var result = await transaction.CommitWithResultAsync();
+ if (result.Status == GattCommunicationStatus.Success)
+ {
+
+ }
+
+ transaction = null;
+ IsTransactionInProgress = false;
+ }
+
+ public void WriteTransaction(GattCharacteristic characteristic, IBuffer value)
+ {
+ transaction.WriteValue(characteristic, value);
+ }
+
///
/// Updates device metadata based on advertisement received
///
@@ -481,9 +589,9 @@ private async void AdvertisementWatcher_Received(BluetoothLEAdvertisementWatcher
{
try
{
+ await AddAdvertisementToList(args);
await BluetoothLEDevicesLock.WaitAsync();
-
foreach (ObservableBluetoothLEDevice d in BluetoothLEDevices)
{
if (d.BluetoothAddressAsUlong == args.BluetoothAddress)
@@ -492,14 +600,7 @@ await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatch
Windows.UI.Core.CoreDispatcherPriority.Normal,
() =>
{
- if (args.Advertisement.ServiceUuids != null)
- {
- d.ServiceCount = args.Advertisement.ServiceUuids.Count();
- }
- else
- {
- Debug.WriteLine("Observed ADVs without ServicesUuids at UPF61");
- }
+ d.Update(args);
});
}
}
@@ -552,7 +653,7 @@ private async void DeviceWatcher_Updated(DeviceWatcher sender, DeviceInformation
{
ObservableBluetoothLEDevice dev;
- // Need to lock as another DeviceWatcher might be modifying BluetoothLEDevices
+ // Need to lock as another DeviceWatcher might be modifying BluetoothLEDevices
try
{
await BluetoothLEDevicesLock.WaitAsync();
@@ -624,7 +725,7 @@ private async void DeviceWatcher_Removed(DeviceWatcher sender, DeviceInformation
try
{
- // Need to lock as another DeviceWatcher might be modifying BluetoothLEDevices
+ // Need to lock as another DeviceWatcher might be modifying BluetoothLEDevices
await BluetoothLEDevicesLock.WaitAsync();
// Find the corresponding DeviceInformation in the collection and remove it.
@@ -693,10 +794,10 @@ private async Task AddDeviceToList(DeviceInformation deviceInfo)
(bool)dev.DeviceInfo.Properties["System.Devices.Aep.IsConnected"])) ||
((dev.DeviceInfo.Properties.Keys.Contains("System.Devices.Aep.IsPaired") &&
(bool)dev.DeviceInfo.Properties["System.Devices.Aep.IsPaired"]));
-
+
if (shouldDisplay)
{
- // Need to lock as another DeviceWatcher might be modifying BluetoothLEDevices
+ // Need to lock as another DeviceWatcher might be modifying BluetoothLEDevices
try
{
await BluetoothLEDevicesLock.WaitAsync();
@@ -731,6 +832,27 @@ await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatch
}
}
+ private async Task AddAdvertisementToList(BluetoothLEAdvertisementReceivedEventArgs advertisementEvent)
+ {
+ try
+ {
+ await AdvertisementsLock.WaitAsync();
+
+ await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(
+ Windows.UI.Core.CoreDispatcherPriority.Normal,
+ () =>
+ {
+ var advertisement = new ObservableBluetoothLEAdvertisement(advertisementEvent);
+
+ Advertisements[advertisement.InternalHashString] = advertisement;
+ });
+ }
+ finally
+ {
+ AdvertisementsLock.Release();
+ }
+ }
+
///
/// Executes when device watcher has stopped
///
diff --git a/BluetoothLEExplorer/BluetoothLEExplorer/Models/ObservableBluetoothLEAdvertisement.cs b/BluetoothLEExplorer/BluetoothLEExplorer/Models/ObservableBluetoothLEAdvertisement.cs
new file mode 100644
index 0000000..e2b0902
--- /dev/null
+++ b/BluetoothLEExplorer/BluetoothLEExplorer/Models/ObservableBluetoothLEAdvertisement.cs
@@ -0,0 +1,311 @@
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+//
+//----------------------------------------------------------------------------------------------
+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.Advertisement;
+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;
+using GattHelper.Converters;
+using BluetoothLEExplorer.Services.AdvertisementHelpers;
+using Windows.UI.Xaml.Media.Animation;
+
+namespace BluetoothLEExplorer.Models
+{
+ public class ObservableBluetoothLEAdvertisementSection : INotifyPropertyChanged
+ {
+ public string TypeAsString
+ {
+ get;
+ private set;
+ }
+
+ public string TypeAsDisplayString
+ {
+ get;
+ private set;
+ }
+
+ public string DataAsString
+ {
+ get;
+ private set;
+ }
+
+ public string DataAsDisplayString
+ {
+ get;
+ private set;
+ }
+
+ public ObservableBluetoothLEAdvertisementSection(BluetoothLEAdvertisementDataSection section)
+ {
+ TypeAsString = section.DataType.ToString("X2");
+ TypeAsDisplayString = AdvertisementDataTypeHelper.ConvertSectionTypeToString(section.DataType);
+
+ DataAsString = GattConvert.ToHexString(section.Data);
+ if (section.DataType == BluetoothLEAdvertisementDataTypes.Flags)
+ {
+ var flagsInt = GattConvert.ToInt32(section.Data);
+ DataAsDisplayString = ((BluetoothLEAdvertisementFlags)Enum.ToObject(typeof(BluetoothLEAdvertisementFlags), flagsInt)).ToString();
+ }
+ else if (section.DataType == BluetoothLEAdvertisementDataTypes.CompleteLocalName ||
+ section.DataType == BluetoothLEAdvertisementDataTypes.ShortenedLocalName)
+ {
+ DataAsDisplayString = GattConvert.ToUTF8String(section.Data);
+ }
+ else if (section.DataType == BluetoothLEAdvertisementDataTypes.TxPowerLevel)
+ {
+ var txPowerLevel = GattConvert.ToInt16(section.Data);
+ DataAsDisplayString = txPowerLevel.ToString();
+ }
+ else
+ {
+ DataAsDisplayString = "";
+ }
+ }
+
+ ///
+ /// Event to notify when this object has changed
+ ///
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ ///
+ /// Executes when this class changes
+ ///
+ ///
+ private void OnPropertyChanged(PropertyChangedEventArgs e)
+ {
+ if (PropertyChanged != null)
+ {
+ PropertyChanged(this, e);
+ }
+ }
+ }
+
+ public class ObservableBluetoothLEAdvertisement : INotifyPropertyChanged
+ {
+ public class RssiComparer : IComparer
+ {
+ public int Compare(object x, object y)
+ {
+ var a = x as ObservableBluetoothLEAdvertisement;
+ var b = y as ObservableBluetoothLEAdvertisement;
+
+ if (a == null || b == null)
+ {
+ throw new InvalidOperationException("Compared objects are not ObservableBluetoothLEAdvertisement");
+ }
+
+ // If they're equal
+ if (SmoothValue(a.Rssi) == SmoothValue(b.Rssi))
+ {
+ return 0;
+ }
+
+ if (a.Rssi < b.Rssi)
+ {
+ return 1;
+ }
+ else
+ {
+ return -1;
+ }
+ }
+
+ private short SmoothValue(short value)
+ {
+ var remainder = (value % 10);
+ if (remainder >= 5)
+ {
+ return (short)(value - remainder + 10);
+ }
+ return (short)(value - remainder);
+ }
+ }
+
+ ///
+ /// Gets the bluetooth address of this device as a string
+ ///
+ public string AddressAsString
+ {
+ get;
+ private set;
+ }
+
+ public BluetoothAddressType AddressType { get; private set; } = BluetoothAddressType.Unspecified;
+
+ public BluetoothLEAdvertisementType Type
+ {
+ get;
+ private set;
+ }
+
+ public Int16 Rssi
+ {
+ get;
+ private set;
+ }
+
+ public bool Anonymous { get; private set; } = false;
+
+ public bool Connectable { get; private set; } = false;
+ public bool Scannable { get; private set; } = false;
+ public bool Directed { get; private set; } = false;
+ public bool ScanResponse { get; private set; } = false;
+
+ public Nullable TxPower { get; private set; } = null;
+
+ public String PayloadAsString
+ {
+ get
+ {
+ String payload = "";
+ foreach (var section in DataSections)
+ {
+ payload += String.Format("{0}-{1}-", section.TypeAsString, section.DataAsString);
+ }
+ return payload.TrimEnd('-');
+ }
+ }
+
+ public String InternalHashString
+ {
+ get;
+ private set;
+ }
+
+ public DateTime Timestamp
+ {
+ get;
+ private set;
+ }
+
+ public ObservableCollection DataSections { get; } = new ObservableCollection();
+
+ public ObservableBluetoothLEAdvertisement(BluetoothLEAdvertisementReceivedEventArgs advertisementEvent)
+ {
+ AddressAsString = advertisementEvent.BluetoothAddress.ToString("X12");
+ if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 10))
+ {
+ AddressType = advertisementEvent.BluetoothAddressType;
+ Anonymous = advertisementEvent.IsAnonymous;
+ Connectable = advertisementEvent.IsConnectable;
+ Scannable = advertisementEvent.IsScannable;
+ Directed = advertisementEvent.IsDirected;
+ ScanResponse = advertisementEvent.IsScanResponse;
+ TxPower = advertisementEvent.TransmitPowerLevelInDBm;
+ }
+ else
+ {
+ if (advertisementEvent.AdvertisementType == BluetoothLEAdvertisementType.ConnectableDirected)
+ {
+ Connectable = true;
+ Directed = true;
+ }
+ else if (advertisementEvent.AdvertisementType == BluetoothLEAdvertisementType.ConnectableUndirected)
+ {
+ Connectable = true;
+ }
+ else if (advertisementEvent.AdvertisementType == BluetoothLEAdvertisementType.ScannableUndirected)
+ {
+ Scannable = true;
+ }
+ else if (advertisementEvent.AdvertisementType == BluetoothLEAdvertisementType.ScanResponse)
+ {
+ ScanResponse = true;
+ }
+ }
+
+ Type = advertisementEvent.AdvertisementType;
+ Rssi = advertisementEvent.RawSignalStrengthInDBm;
+ Timestamp = advertisementEvent.Timestamp.LocalDateTime;
+
+ foreach (var section in advertisementEvent.Advertisement.DataSections)
+ {
+ DataSections.Add(new ObservableBluetoothLEAdvertisementSection(section));
+ }
+
+ InternalHashString = AddressAsString + PayloadAsString;
+ }
+
+ ///
+ /// Event to notify when this object has changed
+ ///
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ public void Update(ObservableBluetoothLEAdvertisement other)
+ {
+ if (AddressType != other.AddressType)
+ {
+ AddressType = other.AddressType;
+ OnPropertyChanged(new PropertyChangedEventArgs("AddressType"));
+ }
+
+ if (TxPower != other.TxPower)
+ {
+ TxPower = other.TxPower;
+ OnPropertyChanged(new PropertyChangedEventArgs("TxPower"));
+ }
+
+ if (Type != other.Type)
+ {
+ Type = other.Type;
+ OnPropertyChanged(new PropertyChangedEventArgs("Type"));
+ }
+
+ if (Rssi != other.Rssi)
+ {
+ Rssi = other.Rssi;
+ OnPropertyChanged(new PropertyChangedEventArgs("Rssi"));
+ }
+
+ if (Timestamp != other.Timestamp)
+ {
+ Timestamp = other.Timestamp;
+ OnPropertyChanged(new PropertyChangedEventArgs("Timestamp"));
+ }
+
+ Anonymous = other.Anonymous;
+ Connectable = other.Connectable;
+ Scannable = other.Scannable;
+ Directed = other.Directed;
+ ScanResponse = other.ScanResponse;
+ }
+
+ public override int GetHashCode()
+ {
+ return InternalHashString.GetHashCode();
+ }
+
+ public override bool Equals(object obj)
+ {
+ return InternalHashString.Equals(((ObservableBluetoothLEAdvertisement)obj).InternalHashString);
+ }
+
+ ///
+ /// Executes when this class changes
+ ///
+ ///
+ private void OnPropertyChanged(PropertyChangedEventArgs e)
+ {
+ if (PropertyChanged != null)
+ {
+ PropertyChanged(this, e);
+ }
+ }
+ }
+}
diff --git a/BluetoothLEExplorer/BluetoothLEExplorer/Models/ObservableBluetoothLEAdvertisementFilter.cs b/BluetoothLEExplorer/BluetoothLEExplorer/Models/ObservableBluetoothLEAdvertisementFilter.cs
new file mode 100644
index 0000000..1646dee
--- /dev/null
+++ b/BluetoothLEExplorer/BluetoothLEExplorer/Models/ObservableBluetoothLEAdvertisementFilter.cs
@@ -0,0 +1,133 @@
+using System;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Windows.Devices.Bluetooth;
+using Windows.Devices.Bluetooth.Advertisement;
+using Windows.Foundation.Metadata;
+using Windows.Storage.Streams;
+using GattHelper.Converters;
+using BluetoothLEExplorer.Services.AdvertisementHelpers;
+using Windows.Security.Cryptography;
+
+namespace BluetoothLEExplorer.Models
+{
+ public class ObservableBluetoothLEAdvertisementFilter : INotifyPropertyChanged
+ {
+ public enum DataFormatType
+ {
+ NotSet,
+ Hex,
+ String,
+ }
+
+ public string Name
+ {
+ get;
+ private set;
+ }
+
+ public byte SectionType
+ {
+ get;
+ private set;
+ }
+
+ public byte SectionOffset
+ {
+ get;
+ set;
+ }
+
+ public string SectionDataString
+ {
+ get;
+ set;
+ }
+
+ public bool DataSectionRawFilter
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ private DataFormatType sectionDataFormat = DataFormatType.Hex;
+
+ public DataFormatType SectionDataFormat
+ {
+ get
+ {
+ return sectionDataFormat;
+ }
+
+ set
+ {
+ if (value == DataFormatType.NotSet)
+ {
+ return;
+ }
+
+ if (sectionDataFormat != value)
+ {
+ sectionDataFormat = value;
+ OnPropertyChanged(new PropertyChangedEventArgs("SectionDataFormat"));
+ }
+ }
+ }
+
+ public ObservableBluetoothLEAdvertisementFilter(byte sectionType)
+ {
+ SectionType = sectionType;
+ Name = AdvertisementDataTypeHelper.ConvertSectionTypeToString(sectionType);
+ }
+
+ ///
+ /// Event to notify when this object has changed
+ ///
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ ///
+ /// Executes when this class changes
+ ///
+ ///
+ private void OnPropertyChanged(PropertyChangedEventArgs e)
+ {
+ if (PropertyChanged != null)
+ {
+ PropertyChanged(this, e);
+ }
+ }
+
+ public BluetoothLEAdvertisementBytePattern GetBytePattern()
+ {
+ var pattern = new BluetoothLEAdvertisementBytePattern();
+ pattern.DataType = SectionType;
+ pattern.Offset = SectionOffset;
+ if (SectionDataString != null && SectionDataString.Length > 0)
+ {
+ if (sectionDataFormat == DataFormatType.Hex)
+ {
+ // pad the value if we've received odd number of bytes
+ if (SectionDataString.Length % 2 == 1)
+ {
+ pattern.Data = GattConvert.ToIBufferFromHexString("0" + SectionDataString);
+ }
+ else
+ {
+ pattern.Data = GattConvert.ToIBufferFromHexString(SectionDataString);
+ }
+ }
+ else if (sectionDataFormat == DataFormatType.String)
+ {
+ pattern.Data = CryptographicBuffer.ConvertStringToBinary(SectionDataString, BinaryStringEncoding.Utf8);
+ }
+ }
+ return pattern;
+ }
+ }
+}
diff --git a/BluetoothLEExplorer/BluetoothLEExplorer/Models/ObservableBluetoothLEBeacon.cs b/BluetoothLEExplorer/BluetoothLEExplorer/Models/ObservableBluetoothLEBeacon.cs
new file mode 100644
index 0000000..f4a6596
--- /dev/null
+++ b/BluetoothLEExplorer/BluetoothLEExplorer/Models/ObservableBluetoothLEBeacon.cs
@@ -0,0 +1,139 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Diagnostics;
+using System.Runtime.InteropServices.WindowsRuntime;
+using Windows.Devices.Bluetooth;
+using Windows.Devices.Bluetooth.Advertisement;
+using Windows.Foundation.Metadata;
+using GattHelper.Converters;
+
+namespace BluetoothLEExplorer.Models
+{
+
+ public class ObservableBluetoothLEBeacon : INotifyPropertyChanged
+ {
+ private GattSampleContext Context { get; set; }
+
+ private BluetoothLEAdvertisementPublisher publisher;
+
+ public String Name { get; private set; }
+
+ private BluetoothLEAdvertisementPublisherStatus status = BluetoothLEAdvertisementPublisherStatus.Created;
+
+ bool isPublishing = false;
+
+ public bool IsPublishing
+ {
+ get
+ {
+ return isPublishing;
+ }
+
+ set
+ {
+ if (value != isPublishing)
+ {
+ if (value)
+ {
+ Start();
+ }
+ else
+ {
+ Stop();
+ }
+ }
+ }
+ }
+
+ public bool IsReady { get; private set; } = true;
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ public ObservableBluetoothLEBeacon(
+ byte[] payload,
+ bool useExtendedFormat,
+ bool isAnonymous,
+ bool includeTxPower,
+ Int16? txPower)
+ {
+ Context = GattSampleContext.Context;
+
+ var payloadString = GattConvert.ToHexString(payload.AsBuffer());
+ Name = payloadString.Substring(0, Math.Min(8, payloadString.Length));
+
+ var advertisement = new BluetoothLEAdvertisement();
+ advertisement.ManufacturerData.Add(new BluetoothLEManufacturerData(0x0006, payload.AsBuffer()));
+
+ publisher = new BluetoothLEAdvertisementPublisher(advertisement);
+
+ if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 10))
+ {
+ publisher.UseExtendedAdvertisement = useExtendedFormat;
+ publisher.IsAnonymous = isAnonymous;
+ publisher.IncludeTransmitPowerLevel = includeTxPower;
+ publisher.PreferredTransmitPowerLevelInDBm = txPower;
+ }
+
+ publisher.StatusChanged += Publisher_StatusChanged;
+ }
+
+ private void Publisher_StatusChanged(BluetoothLEAdvertisementPublisher sender, BluetoothLEAdvertisementPublisherStatusChangedEventArgs args)
+ {
+ if (args.Status == BluetoothLEAdvertisementPublisherStatus.Started)
+ {
+ if (!isPublishing)
+ {
+ isPublishing = true;
+ OnPropertyChanged("IsPublishing");
+ }
+ IsReady = true;
+ }
+ else if (args.Status == BluetoothLEAdvertisementPublisherStatus.Stopped ||
+ args.Status == BluetoothLEAdvertisementPublisherStatus.Aborted)
+ {
+ if (isPublishing)
+ {
+ isPublishing = false;
+ OnPropertyChanged("IsPublishing");
+ }
+ IsReady = true;
+ }
+ }
+
+ public void Start()
+ {
+ IsReady = false;
+ publisher.Start();
+ }
+
+ public void Stop()
+ {
+ IsReady = false;
+ publisher.Stop();
+ }
+
+ public void Destroy()
+ {
+ Stop();
+ Context.Beacons.Remove(this);
+ }
+
+ private async void OnPropertyChanged(string propertyName)
+ {
+ try
+ {
+ await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(
+ Windows.UI.Core.CoreDispatcherPriority.Normal,
+ () => { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); });
+ }
+ catch (Exception e)
+ {
+ Debug.Fail(String.Format("Failed to update property '{0}' due to {1}", propertyName, e.ToString()));
+ }
+ }
+ }
+}
diff --git a/BluetoothLEExplorer/BluetoothLEExplorer/Models/ObservableBluetoothLEDevice.cs b/BluetoothLEExplorer/BluetoothLEExplorer/Models/ObservableBluetoothLEDevice.cs
index 850a2bd..b85e348 100644
--- a/BluetoothLEExplorer/BluetoothLEExplorer/Models/ObservableBluetoothLEDevice.cs
+++ b/BluetoothLEExplorer/BluetoothLEExplorer/Models/ObservableBluetoothLEDevice.cs
@@ -1,803 +1,864 @@
-//
-// Copyright (c) Microsoft Corporation. All rights reserved.
-//
-//----------------------------------------------------------------------------------------------
-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;
-using BluetoothLEExplorer.Services.GattUuidHelpers;
-
-namespace BluetoothLEExplorer.Models
-{
- ///
- /// Wrapper around to make it easier to use
- ///
- public class ObservableBluetoothLEDevice : INotifyPropertyChanged, IEquatable, IDisposable
- {
- ///
- /// Compares RSSI values between ObservableBluetoothLEDevice. Sorts based on closest to furthest where 0 is unknown
- /// and is sorted as furthest away
- ///
- 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;
- }
- }
- }
-
- ///
- /// Source for
- ///
- private BluetoothLEDevice bluetoothLEDevice;
-
- ///
- /// Gets the bluetooth device this class wraps
- ///
- public BluetoothLEDevice BluetoothLEDevice
- {
- get
- {
- return bluetoothLEDevice;
- }
-
- private set
- {
- bluetoothLEDevice = value;
- OnPropertyChanged(new PropertyChangedEventArgs("BluetoothLEDevice"));
- }
- }
-
- ///
- /// Source for
- ///
- private BitmapImage glyph;
-
- ///
- /// Gets or sets the glyph of this bluetooth device
- ///
- public BitmapImage Glyph
- {
- get
- {
- return glyph;
- }
-
- set
- {
- glyph = value;
- OnPropertyChanged(new PropertyChangedEventArgs("Glyph"));
- }
- }
-
- ///
- /// Source for
- ///
- private DeviceInformation deviceInfo;
-
- ///
- /// Gets the device information for the device this class wraps
- ///
- public DeviceInformation DeviceInfo
- {
- get
- {
- return deviceInfo;
- }
-
- private set
- {
- deviceInfo = value;
- OnPropertyChanged(new PropertyChangedEventArgs("DeviceInfo"));
- }
- }
-
- ///
- /// Source for
- ///
- private bool isConnected;
-
- ///
- /// Gets or sets a value indicating whether this device is connected
- ///
- public bool IsConnected
- {
- get
- {
- return isConnected;
- }
-
- set
- {
- if (isConnected != value)
- {
- isConnected = value;
- OnPropertyChanged(new PropertyChangedEventArgs("IsConnected"));
- }
- }
- }
-
- ///
- /// Gets if the device is connectable
- ///
- public bool IsConnectable
- {
- get
- {
- return DeviceInfo.Properties.Keys.Contains("System.Devices.Aep.Bluetooth.Le.IsConnectable") &&
- (bool)DeviceInfo.Properties["System.Devices.Aep.Bluetooth.Le.IsConnectable"];
- }
- }
-
- ///
- /// Source for
- ///
- private DateTime lastSeenTime;
-
- ///
- /// Gets or sets a value indicating the last time an advertisement was seen from the device
- ///
- public DateTime LastSeenTime
- {
- get
- {
- return lastSeenTime;
- }
-
- set
- {
- if (lastSeenTime != value)
- {
- lastSeenTime = value;
- OnPropertyChanged(new PropertyChangedEventArgs("LastSeenTime"));
- }
- }
- }
-
- ///
- /// Source for
- ///
- private bool isPaired;
-
- ///
- /// Gets or sets a value indicating whether this device is paired
- ///
- public bool IsPaired
- {
- get
- {
- return isPaired;
- }
-
- set
- {
- if (isPaired != value)
- {
- isPaired = value;
- OnPropertyChanged(new PropertyChangedEventArgs("IsPaired"));
- }
- }
- }
-
- ///
- /// Source for
- ///
- private bool canPair;
-
- ///
- /// Gets or sets a value indicating whether this device is paired
- ///
- public bool CanPair
- {
- get
- {
- return canPair;
- }
-
- set
- {
- if (canPair != value)
- {
- canPair = value;
- OnPropertyChanged(new PropertyChangedEventArgs("CanPair"));
- }
- }
- }
-
- private bool isSecureConnection;
-
- public bool IsSecureConnection
- {
- get
- {
- return isSecureConnection;
- }
-
- set
- {
- if (isSecureConnection != value)
- {
- isSecureConnection = value;
- OnPropertyChanged(new PropertyChangedEventArgs("IsSecureConnection"));
- }
- }
- }
-
- // Make this variable static so we only query IsPropertyPresent once
- private static bool isSecureConnectionSupported = ApiInformation.IsPropertyPresent("Windows.Devices.Bluetooth.BluetoothLEDevice", "WasSecureConnectionUsedForPairing");
-
- ///
- /// Source for
- ///
- private DisposableObservableCollection services = new DisposableObservableCollection();
-
- ///
- /// Gets the services this device supports
- ///
- public DisposableObservableCollection Services
- {
- get
- {
- return services;
- }
-
- private set
- {
- services = value;
- OnPropertyChanged(new PropertyChangedEventArgs("Services"));
- }
- }
-
- ///
- /// Source for
- ///
- private int serviceCount;
-
- ///
- /// Gets or sets the number of services this device has
- ///
- public int ServiceCount
- {
- get
- {
- return serviceCount;
- }
-
- set
- {
- if (serviceCount < value)
- {
- serviceCount = value;
- OnPropertyChanged(new PropertyChangedEventArgs("ServiceCount"));
- }
- }
- }
-
- ///
- /// Source for
- ///
- private string name;
-
- ///
- /// Gets the name of this device
- ///
- public string Name
- {
- get
- {
- return name;
- }
-
- private set
- {
- if (name != value)
- {
- name = value;
- OnPropertyChanged(new PropertyChangedEventArgs("Name"));
- }
- }
- }
-
- ///
- /// Source for
- ///
- private string errorText;
-
- ///
- /// Gets the error text when connecting to this device fails
- ///
- public string ErrorText
- {
- get
- {
- return errorText;
- }
-
- private set
- {
- errorText = value;
- OnPropertyChanged(new PropertyChangedEventArgs("ErrorText"));
- }
- }
-
- private Queue RssiValue = new Queue(10);
-
- ///
- /// Source for
- ///
- private int rssi;
-
- ///
- /// Gets the RSSI value of this device
- ///
- 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"));
- }
- }
- }
-
- private string bluetoothAddressAsString;
- ///
- /// Gets the bluetooth address of this device as a string
- ///
- public string BluetoothAddressAsString
- {
- get
- {
- return bluetoothAddressAsString;
- }
-
- private set
- {
- if(bluetoothAddressAsString != value)
- {
- bluetoothAddressAsString = value;
- OnPropertyChanged(new PropertyChangedEventArgs("BluetoothAddressAsString"));
- }
- }
- }
-
- private ulong bluetoothAddressAsUlong;
- ///
- /// Gets the bluetooth address of this device
- ///
- public ulong BluetoothAddressAsUlong
- {
- get
- {
- return bluetoothAddressAsUlong;
- }
-
- private set
- {
- if (bluetoothAddressAsUlong != value)
- {
- bluetoothAddressAsUlong = value;
- OnPropertyChanged(new PropertyChangedEventArgs("BluetoothAddressAsUlong"));
- }
- }
- }
-
- ///
- /// Releases references to Services and the BluetoothLEDevice
- ///
- public void Dispose()
- {
- Services.Clear();
- var temp = bluetoothLEDevice;
-
- BluetoothLEDevice = null;
-
- if (temp != null)
- {
- temp.ConnectionStatusChanged -= BluetoothLEDevice_ConnectionStatusChanged;
- temp.NameChanged -= BluetoothLEDevice_NameChanged;
- temp.Dispose();
- }
- IsConnected = false;
- }
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The device info that describes this bluetooth device"/>
- 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;
- CanPair = DeviceInfo.Pairing.CanPair;
-
- LoadGlyph();
-
- this.PropertyChanged += ObservableBluetoothLEDevice_PropertyChanged;
- }
-
- private void ObservableBluetoothLEDevice_PropertyChanged(object sender, PropertyChangedEventArgs e)
- {
- if (e.PropertyName == "DeviceInfo")
- {
- if (DeviceInfo.Properties.ContainsKey("System.Devices.Aep.Bluetooth.LastSeenTime") && (DeviceInfo.Properties["System.Devices.Aep.Bluetooth.LastSeenTime"] != null))
- {
- LastSeenTime = ((System.DateTimeOffset)DeviceInfo.Properties["System.Devices.Aep.Bluetooth.LastSeenTime"]).UtcDateTime;
- }
-
- if (DeviceInfo.Properties.ContainsKey("System.Devices.Aep.SignalStrength") && (DeviceInfo.Properties["System.Devices.Aep.SignalStrength"] != null))
- {
- RSSI = (int)DeviceInfo.Properties["System.Devices.Aep.SignalStrength"];
- }
- }
- }
-
- ///
- /// result of finding all the services
- ///
- private GattDeviceServicesResult result;
-
- ///
- /// Event to notify when this object has changed
- ///
- public event PropertyChangedEventHandler PropertyChanged;
-
- ///
- /// Connect to this bluetooth device
- ///
- /// Connection task
- public async Task 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);
-
- 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();
- return;
- }
- else
- {
- // Setup our event handlers and view model properties
- BluetoothLEDevice.ConnectionStatusChanged += BluetoothLEDevice_ConnectionStatusChanged;
- BluetoothLEDevice.NameChanged += BluetoothLEDevice_NameChanged;
- }
- }
- else
- {
- Debug.WriteLine(debugMsg + "Previously connected, not calling BluetoothLEDevice.FromIdAsync");
- }
-
- Debug.WriteLine(debugMsg + "BluetoothLEDevice is " + BluetoothLEDevice.Name);
-
- Name = bluetoothLEDevice.Name;
- CanPair = DeviceInfo.Pairing.CanPair;
- IsPaired = DeviceInfo.Pairing.IsPaired;
- IsConnected = BluetoothLEDevice.ConnectionStatus == BluetoothConnectionStatus.Connected;
-
- UpdateSecureConnectionStatus();
-
- // Get all the services for this device
- CancellationTokenSource GetGattServicesAsyncTokenSource = new CancellationTokenSource(5000);
-
- BluetoothCacheMode cacheMode = BluetoothLEExplorer.Services.SettingsServices.SettingsService.Instance.UseCaching ? BluetoothCacheMode.Cached : BluetoothCacheMode.Uncached;
-
- // In case we connected before, clear the service list and recreate it
- Services.Clear();
-
- var GetGattServicesAsyncTask = Task.Run(() => BluetoothLEDevice.GetGattServicesAsync(cacheMode), GetGattServicesAsyncTokenSource.Token);
-
- result = await GetGattServicesAsyncTask.Result;
-
- if (result.Status == GattCommunicationStatus.Success)
- {
- System.Diagnostics.Debug.WriteLine(debugMsg + "GetGattServiceAsync SUCCESS");
- foreach (var serv in result.Services)
- {
- if (!GattServiceUuidHelper.IsReserved(serv.Uuid))
- {
- ObservableGattDeviceService temp = new ObservableGattDeviceService(serv);
- // This isn't awaited so that the user can disconnect while the services are still being enumerated
- temp.Initialize();
- Services.Add(temp);
- }
- else
- {
- serv.Dispose();
- }
- }
-
- 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 DoInAppPairing()
- {
- Debug.WriteLine("Trying in app pairing");
-
- // BT_Code: Pair the currently selected device.
- DevicePairingResult result = await DeviceInfo.Pairing.PairAsync();
-
- CanPair = DeviceInfo.Pairing.CanPair;
- IsPaired = DeviceInfo.Pairing.IsPaired;
-
- 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;
- }
- }
-
- ///
- /// Executes when the name of this devices changes
- ///
- ///
- ///
- private async void BluetoothLEDevice_NameChanged(BluetoothLEDevice sender, object args)
- {
- await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(
- Windows.UI.Core.CoreDispatcherPriority.Normal,
- () =>
- {
- try
- {
- Name = BluetoothLEDevice.Name;
- }
- catch
- {
- Name = "Unknown (exception)";
- }
-
- });
- }
-
- ///
- /// Executes when the connection state changes
- ///
- ///
- ///
- 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;
- CanPair = DeviceInfo.Pairing.CanPair;
- try
- {
- IsConnected = bluetoothLEDevice.ConnectionStatus == BluetoothConnectionStatus.Connected;
- UpdateSecureConnectionStatus();
- }
- catch
- {
- IsConnected = false;
- }
- });
- }
-
- ///
- /// Load the glyph for this device
- ///
- private async void LoadGlyph()
- {
- await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(
- Windows.UI.Core.CoreDispatcherPriority.Normal,
- async () =>
- {
- try
- {
- DeviceThumbnail deviceThumbnail = await DeviceInfo.GetGlyphThumbnailAsync();
- BitmapImage glyphBitmapImage = new BitmapImage();
- await glyphBitmapImage.SetSourceAsync(deviceThumbnail);
- Glyph = glyphBitmapImage;
- }
- catch (Exception)
- {
- Glyph = null;
- }
- });
- }
-
- ///
- /// Executes when a property is changed
- ///
- ///
- private void OnPropertyChanged(PropertyChangedEventArgs e)
- {
- if (PropertyChanged != null)
- {
- PropertyChanged(this, e);
- }
- }
-
- ///
- /// Overrides the ToString function to return the name of the device
- ///
- /// Name of this characteristic
- public override string ToString()
- {
- return this.name;
- }
-
- ///
- /// Compares this device to other bluetooth devices by checking the id
- ///
- ///
- /// true for equal
- bool IEquatable.Equals(ObservableBluetoothLEDevice other)
- {
- if (other == null)
- {
- return false;
- }
-
- if (this.DeviceInfo.Id == other.DeviceInfo.Id)
- {
- return true;
- }
- else
- {
- return false;
- }
- }
-
- ///
- /// Updates this device's deviceInformation
- ///
- ///
- public void Update(DeviceInformationUpdate deviceUpdate)
- {
- DeviceInfo.Update(deviceUpdate);
-
- OnPropertyChanged(new PropertyChangedEventArgs("DeviceInfo"));
- }
-
- ///
- /// Helper method which checks to see if the Secure Connection APIs are supported
- /// and updates the current status.
- ///
- private void UpdateSecureConnectionStatus()
- {
- if (isSecureConnectionSupported)
- {
- IsSecureConnection = bluetoothLEDevice.WasSecureConnectionUsedForPairing;
- }
- else
- {
- IsSecureConnection = false;
- }
- }
- }
-}
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+//
+//----------------------------------------------------------------------------------------------
+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.Advertisement;
+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;
+using BluetoothLEExplorer.Services.GattUuidHelpers;
+
+namespace BluetoothLEExplorer.Models
+{
+ ///
+ /// Wrapper around to make it easier to use
+ ///
+ public class ObservableBluetoothLEDevice : INotifyPropertyChanged, IEquatable, IDisposable
+ {
+ ///
+ /// Compares RSSI values between ObservableBluetoothLEDevice. Sorts based on closest to furthest where 0 is unknown
+ /// and is sorted as furthest away
+ ///
+ 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;
+ }
+ }
+ }
+
+ ///
+ /// Source for
+ ///
+ private BluetoothLEDevice bluetoothLEDevice;
+
+ ///
+ /// Gets the bluetooth device this class wraps
+ ///
+ public BluetoothLEDevice BluetoothLEDevice
+ {
+ get
+ {
+ return bluetoothLEDevice;
+ }
+
+ private set
+ {
+ bluetoothLEDevice = value;
+ OnPropertyChanged(new PropertyChangedEventArgs("BluetoothLEDevice"));
+ }
+ }
+
+ ///
+ /// Source for
+ ///
+ private BitmapImage glyph;
+
+ ///
+ /// Gets or sets the glyph of this bluetooth device
+ ///
+ public BitmapImage Glyph
+ {
+ get
+ {
+ return glyph;
+ }
+
+ set
+ {
+ glyph = value;
+ OnPropertyChanged(new PropertyChangedEventArgs("Glyph"));
+ }
+ }
+
+ ///
+ /// Source for
+ ///
+ private DeviceInformation deviceInfo;
+
+ ///
+ /// Gets the device information for the device this class wraps
+ ///
+ public DeviceInformation DeviceInfo
+ {
+ get
+ {
+ return deviceInfo;
+ }
+
+ private set
+ {
+ deviceInfo = value;
+ OnPropertyChanged(new PropertyChangedEventArgs("DeviceInfo"));
+ }
+ }
+
+ ///
+ /// Source for
+ ///
+ private bool isConnected;
+
+ ///
+ /// Gets or sets a value indicating whether this device is connected
+ ///
+ public bool IsConnected
+ {
+ get
+ {
+ return isConnected;
+ }
+
+ set
+ {
+ if (isConnected != value)
+ {
+ isConnected = value;
+ OnPropertyChanged(new PropertyChangedEventArgs("IsConnected"));
+ }
+ }
+ }
+
+ ///
+ /// Gets if the device is connectable
+ ///
+ public bool IsConnectable
+ {
+ get
+ {
+ return DeviceInfo.Properties.Keys.Contains("System.Devices.Aep.Bluetooth.Le.IsConnectable") &&
+ (bool)DeviceInfo.Properties["System.Devices.Aep.Bluetooth.Le.IsConnectable"];
+ }
+ }
+
+ ///
+ /// Source for
+ ///
+ private DateTime lastSeenTime;
+
+ ///
+ /// Gets or sets a value indicating the last time an advertisement was seen from the device
+ ///
+ public DateTime LastSeenTime
+ {
+ get
+ {
+ return lastSeenTime;
+ }
+
+ set
+ {
+ if (lastSeenTime != value)
+ {
+ lastSeenTime = value;
+ OnPropertyChanged(new PropertyChangedEventArgs("LastSeenTime"));
+ }
+ }
+ }
+
+ ///
+ /// Source for
+ ///
+ private bool isPaired;
+
+ ///
+ /// Gets or sets a value indicating whether this device is paired
+ ///
+ public bool IsPaired
+ {
+ get
+ {
+ return isPaired;
+ }
+
+ set
+ {
+ if (isPaired != value)
+ {
+ isPaired = value;
+ OnPropertyChanged(new PropertyChangedEventArgs("IsPaired"));
+ }
+ }
+ }
+
+ ///
+ /// Source for
+ ///
+ private bool canPair;
+
+ ///
+ /// Gets or sets a value indicating whether this device is paired
+ ///
+ public bool CanPair
+ {
+ get
+ {
+ return canPair;
+ }
+
+ set
+ {
+ if (canPair != value)
+ {
+ canPair = value;
+ OnPropertyChanged(new PropertyChangedEventArgs("CanPair"));
+ }
+ }
+ }
+
+ private bool isSecureConnection;
+
+ public bool IsSecureConnection
+ {
+ get
+ {
+ return isSecureConnection;
+ }
+
+ set
+ {
+ if (isSecureConnection != value)
+ {
+ isSecureConnection = value;
+ OnPropertyChanged(new PropertyChangedEventArgs("IsSecureConnection"));
+ }
+ }
+ }
+
+ // Make this variable static so we only query IsPropertyPresent once
+ private static bool isSecureConnectionSupported = ApiInformation.IsPropertyPresent("Windows.Devices.Bluetooth.BluetoothLEDevice", "WasSecureConnectionUsedForPairing");
+
+ private ObservableCollection advertisements = new ObservableCollection();
+
+ public ObservableCollection Advertisements
+ {
+ get
+ {
+ return advertisements;
+ }
+ }
+
+ ///
+ /// Source for
+ ///
+ private DisposableObservableCollection services = new DisposableObservableCollection();
+
+ ///
+ /// Gets the services this device supports
+ ///
+ public DisposableObservableCollection Services
+ {
+ get
+ {
+ return services;
+ }
+
+ private set
+ {
+ services = value;
+ OnPropertyChanged(new PropertyChangedEventArgs("Services"));
+ }
+ }
+
+ ///
+ /// Source for
+ ///
+ private int serviceCount;
+
+ ///
+ /// Gets or sets the number of services this device has
+ ///
+ public int ServiceCount
+ {
+ get
+ {
+ return serviceCount;
+ }
+
+ private set
+ {
+ if (serviceCount < value)
+ {
+ serviceCount = value;
+ OnPropertyChanged(new PropertyChangedEventArgs("ServiceCount"));
+ }
+ }
+ }
+
+ private int advertisementServiceCount;
+
+ public int AdvertisementServiceCount
+ {
+ get
+ {
+ return advertisementServiceCount;
+ }
+
+ private set
+ {
+ if (advertisementServiceCount < value)
+ {
+ advertisementServiceCount = value;
+ OnPropertyChanged(new PropertyChangedEventArgs("AdvertisementServiceCount"));
+ }
+ }
+ }
+
+ ///
+ /// Source for
+ ///
+ private string name;
+
+ ///
+ /// Gets the name of this device
+ ///
+ public string Name
+ {
+ get
+ {
+ return name;
+ }
+
+ private set
+ {
+ if (name != value)
+ {
+ name = value;
+ OnPropertyChanged(new PropertyChangedEventArgs("Name"));
+ }
+ }
+ }
+
+ ///
+ /// Source for
+ ///
+ private string errorText;
+
+ ///
+ /// Gets the error text when connecting to this device fails
+ ///
+ public string ErrorText
+ {
+ get
+ {
+ return errorText;
+ }
+
+ private set
+ {
+ errorText = value;
+ OnPropertyChanged(new PropertyChangedEventArgs("ErrorText"));
+ }
+ }
+
+ private Queue RssiValue = new Queue(10);
+
+ ///
+ /// Source for
+ ///
+ private int rssi;
+
+ ///
+ /// Gets the RSSI value of this device
+ ///
+ 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"));
+ }
+ }
+ }
+
+ private string bluetoothAddressAsString;
+ ///
+ /// Gets the bluetooth address of this device as a string
+ ///
+ public string BluetoothAddressAsString
+ {
+ get
+ {
+ return bluetoothAddressAsString;
+ }
+
+ private set
+ {
+ if(bluetoothAddressAsString != value)
+ {
+ bluetoothAddressAsString = value;
+ OnPropertyChanged(new PropertyChangedEventArgs("BluetoothAddressAsString"));
+ }
+ }
+ }
+
+ private ulong bluetoothAddressAsUlong;
+ ///
+ /// Gets the bluetooth address of this device
+ ///
+ public ulong BluetoothAddressAsUlong
+ {
+ get
+ {
+ return bluetoothAddressAsUlong;
+ }
+
+ private set
+ {
+ if (bluetoothAddressAsUlong != value)
+ {
+ bluetoothAddressAsUlong = value;
+ OnPropertyChanged(new PropertyChangedEventArgs("BluetoothAddressAsUlong"));
+ }
+ }
+ }
+
+ ///
+ /// Releases references to Services and the BluetoothLEDevice
+ ///
+ public void Dispose()
+ {
+ Services.Clear();
+ var temp = bluetoothLEDevice;
+ BluetoothLEDevice = null;
+
+ if (temp != null)
+ {
+ // Tear down event handlers
+ temp.ConnectionStatusChanged -= BluetoothLEDevice_ConnectionStatusChanged;
+ temp.NameChanged -= BluetoothLEDevice_NameChanged;
+ temp.GattServicesChanged -= BluetoothLEDevice_GattServicesChanged;
+ temp.Dispose();
+ }
+
+ IsConnected = false;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The device info that describes this bluetooth device"/>
+ 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;
+ CanPair = DeviceInfo.Pairing.CanPair;
+
+ LoadGlyph();
+
+ this.PropertyChanged += ObservableBluetoothLEDevice_PropertyChanged;
+ }
+
+ private void ObservableBluetoothLEDevice_PropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == "DeviceInfo")
+ {
+ if (DeviceInfo.Properties.ContainsKey("System.Devices.Aep.Bluetooth.LastSeenTime") && (DeviceInfo.Properties["System.Devices.Aep.Bluetooth.LastSeenTime"] != null))
+ {
+ LastSeenTime = ((System.DateTimeOffset)DeviceInfo.Properties["System.Devices.Aep.Bluetooth.LastSeenTime"]).UtcDateTime;
+ }
+ }
+ }
+
+ ///
+ /// result of finding all the services
+ ///
+ private GattDeviceServicesResult result;
+
+ ///
+ /// Event to notify when this object has changed
+ ///
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ ///
+ /// Connect to this bluetooth device
+ ///
+ /// Connection task
+ public async Task 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;
+ BluetoothLEDevice.GattServicesChanged += BluetoothLEDevice_GattServicesChanged;
+
+ IsPaired = DeviceInfo.Pairing.IsPaired;
+ CanPair = DeviceInfo.Pairing.CanPair;
+ IsConnected = BluetoothLEDevice.ConnectionStatus == BluetoothConnectionStatus.Connected;
+ Name = BluetoothLEDevice.Name;
+
+ UpdateSecureConnectionStatus();
+
+ BluetoothCacheMode cacheMode = BluetoothLEExplorer.Services.SettingsServices.SettingsService.Instance.UseCaching ? BluetoothCacheMode.Cached : BluetoothCacheMode.Uncached;
+
+ ret = await GetAllPrimaryServices(cacheMode);
+ }
+ }
+ 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;
+ }
+
+ private async Task GetAllPrimaryServices(BluetoothCacheMode cacheMode)
+ {
+ var succeeded = false;
+ string debugMsg = String.Format("GetAllPrimaryServices: ");
+
+ Debug.WriteLine(debugMsg + "Entering");
+
+ // Get all the services for this device
+ var result = await BluetoothLEDevice.GetGattServicesAsync(cacheMode);
+ var returnedServices = new DisposableObservableCollection();
+
+ if (result.Status == GattCommunicationStatus.Success)
+ {
+ System.Diagnostics.Debug.WriteLine(debugMsg + "GetGattServiceAsync SUCCESS");
+ foreach (var serv in result.Services)
+ {
+ if (!GattServiceUuidHelper.IsReserved(serv.Uuid))
+ {
+ var temp = new ObservableGattDeviceService(serv);
+ // This isn't awaited so that the user can disconnect while the services are still being enumerated
+ temp.Initialize();
+ returnedServices.Add(temp);
+ }
+ else
+ {
+ serv.Dispose();
+ }
+ }
+
+ succeeded = 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();
+ }
+
+ lock (Services)
+ {
+ // In case we connected before, clear the service list and recreate it
+ Services.Clear();
+ Services = returnedServices;
+ ServiceCount = Services.Count();
+ }
+
+ return succeeded;
+ }
+
+ public async Task DoInAppPairing()
+ {
+ Debug.WriteLine("Trying in app pairing");
+
+ // BT_Code: Pair the currently selected device.
+ DevicePairingResult result = await DeviceInfo.Pairing.PairAsync();
+
+ CanPair = DeviceInfo.Pairing.CanPair;
+ IsPaired = DeviceInfo.Pairing.IsPaired;
+
+ 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;
+ }
+ }
+
+ ///
+ /// Executes when the name of this devices changes
+ ///
+ ///
+ ///
+ private async void BluetoothLEDevice_NameChanged(BluetoothLEDevice sender, object args)
+ {
+ await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(
+ Windows.UI.Core.CoreDispatcherPriority.Normal,
+ () =>
+ {
+ try
+ {
+ Name = BluetoothLEDevice.Name;
+ }
+ catch
+ {
+ Name = "Unknown (exception)";
+ }
+ });
+ }
+
+ private async void BluetoothLEDevice_GattServicesChanged(BluetoothLEDevice sender, object args)
+ {
+ await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunTaskAsync(async () =>
+ {
+ // If we received a service change. If we are in the middle of discovery services,
+ // we can ignore the service change.
+ await GetAllPrimaryServices(IsPaired ? BluetoothCacheMode.Cached : BluetoothCacheMode.Uncached);
+ });
+ }
+
+ ///
+ /// Executes when the connection state changes
+ ///
+ ///
+ ///
+ 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;
+ CanPair = DeviceInfo.Pairing.CanPair;
+ try
+ {
+ IsConnected = bluetoothLEDevice.ConnectionStatus == BluetoothConnectionStatus.Connected;
+ UpdateSecureConnectionStatus();
+ }
+ catch
+ {
+ IsConnected = false;
+ }
+ });
+ }
+
+ ///
+ /// Load the glyph for this device
+ ///
+ private async void LoadGlyph()
+ {
+ await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(
+ Windows.UI.Core.CoreDispatcherPriority.Normal,
+ async () =>
+ {
+ try
+ {
+ DeviceThumbnail deviceThumbnail = await DeviceInfo.GetGlyphThumbnailAsync();
+ BitmapImage glyphBitmapImage = new BitmapImage();
+ await glyphBitmapImage.SetSourceAsync(deviceThumbnail);
+ Glyph = glyphBitmapImage;
+ }
+ catch (Exception)
+ {
+ Glyph = null;
+ }
+ });
+ }
+
+ ///
+ /// Executes when a property is changed
+ ///
+ ///
+ private void OnPropertyChanged(PropertyChangedEventArgs e)
+ {
+ if (PropertyChanged != null)
+ {
+ PropertyChanged(this, e);
+ }
+ }
+
+ ///
+ /// Overrides the ToString function to return the name of the device
+ ///
+ /// Name of this characteristic
+ public override string ToString()
+ {
+ return this.name;
+ }
+
+ ///
+ /// Compares this device to other bluetooth devices by checking the id
+ ///
+ ///
+ /// true for equal
+ bool IEquatable.Equals(ObservableBluetoothLEDevice other)
+ {
+ if (other == null)
+ {
+ return false;
+ }
+
+ if (this.DeviceInfo.Id == other.DeviceInfo.Id)
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ ///
+ /// Updates this device's deviceInformation
+ ///
+ ///
+ public void Update(DeviceInformationUpdate deviceUpdate)
+ {
+ DeviceInfo.Update(deviceUpdate);
+
+ OnPropertyChanged(new PropertyChangedEventArgs("DeviceInfo"));
+ }
+
+ ///
+ /// Helper method which checks to see if the Secure Connection APIs are supported
+ /// and updates the current status.
+ ///
+ private void UpdateSecureConnectionStatus()
+ {
+ if (isSecureConnectionSupported)
+ {
+ IsSecureConnection = bluetoothLEDevice.WasSecureConnectionUsedForPairing;
+ }
+ else
+ {
+ IsSecureConnection = false;
+ }
+ }
+
+ public void Update(BluetoothLEAdvertisementReceivedEventArgs advertisementEvent)
+ {
+ // Update our current RSSI for the device.
+ RSSI = advertisementEvent.RawSignalStrengthInDBm;
+
+ if (advertisementEvent.Advertisement.ServiceUuids != null)
+ {
+ AdvertisementServiceCount = advertisementEvent.Advertisement.ServiceUuids.Count();
+ }
+
+ Advertisements.Add(new ObservableBluetoothLEAdvertisement(advertisementEvent));
+ }
+ }
+}
diff --git a/BluetoothLEExplorer/BluetoothLEExplorer/Models/ObservableGattCharacteristics.cs b/BluetoothLEExplorer/BluetoothLEExplorer/Models/ObservableGattCharacteristics.cs
index aaeaf1e..c0e7c07 100644
--- a/BluetoothLEExplorer/BluetoothLEExplorer/Models/ObservableGattCharacteristics.cs
+++ b/BluetoothLEExplorer/BluetoothLEExplorer/Models/ObservableGattCharacteristics.cs
@@ -320,7 +320,6 @@ public ObservableGattCharacteristics(GattCharacteristic characteristic, Observab
{
Characteristic = characteristic;
Parent = parent;
-
Name = GattCharacteristicUuidHelper.ConvertUuidToName(characteristic.Uuid);
UUID = characteristic.Uuid.ToString();
}
diff --git a/BluetoothLEExplorer/BluetoothLEExplorer/Models/ObservableGattDescriptors.cs b/BluetoothLEExplorer/BluetoothLEExplorer/Models/ObservableGattDescriptors.cs
index 57cec96..da1cad3 100644
--- a/BluetoothLEExplorer/BluetoothLEExplorer/Models/ObservableGattDescriptors.cs
+++ b/BluetoothLEExplorer/BluetoothLEExplorer/Models/ObservableGattDescriptors.cs
@@ -166,13 +166,12 @@ public ObservableGattDescriptors(GattDescriptor descriptor, ObservableGattCharac
{
Descriptor = descriptor;
Parent = parent;
+ Name = GattDescriptorUuidHelper.ConvertUuidToName(descriptor.Uuid);
+ UUID = descriptor.Uuid.ToString();
}
public async Task Initialize()
{
- Name = GattDescriptorUuidHelper.ConvertUuidToName(descriptor.Uuid);
- UUID = descriptor.Uuid.ToString();
-
await ReadValueAsync();
PropertyChanged += ObservableGattDescriptors_PropertyChanged;
}
diff --git a/BluetoothLEExplorer/BluetoothLEExplorer/Models/ObservableGattDeviceService.cs b/BluetoothLEExplorer/BluetoothLEExplorer/Models/ObservableGattDeviceService.cs
index 400beae..f88e6db 100644
--- a/BluetoothLEExplorer/BluetoothLEExplorer/Models/ObservableGattDeviceService.cs
+++ b/BluetoothLEExplorer/BluetoothLEExplorer/Models/ObservableGattDeviceService.cs
@@ -160,6 +160,25 @@ public string UUID
}
}
+ private ushort attributeHandle;
+
+ public ushort AttributeHandle
+ {
+ get
+ {
+ return attributeHandle;
+ }
+
+ set
+ {
+ if (attributeHandle != value)
+ {
+ attributeHandle = value;
+ OnPropertyChanged(new PropertyChangedEventArgs("AttributeHandle"));
+ }
+ }
+ }
+
///
/// Determines if the SelectedCharacteristic_PropertyChanged has been added
///
@@ -178,6 +197,28 @@ public ObservableGattDeviceService(GattDeviceService service)
public async Task Initialize()
{
+ await GetAllServiceAttributes();
+ }
+
+ private async Task GetAllServiceAttributes()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.Append("ObservableGattDeviceService::GetAllServiceAttributes: ");
+ sb.Append(Name);
+
+ // Request the necessary access permissions for the service and abort
+ // if permissions are denied.
+ GattOpenStatus status = await Service.OpenAsync(GattSharingMode.SharedReadAndWrite);
+ if (status != GattOpenStatus.Success && status != GattOpenStatus.AlreadyOpened)
+ {
+ string error = " - Error: " + status.ToString();
+ Name += error;
+ sb.Append(error);
+ Debug.WriteLine(sb.ToString());
+ return;
+ }
+
+ await GetAllIncludedServices();
await GetAllCharacteristics();
}
@@ -213,7 +254,7 @@ public async Task TurnOnAllNotifications()
{
if (gattchar.Characteristic.CharacteristicProperties.HasFlag(GattCharacteristicProperties.Notify))
{
- if(!await gattchar.SetNotify())
+ if (!await gattchar.SetNotify())
{
success = false;
}
@@ -232,7 +273,7 @@ public async Task TurnOffAllNotifications()
{
if (gattchar.Characteristic.CharacteristicProperties.HasFlag(GattCharacteristicProperties.Notify))
{
- if(!await gattchar.StopNotify())
+ if (!await gattchar.StopNotify())
{
success = false;
}
@@ -341,6 +382,41 @@ public bool IsIndicateSet()
return true;
}
+ private async Task GetAllIncludedServices()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.Append("ObservableGattDeviceService::getAllIncludedServices: ");
+ sb.Append(name);
+
+ try
+ {
+ var result = await service.GetIncludedServicesAsync(Services.SettingsServices.SettingsService.Instance.UseCaching ? BluetoothCacheMode.Cached : BluetoothCacheMode.Uncached);
+ if (result.Status == GattCommunicationStatus.Success)
+ {
+ sb.Append(" - getAllIncludedServices found ");
+ sb.Append(result.Services.Count());
+ sb.Append(" services");
+ Debug.WriteLine(sb);
+ }
+ else if (result.Status == GattCommunicationStatus.Unreachable)
+ {
+ sb.Append(" - getAllIncludedServices failed with Unreachable");
+ Debug.WriteLine(sb.ToString());
+ }
+ else if (result.Status == GattCommunicationStatus.ProtocolError)
+ {
+ sb.Append(" - getAllIncludedServices failed with Unreachable");
+ Debug.WriteLine(sb.ToString());
+ }
+
+ }
+ catch (Exception ex)
+ {
+ Debug.WriteLine("getAllIncludedServices: Exception - {0}" + ex.Message);
+ Name += " - Exception: " + ex.Message;
+ }
+ }
+
///
/// Gets all the characteristics of this service
///
@@ -362,11 +438,10 @@ private async Task GetAllCharacteristics()
Name += error;
sb.Append(error);
Debug.WriteLine(sb.ToString());
-
return;
}
- GattCharacteristicsResult result = await service.GetCharacteristicsAsync(Services.SettingsServices.SettingsService.Instance.UseCaching ? BluetoothCacheMode.Cached : BluetoothCacheMode.Uncached);
+ var result = await service.GetCharacteristicsAsync(Services.SettingsServices.SettingsService.Instance.UseCaching ? BluetoothCacheMode.Cached : BluetoothCacheMode.Uncached);
if (result.Status == GattCommunicationStatus.Success)
{
diff --git a/BluetoothLEExplorer/BluetoothLEExplorer/Package.appxmanifest b/BluetoothLEExplorer/BluetoothLEExplorer/Package.appxmanifest
index 3f8bfc5..d5dd1ca 100644
--- a/BluetoothLEExplorer/BluetoothLEExplorer/Package.appxmanifest
+++ b/BluetoothLEExplorer/BluetoothLEExplorer/Package.appxmanifest
@@ -1,6 +1,6 @@
-
+
Bluetooth LE Explorer
diff --git a/BluetoothLEExplorer/BluetoothLEExplorer/Services/AdvertisementService/AdvertisementDataTypeService.cs b/BluetoothLEExplorer/BluetoothLEExplorer/Services/AdvertisementService/AdvertisementDataTypeService.cs
new file mode 100644
index 0000000..54c201f
--- /dev/null
+++ b/BluetoothLEExplorer/BluetoothLEExplorer/Services/AdvertisementService/AdvertisementDataTypeService.cs
@@ -0,0 +1,59 @@
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+//
+//----------------------------------------------------------------------------------------------
+using System;
+using Windows.Devices.Bluetooth;
+using Windows.Devices.Bluetooth.Advertisement;
+
+namespace BluetoothLEExplorer.Services.AdvertisementHelpers
+{
+ enum AdvertisementSectionType : byte
+ {
+ Flags = 0x01,
+ IncompleteService16BitUuids = 0x02,
+ CompleteService16BitUuids = 0x03,
+ IncompleteService32BitUuids = 0x04,
+ CompleteService32BitUuids = 0x05,
+ IncompleteService128BitUuids = 0x06,
+ CompleteService128BitUuids = 0x07,
+ ShortenedLocalName = 0x08,
+ CompleteLocalName = 0x09,
+ TxPowerLevel = 0x0A,
+ ClassOfDevice = 0x0D,
+ SimplePairingHashC192 = 0x0E,
+ SimplePairingRandomizerR192 = 0x0F,
+ SecurityManagerTKValues = 0x10,
+ SecurityManagerOutOfBandFlags = 0x11,
+ SlaveConnectionIntervalRange = 0x12,
+ ServiceSolicitation16BitUuids = 0x14,
+ ServiceSolicitation32BitUuids = 0x1F,
+ ServiceSolicitation128BitUuids = 0x15,
+ ServiceData16BitUuids = 0x16,
+ ServiceData32BitUuids = 0x20,
+ ServiceData128BitUuids = 0x21,
+ PublicTargetAddress = 0x17,
+ RandomTargetAddress = 0x18,
+ Appearance = 0x19,
+ AdvertisingInterval = 0x1A,
+ LEBluetoothDeviceAddress = 0x1B,
+ LERole = 0x1C,
+ SimplePairingHashC256 = 0x1D,
+ SimplePairingRandomizerR256 = 0x1E,
+ ThreeDimensionInformationData = 0x3D,
+ ManufacturerSpecificData = 0xFF,
+ }
+
+ public static class AdvertisementDataTypeHelper
+ {
+ public static string ConvertSectionTypeToString(byte sectionType)
+ {
+ AdvertisementSectionType convertedSectionType;
+ if (Enum.TryParse(sectionType.ToString(), out convertedSectionType))
+ {
+ return convertedSectionType.ToString();
+ }
+ return sectionType.ToString("X2");
+ }
+ }
+}
diff --git a/BluetoothLEExplorer/BluetoothLEExplorer/Services/Converters/Converters.cs b/BluetoothLEExplorer/BluetoothLEExplorer/Services/Converters/Converters.cs
index be4b480..83fad9b 100644
--- a/BluetoothLEExplorer/BluetoothLEExplorer/Services/Converters/Converters.cs
+++ b/BluetoothLEExplorer/BluetoothLEExplorer/Services/Converters/Converters.cs
@@ -8,6 +8,10 @@
using BluetoothLEExplorer.ViewModels;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Data;
+using GattHelper.Converters;
+using Windows.Networking.BackgroundTransfer;
+using System.Runtime.InteropServices.WindowsRuntime;
+using Windows.Storage.Streams;
[module: System.Diagnostics.CodeAnalysis.SuppressMessage(
"StyleCop.CSharp.DocumentationRules",
@@ -21,6 +25,101 @@
namespace BluetoothLEExplorer.Services.Converters
{
+ class ByteArrayToStringConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, string culture)
+ {
+ var byteArray = value as byte[];
+ if (byteArray != null)
+ {
+ return GattConvert.ToHexString(byteArray.AsBuffer());
+ }
+ return "";
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, string culture)
+ {
+ var byteString = value as String;
+ if (byteString != null)
+ {
+ var data = GattConvert.ToIBufferFromHexString(byteString);
+
+ return data.ToArray();
+ }
+ return null;
+ }
+ }
+
+ public class ScanningModeToBooleanConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, string culture)
+ {
+ Debug.WriteLine($"Convert: value = {value.ToString()}, {parameter.ToString()}");
+ return value.ToString().Equals(parameter);
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, string culture)
+ {
+ Debug.Write($"ConvertBack: {value.ToString()}, ");
+ AdvertisementMonitorPageViewModel.MonitorScanningMode type = AdvertisementMonitorPageViewModel.MonitorScanningMode.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;
+ }
+ }
+ }
+
+ public class DataFormatTypeToBooleanConverter : IValueConverter
+ {
+ ///
+ /// Display type radio button converter
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// return value of display type radio button converter
+ public object Convert(object value, Type targetType, object parameter, string culture)
+ {
+ Debug.WriteLine($"Convert: value = {value.ToString()}, {parameter.ToString()}");
+ return value.ToString().Equals(parameter);
+ }
+
+ ///
+ /// Display type radio button back converter
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// Return value of display type radio button back converter
+ public object ConvertBack(object value, Type targetType, object parameter, string culture)
+ {
+ Debug.Write($"ConvertBack: {value.ToString()}, ");
+ ObservableBluetoothLEAdvertisementFilter.DataFormatType type = ObservableBluetoothLEAdvertisementFilter.DataFormatType.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;
+ }
+ }
+ }
+
///
/// Converter to change to a boolean.
/// This is used in to determine if a radio button is checked or not
@@ -109,6 +208,19 @@ public object ConvertBack(object value, Type targetType, object parameter, strin
}
}
+ public class StringFormatConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, string language)
+ {
+ return string.Format(parameter as string, value);
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, string language)
+ {
+ return null;
+ }
+ }
+
///
/// Uses a boolean to determine if text should be crossed out
///
diff --git a/BluetoothLEExplorer/BluetoothLEExplorer/Services/GattUuidsService/GattUuidsService.cs b/BluetoothLEExplorer/BluetoothLEExplorer/Services/GattUuidsService/GattUuidsService.cs
index 5c33f80..f3ffb24 100644
--- a/BluetoothLEExplorer/BluetoothLEExplorer/Services/GattUuidsService/GattUuidsService.cs
+++ b/BluetoothLEExplorer/BluetoothLEExplorer/Services/GattUuidsService/GattUuidsService.cs
@@ -42,7 +42,7 @@ public static bool IsReserved(Guid uuid)
{
return true;
}
-
+
return false;
}
}
diff --git a/BluetoothLEExplorer/BluetoothLEExplorer/ViewModels/AdvertisementBeaconDetailsPageViewModel.cs b/BluetoothLEExplorer/BluetoothLEExplorer/ViewModels/AdvertisementBeaconDetailsPageViewModel.cs
new file mode 100644
index 0000000..5d401c4
--- /dev/null
+++ b/BluetoothLEExplorer/BluetoothLEExplorer/ViewModels/AdvertisementBeaconDetailsPageViewModel.cs
@@ -0,0 +1,32 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Collections.ObjectModel;
+
+using Template10.Mvvm;
+using Template10.Services.NavigationService;
+
+using Windows.UI.Xaml.Navigation;
+using Windows.Devices.Bluetooth.Advertisement;
+
+using BluetoothLEExplorer.Models;
+using BluetoothLEExplorer.Services.AdvertisementHelpers;
+
+
+namespace BluetoothLEExplorer.ViewModels
+{
+ public class AdvertisementBeaconDetailsPageViewModel : ViewModelBase
+ {
+ public void GotoSettings() => NavigationService.Navigate(typeof(Views.SettingsPage), 0);
+
+ ///
+ /// Go to privacy
+ ///
+ public void GotoPrivacy() => NavigationService.Navigate(typeof(Views.SettingsPage), 1);
+
+ ///
+ /// Go to about
+ ///
+ public void GotoAbout() => NavigationService.Navigate(typeof(Views.SettingsPage), 2);
+ }
+}
diff --git a/BluetoothLEExplorer/BluetoothLEExplorer/ViewModels/AdvertisementBeaconPageViewModel.cs b/BluetoothLEExplorer/BluetoothLEExplorer/ViewModels/AdvertisementBeaconPageViewModel.cs
new file mode 100644
index 0000000..be647c0
--- /dev/null
+++ b/BluetoothLEExplorer/BluetoothLEExplorer/ViewModels/AdvertisementBeaconPageViewModel.cs
@@ -0,0 +1,252 @@
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+//
+//----------------------------------------------------------------------------------------------
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Collections.ObjectModel;
+
+using Template10.Mvvm;
+using Template10.Services.NavigationService;
+
+using Windows.UI.Xaml.Navigation;
+using Windows.Devices.Bluetooth.Advertisement;
+
+using BluetoothLEExplorer.Models;
+using BluetoothLEExplorer.Services.AdvertisementHelpers;
+using System.Windows.Input;
+using GattHelper.Converters;
+using System.Runtime.InteropServices.WindowsRuntime;
+using System;
+using Windows.Foundation.Metadata;
+
+namespace BluetoothLEExplorer.ViewModels
+{
+ public class AdvertisementBeaconPageNewBeaconViewModel : ViewModelBase
+ {
+ private bool useExtendedFormat = false;
+
+ public bool UseExtendedFormat
+ {
+ get
+ {
+ return useExtendedFormat;
+ }
+
+ set
+ {
+ if (value != useExtendedFormat)
+ {
+ Set(ref useExtendedFormat, value, "UseExtendedFormat");
+ IsValid = AreParametersValid();
+ }
+ }
+ }
+
+ private bool isAnonymous = false;
+
+ public bool IsAnonymous
+ {
+ get
+ {
+ return isAnonymous;
+ }
+
+ set
+ {
+ if (value != isAnonymous)
+ {
+ Set(ref isAnonymous, value, "IsAnonymous");
+ IsValid = AreParametersValid();
+ }
+ }
+ }
+
+ private bool includeTxPower = false;
+
+ public bool IncludeTxPower
+ {
+ get
+ {
+ return includeTxPower;
+ }
+
+ set
+ {
+ if (value != includeTxPower)
+ {
+ Set(ref includeTxPower, value, "IncludeTxPower");
+ IsValid = AreParametersValid();
+ }
+ }
+ }
+
+ private String preferredTxPower = "";
+
+ public String PreferredTxPower
+ {
+ get
+ {
+ return preferredTxPower;
+ }
+
+ set
+ {
+ if (value != preferredTxPower)
+ {
+ Set(ref preferredTxPower, value, "PreferredTxPower");
+ IsValid = AreParametersValid();
+ }
+ }
+ }
+
+ private String payload = "";
+
+ public String Payload
+ {
+ get
+ {
+ return payload;
+ }
+
+ set
+ {
+ if (value != payload)
+ {
+ Set(ref payload, value, "Payload");
+ IsValid = AreParametersValid();
+ }
+ }
+ }
+
+ private bool isValid = false;
+
+ public bool IsValid
+ {
+ get
+ {
+ return isValid;
+ }
+
+ private set
+ {
+ if (value != isValid)
+ {
+ Set(ref isValid, value, "IsValid");
+ }
+ }
+ }
+
+ public void Reset()
+ {
+ UseExtendedFormat = false;
+ IsAnonymous = false;
+ IncludeTxPower = false;
+ PreferredTxPower = "";
+ Payload = "";
+ }
+
+ public bool AreParametersValid()
+ {
+ if (payload == "")
+ {
+ return false;
+ }
+ else if (isAnonymous || includeTxPower)
+ {
+ if (!useExtendedFormat)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ ///
+ /// Initializes a new instance of the class. Currently unused.
+ ///
+ public class AdvertisementBeaconPageViewModel : ViewModelBase
+ {
+ private GattSampleContext Context;
+
+ public ICommand CreateBeaconCommand
+ {
+ get
+ {
+ return new DelegateCommand((x) => { if (x != null) { CreateBeacon(x); } });
+ }
+ }
+
+ public ObservableCollection Beacons
+ {
+ get { return Context.Beacons; }
+ private set { Context.Beacons = value; }
+ }
+
+ public ObservableBluetoothLEBeacon SelectedBeacon
+ {
+ get { return Context.SelectedBeacon; }
+
+ set
+ {
+ if (Context.SelectedBeacon != value)
+ {
+ Context.SelectedBeacon = value;
+ if (Context.SelectedBeacon != null)
+ {
+ NavigateToBeacon();
+ }
+ }
+ }
+ }
+
+ public AdvertisementBeaconPageViewModel()
+ {
+ Context = GattSampleContext.Context;
+ Context.PropertyChanged += Context_PropertyChanged;
+ }
+
+ private void Context_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
+ {
+ }
+
+ public void CreateBeacon(AdvertisementBeaconPageNewBeaconViewModel parameters)
+ {
+ short? preferredTxPower = null;
+ if (parameters.PreferredTxPower != "")
+ {
+ preferredTxPower = Convert.ToInt16(parameters.PreferredTxPower);
+ }
+
+ var beacon = new ObservableBluetoothLEBeacon(
+ GattConvert.ToIBufferFromHexString(parameters.Payload).ToArray(),
+ parameters.UseExtendedFormat,
+ parameters.IsAnonymous,
+ parameters.IncludeTxPower,
+ preferredTxPower);
+ Beacons.Add(beacon);
+
+ // Reset the parameters after creation.
+ parameters.Reset();
+ }
+
+ public void NavigateToBeacon() => NavigationService.Navigate(typeof(Views.AdvertisementBeaconDetailsPage));
+
+ ///
+ /// Go to settings
+ ///
+ public void GotoSettings() => NavigationService.Navigate(typeof(Views.SettingsPage), 0);
+
+ ///
+ /// Go to privacy
+ ///
+ public void GotoPrivacy() => NavigationService.Navigate(typeof(Views.SettingsPage), 1);
+
+ ///
+ /// Go to about
+ ///
+ public void GotoAbout() => NavigationService.Navigate(typeof(Views.SettingsPage), 2);
+ }
+}
diff --git a/BluetoothLEExplorer/BluetoothLEExplorer/ViewModels/AdvertisementMonitorPageViewModel.cs b/BluetoothLEExplorer/BluetoothLEExplorer/ViewModels/AdvertisementMonitorPageViewModel.cs
new file mode 100644
index 0000000..df3812a
--- /dev/null
+++ b/BluetoothLEExplorer/BluetoothLEExplorer/ViewModels/AdvertisementMonitorPageViewModel.cs
@@ -0,0 +1,310 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using BluetoothLEExplorer.Models;
+using Template10.Mvvm;
+using Template10.Services.NavigationService;
+using Windows.UI.Xaml.Navigation;
+using Windows.Devices.Bluetooth.GenericAttributeProfile;
+using Windows.Devices.Bluetooth.Advertisement;
+using Windows.Security.Cryptography;
+using Windows.Storage.Streams;
+using Windows.UI.Popups;
+using SortedObservableCollection;
+using GattHelper.Converters;
+using Windows.Foundation.Metadata;
+using BluetoothLEExplorer.Services.AdvertisementHelpers;
+using Windows.UI.Xaml.Data;
+using Template10.Common;
+
+namespace BluetoothLEExplorer.ViewModels
+{
+ public class AdvertisementMonitorPageViewModel : ViewModelBase
+ {
+ public enum MonitorScanningMode
+ {
+ NotSet,
+ Passive,
+ Active,
+ None
+ }
+
+ private MonitorScanningMode scanningMode = MonitorScanningMode.Passive;
+
+ public MonitorScanningMode ScanningMode
+ {
+ get
+ {
+ return scanningMode;
+ }
+
+ set
+ {
+ Set(ref scanningMode, value);
+ }
+ }
+
+ ///
+ /// App context
+ ///
+ private GattSampleContext Context;
+
+ public bool IsWatcherStarted
+ {
+ get
+ {
+ return Context.AdvertisementWatcherStarted;
+ }
+ }
+
+ public List KnownDataSectionFilters { get; private set; } = new List();
+
+ private ObservableBluetoothLEAdvertisementFilter selectedDataSectionFilter = null;
+
+ public ObservableBluetoothLEAdvertisementFilter SelectedDataSectionFilter
+ {
+ get
+ {
+ return selectedDataSectionFilter;
+ }
+
+ set
+ {
+ ShowDataSectionRawPane = value.DataSectionRawFilter;
+
+ Set(ref selectedDataSectionFilter, value);
+ }
+ }
+
+ private bool showDataSectionRawPane = false;
+
+ public bool ShowDataSectionRawPane
+ {
+ get
+ {
+ return showDataSectionRawPane;
+ }
+
+ private set
+ {
+ Set(ref showDataSectionRawPane, value);
+ }
+ }
+
+ private object advertisementsLock = new object();
+
+ public SortedObservableCollection AdvertisementsView { get; private set; } = new SortedObservableCollection(new ObservableBluetoothLEAdvertisement.RssiComparer(), "Rssi");
+
+ private ObservableBluetoothLEAdvertisement selectedAdvertisement;
+
+ public ObservableBluetoothLEAdvertisement SelectedAdvertisement
+ {
+ get
+ {
+ return selectedAdvertisement;
+ }
+
+ set
+ {
+ bool same = (selectedAdvertisement == value);
+
+ Set(ref selectedAdvertisement, value, "SelectedAdvertisement");
+ Context.SelectedAdvertisement = selectedAdvertisement;
+
+ if ((same == false) && (value != null))
+ {
+ ViewAdvertisement();
+ }
+ }
+ }
+
+ public String ContentFilter
+ {
+ get
+ {
+ return Context.AdvertisementContentFilter;
+ }
+
+ set
+ {
+ if (Context.AdvertisementContentFilter != value)
+ {
+ Context.AdvertisementContentFilter = value;
+ UpdateAdvertisementView();
+ RaisePropertyChanged("ContentFilter");
+ }
+ }
+ }
+
+ public AdvertisementMonitorPageViewModel()
+ {
+ Context = GattSampleContext.Context;
+ Context.PropertyChanged += Context_PropertyChanged;
+
+ foreach (var value in Enum.GetValues(typeof(AdvertisementSectionType)))
+ {
+ KnownDataSectionFilters.Add(new ObservableBluetoothLEAdvertisementFilter((byte)value));
+ }
+ }
+
+ private void Context_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == "AdvertisementWatcherStarted")
+ {
+ RaisePropertyChanged("IsWatcherStarted");
+ }
+ }
+
+ public override async Task OnNavigatedToAsync(object parameter, NavigationMode mode, IDictionary suspensionState)
+ {
+ Context.Advertisements.MapChanged += Advertisements_MapChanged;
+ await Task.CompletedTask;
+ }
+
+ ///
+ /// Navigate from page
+ ///
+ ///
+ ///
+ /// Navigate from task
+ public override async Task OnNavigatedFromAsync(IDictionary suspensionState, bool suspending)
+ {
+ Context.Advertisements.MapChanged -= Advertisements_MapChanged;
+ await Task.CompletedTask;
+ }
+
+ public override async Task OnNavigatingFromAsync(NavigatingEventArgs args)
+ {
+ args.Cancel = false;
+ Context.StopAdvertisementWatcher();
+ await Task.CompletedTask;
+ }
+
+ public async void ToggleWatcher()
+ {
+ await Dispatcher.DispatchAsync(() =>
+ {
+ if (IsWatcherStarted == false)
+ {
+ BluetoothLEScanningMode convertedScanningMode = BluetoothLEScanningMode.Passive;
+ if (scanningMode == MonitorScanningMode.Active)
+ {
+ convertedScanningMode = BluetoothLEScanningMode.Active;
+ }
+ else if ((scanningMode == MonitorScanningMode.None) &&
+ ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 10))
+ {
+ convertedScanningMode = BluetoothLEScanningMode.None;
+ }
+
+ Context.StartAdvertisementWatcher(convertedScanningMode);
+ }
+ else
+ {
+ Context.StopAdvertisementWatcher();
+ }
+ });
+ }
+
+ public void ApplyFilter()
+ {
+ var filter = new BluetoothLEAdvertisementFilter();
+ if (SelectedDataSectionFilter != null)
+ {
+ filter.BytePatterns.Add(SelectedDataSectionFilter.GetBytePattern());
+ }
+ Context.UpdateAdvertisementFilter(filter);
+ }
+
+ public void ClearFilter()
+ {
+ Context.UpdateAdvertisementFilter(new BluetoothLEAdvertisementFilter());
+ }
+
+ private void Advertisements_MapChanged(Windows.Foundation.Collections.IObservableMap sender, Windows.Foundation.Collections.IMapChangedEventArgs @event)
+ {
+ lock (advertisementsLock)
+ {
+ if ((@event.CollectionChange == Windows.Foundation.Collections.CollectionChange.ItemInserted) ||
+ (@event.CollectionChange == Windows.Foundation.Collections.CollectionChange.ItemChanged))
+ {
+ var advertisement = sender[@event.Key];
+
+ if (IsContentFilterMatch(Context.AdvertisementContentFilter, advertisement))
+ {
+ if (!AdvertisementsView.Contains(advertisement))
+ {
+ AdvertisementsView.Add(advertisement);
+ }
+ else
+ {
+ AdvertisementsView[AdvertisementsView.IndexOf(advertisement)].Update(advertisement);
+ }
+ }
+ }
+ else if (@event.CollectionChange == Windows.Foundation.Collections.CollectionChange.Reset)
+ {
+ AdvertisementsView.Clear();
+ }
+ else if (@event.CollectionChange == Windows.Foundation.Collections.CollectionChange.ItemRemoved)
+ {
+ foreach (var advertisement in AdvertisementsView)
+ {
+ if (advertisement.InternalHashString == @event.Key)
+ {
+ AdvertisementsView.Remove(advertisement);
+ }
+ }
+ }
+ }
+ }
+
+ private bool IsContentFilterMatch(String filter, ObservableBluetoothLEAdvertisement advertisement)
+ {
+ if (filter == "")
+ {
+ return true;
+ }
+ else if ((advertisement.AddressAsString.IndexOf(filter, StringComparison.OrdinalIgnoreCase) >= 0) ||
+ (advertisement.PayloadAsString.IndexOf(filter, StringComparison.OrdinalIgnoreCase) >= 0))
+ {
+ return true;
+ }
+ return false;
+ }
+
+ private void UpdateAdvertisementView()
+ {
+ AdvertisementsView = new SortedObservableCollection(new ObservableBluetoothLEAdvertisement.RssiComparer(), "Rssi");
+
+ foreach (var advertisement in Context.Advertisements)
+ {
+ if (IsContentFilterMatch(ContentFilter, advertisement.Value))
+ {
+ AdvertisementsView.Add(advertisement.Value);
+ }
+ }
+
+ RaisePropertyChanged("AdvertisementsView");
+ }
+
+ public void ViewAdvertisement() => NavigationService.Navigate(typeof(Views.AdvertisementPage));
+
+ ///
+ /// Go to settings
+ ///
+ public void GotoSettings() => NavigationService.Navigate(typeof(Views.SettingsPage), 0);
+
+ ///
+ /// Go to privacy
+ ///
+ public void GotoPrivacy() => NavigationService.Navigate(typeof(Views.SettingsPage), 1);
+
+ ///
+ /// Go to about
+ ///
+ public void GotoAbout() => NavigationService.Navigate(typeof(Views.SettingsPage), 2);
+ }
+}
diff --git a/BluetoothLEExplorer/BluetoothLEExplorer/ViewModels/AdvertisementPageViewModel.cs b/BluetoothLEExplorer/BluetoothLEExplorer/ViewModels/AdvertisementPageViewModel.cs
new file mode 100644
index 0000000..089bf60
--- /dev/null
+++ b/BluetoothLEExplorer/BluetoothLEExplorer/ViewModels/AdvertisementPageViewModel.cs
@@ -0,0 +1,45 @@
+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
+{
+ public class AdvertisementPageViewModel : ViewModelBase
+ {
+ private GattSampleContext context = GattSampleContext.Context;
+
+ ///
+ /// Source for
+ ///
+ private ObservableBluetoothLEAdvertisement advertisement = GattSampleContext.Context.SelectedAdvertisement;
+
+ ///
+ /// Gets or sets the advertisement that this view model wraps
+ ///
+ public ObservableBluetoothLEAdvertisement Advertisement
+ {
+ get
+ {
+ return advertisement;
+ }
+
+ set
+ {
+ Set(ref advertisement, value, "Advertisement");
+ }
+ }
+
+
+ }
+}
diff --git a/BluetoothLEExplorer/BluetoothLEExplorer/ViewModels/BeaconViewModel.cs b/BluetoothLEExplorer/BluetoothLEExplorer/ViewModels/BeaconViewModel.cs
deleted file mode 100644
index 0a2c713..0000000
--- a/BluetoothLEExplorer/BluetoothLEExplorer/ViewModels/BeaconViewModel.cs
+++ /dev/null
@@ -1,89 +0,0 @@
-//
-// Copyright (c) Microsoft Corporation. All rights reserved.
-//
-//----------------------------------------------------------------------------------------------
-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
-{
- ///
- /// Initializes a new instance of the class. Currently unused.
- ///
- public class BeaconViewModel : ViewModelBase
- {
- ///
- /// Initializes a new instance of the class.
- ///
- public BeaconViewModel()
- {
- if (Windows.ApplicationModel.DesignMode.DesignModeEnabled)
- {
- }
- }
-
- ///
- /// Navigate to page
- ///
- ///
- ///
- ///
- /// Navigate to task
- public override async Task OnNavigatedToAsync(object parameter, NavigationMode mode, IDictionary suspensionState)
- {
- if (suspensionState.Any())
- {
- }
-
- await Task.CompletedTask;
- }
-
- ///
- /// Navigate from page
- ///
- ///
- ///
- /// Navigate from task
- public override async Task OnNavigatedFromAsync(IDictionary suspensionState, bool suspending)
- {
- if (suspending)
- {
- }
-
- await Task.CompletedTask;
- }
-
- ///
- /// Navigate from page
- ///
- ///
- /// Navigate from task
- public override async Task OnNavigatingFromAsync(NavigatingEventArgs args)
- {
- args.Cancel = false;
- await Task.CompletedTask;
- }
-
- ///
- /// Go to settings
- ///
- public void GotoSettings() =>
- NavigationService.Navigate(typeof(Views.SettingsPage), 0);
-
- ///
- /// Go to privacy
- ///
- public void GotoPrivacy() =>
- NavigationService.Navigate(typeof(Views.SettingsPage), 1);
-
- ///
- /// Go to about
- ///
- public void GotoAbout() =>
- NavigationService.Navigate(typeof(Views.SettingsPage), 2);
- }
-}
diff --git a/BluetoothLEExplorer/BluetoothLEExplorer/ViewModels/CharacteristicPageViewModel.cs b/BluetoothLEExplorer/BluetoothLEExplorer/ViewModels/CharacteristicPageViewModel.cs
index 89ac87f..e47f577 100644
--- a/BluetoothLEExplorer/BluetoothLEExplorer/ViewModels/CharacteristicPageViewModel.cs
+++ b/BluetoothLEExplorer/BluetoothLEExplorer/ViewModels/CharacteristicPageViewModel.cs
@@ -448,6 +448,14 @@ private set
}
}
+ public bool IsTransactionInProgress
+ {
+ get
+ {
+ return context.IsTransactionInProgress;
+ }
+ }
+
///
/// Initializes a new instance of the class.
///
@@ -467,6 +475,16 @@ public CharacteristicPageViewModel()
{
DisplayPresentError = Windows.UI.Xaml.Visibility.Visible;
}
+
+ context.PropertyChanged += Context_PropertyChanged;
+ }
+
+ private void Context_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == "IsTransactionInProgress")
+ {
+ this.RaisePropertyChanged("IsTransactionInProgress");
+ }
}
///
@@ -663,6 +681,55 @@ public async void WriteValue()
}
}
+ public async void WriteTransaction()
+ {
+ 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);
+ }
+
+ context.WriteTransaction(Characteristic.Characteristic, writeBuffer);
+ }
+ else
+ {
+ NotifyUser.Insert(0, "No data to write to device");
+ }
+ }
+
///
/// Navigate to page
///
@@ -695,7 +762,7 @@ await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatch
continue;
}
- if (BluetoothLEExplorer.Services.GattUuidHelpers.GattServiceUuidHelper.IsReadOnly(Characteristic.Characteristic.Service.Uuid) &&
+ if (GattServiceUuidHelper.IsReadOnly(Characteristic.Characteristic.Service.Uuid) &&
(p == GattCharacteristicProperties.Write || p == GattCharacteristicProperties.WriteWithoutResponse))
{
continue;
diff --git a/BluetoothLEExplorer/BluetoothLEExplorer/ViewModels/DeviceServicesPageViewModel.cs b/BluetoothLEExplorer/BluetoothLEExplorer/ViewModels/DeviceServicesPageViewModel.cs
index dbce636..935d992 100644
--- a/BluetoothLEExplorer/BluetoothLEExplorer/ViewModels/DeviceServicesPageViewModel.cs
+++ b/BluetoothLEExplorer/BluetoothLEExplorer/ViewModels/DeviceServicesPageViewModel.cs
@@ -9,6 +9,9 @@
using BluetoothLEExplorer.Views;
using Template10.Mvvm;
using Template10.Services.NavigationService;
+using Windows.Devices.Bluetooth;
+using Windows.Devices.Bluetooth.Advertisement;
+using Windows.Foundation.Metadata;
using Windows.UI.Xaml.Navigation;
namespace BluetoothLEExplorer.ViewModels
@@ -18,6 +21,7 @@ namespace BluetoothLEExplorer.ViewModels
///
public class DeviceServicesPageViewModel : ViewModelBase
{
+
///
/// App context
///
@@ -40,6 +44,14 @@ public bool IsSecureConnectionSupported
}
}
+ public bool IsTransactionInProgress
+ {
+ get
+ {
+ return context.IsTransactionInProgress;
+ }
+ }
+
///
/// Source for
///
@@ -115,6 +127,18 @@ public DeviceServicesPageViewModel()
if (Windows.ApplicationModel.DesignMode.DesignModeEnabled)
{
}
+
+ context.PropertyChanged += Context_PropertyChanged;
+ }
+
+ public void StartTransaction()
+ {
+ context.CreateTransaction();
+ }
+
+ public void CommitTransaction()
+ {
+ context.CommitTransaction();
}
///
@@ -129,8 +153,8 @@ public override async Task OnNavigatedToAsync(object parameter, NavigationMode m
await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(
Windows.UI.Core.CoreDispatcherPriority.Normal,
() =>
- {
- });
+ {
+ });
await Task.CompletedTask;
}
@@ -172,5 +196,13 @@ public async void Refresh()
Views.Busy.SetBusy(false);
}
+
+ private void Context_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == "IsTransactionInProgress")
+ {
+ this.RaisePropertyChanged("IsTransactionInProgress");
+ }
+ }
}
}
diff --git a/BluetoothLEExplorer/BluetoothLEExplorer/ViewModels/DiscoverViewModel.cs b/BluetoothLEExplorer/BluetoothLEExplorer/ViewModels/DiscoverViewModel.cs
index ee51b3c..fe23808 100644
--- a/BluetoothLEExplorer/BluetoothLEExplorer/ViewModels/DiscoverViewModel.cs
+++ b/BluetoothLEExplorer/BluetoothLEExplorer/ViewModels/DiscoverViewModel.cs
@@ -13,6 +13,7 @@
using Template10.Services.NavigationService;
using Windows.UI.Xaml.Navigation;
using SortedObservableCollection;
+using Windows.Devices.Bluetooth.Advertisement;
namespace BluetoothLEExplorer.ViewModels
{
diff --git a/BluetoothLEExplorer/BluetoothLEExplorer/ViewModels/GenericGattServiceViewModel.cs b/BluetoothLEExplorer/BluetoothLEExplorer/ViewModels/GenericGattServiceViewModel.cs
index 4259a85..df5d35a 100644
--- a/BluetoothLEExplorer/BluetoothLEExplorer/ViewModels/GenericGattServiceViewModel.cs
+++ b/BluetoothLEExplorer/BluetoothLEExplorer/ViewModels/GenericGattServiceViewModel.cs
@@ -7,6 +7,7 @@
using GattServicesLibrary;
using Template10.Mvvm;
using Windows.Devices.Bluetooth.GenericAttributeProfile;
+using Windows.Storage.Streams;
namespace BluetoothLEExplorer.ViewModels
{
@@ -78,6 +79,23 @@ public bool IsDiscoverable
}
}
+ public bool IncludeServiceData
+ {
+ get
+ {
+ return service.IncludeServiceData;
+ }
+
+ set
+ {
+ if (value != service.IncludeServiceData)
+ {
+ service.IncludeServiceData = value;
+ RaisePropertyChanged("IncludeServiceData");
+ }
+ }
+ }
+
///
/// Source for property
///
@@ -95,12 +113,11 @@ public bool IsPublishing
set
{
- if(value != isPublishing)
+ if (value != isPublishing)
{
- if(value == true)
+ if (value == true)
{
Start();
-
}
if(value == false)
{
diff --git a/BluetoothLEExplorer/BluetoothLEExplorer/ViewModels/ServicePageViewModel.cs b/BluetoothLEExplorer/BluetoothLEExplorer/ViewModels/ServicePageViewModel.cs
index 4c4fa49..0bcd59d 100644
--- a/BluetoothLEExplorer/BluetoothLEExplorer/ViewModels/ServicePageViewModel.cs
+++ b/BluetoothLEExplorer/BluetoothLEExplorer/ViewModels/ServicePageViewModel.cs
@@ -399,4 +399,4 @@ public override async Task OnNavigatingFromAsync(NavigatingEventArgs args)
await Task.CompletedTask;
}
}
-}
+}
\ No newline at end of file
diff --git a/BluetoothLEExplorer/BluetoothLEExplorer/Views/AdvertisementBeaconDetailsPage.xaml b/BluetoothLEExplorer/BluetoothLEExplorer/Views/AdvertisementBeaconDetailsPage.xaml
new file mode 100644
index 0000000..415ba81
--- /dev/null
+++ b/BluetoothLEExplorer/BluetoothLEExplorer/Views/AdvertisementBeaconDetailsPage.xaml
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
+
+
+ Collapsed
+
+
+ Visible
+
+
+
+
+
+ True
+
+
+ Visible
+
+
+ Collapsed
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/BluetoothLEExplorer/BluetoothLEExplorer/Views/AdvertisementBeaconDetailsPage.xaml.cs b/BluetoothLEExplorer/BluetoothLEExplorer/Views/AdvertisementBeaconDetailsPage.xaml.cs
new file mode 100644
index 0000000..ddec301
--- /dev/null
+++ b/BluetoothLEExplorer/BluetoothLEExplorer/Views/AdvertisementBeaconDetailsPage.xaml.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices.WindowsRuntime;
+using Windows.Foundation;
+using Windows.Foundation.Collections;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Controls.Primitives;
+using Windows.UI.Xaml.Data;
+using Windows.UI.Xaml.Input;
+using Windows.UI.Xaml.Media;
+using Windows.UI.Xaml.Navigation;
+
+// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238
+
+namespace BluetoothLEExplorer.Views
+{
+ ///
+ /// An empty page that can be used on its own or navigated to within a Frame.
+ ///
+ public sealed partial class AdvertisementBeaconDetailsPage : Page
+ {
+ public AdvertisementBeaconDetailsPage()
+ {
+ this.InitializeComponent();
+ }
+ }
+}
diff --git a/BluetoothLEExplorer/BluetoothLEExplorer/Views/AdvertisementBeaconPage.xaml b/BluetoothLEExplorer/BluetoothLEExplorer/Views/AdvertisementBeaconPage.xaml
new file mode 100644
index 0000000..893716f
--- /dev/null
+++ b/BluetoothLEExplorer/BluetoothLEExplorer/Views/AdvertisementBeaconPage.xaml
@@ -0,0 +1,216 @@
+
+
+
+
+
+
+
+
+
+ True
+
+
+ Visible
+
+
+ Collapsed
+
+
+
+
+
+ False
+
+
+ Visible
+
+
+ Collapsed
+
+
+
+
+
+ True
+
+
+ Stop
+
+
+ Start
+
+
+
+
+
+ 0
+
+
+ Collapsed
+
+
+ Visible
+
+
+
+
+
+ -1
+
+
+ Collapsed
+
+
+ Visible
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/BluetoothLEExplorer/BluetoothLEExplorer/Views/AdvertisementBeaconPage.xaml.cs b/BluetoothLEExplorer/BluetoothLEExplorer/Views/AdvertisementBeaconPage.xaml.cs
new file mode 100644
index 0000000..7450dd1
--- /dev/null
+++ b/BluetoothLEExplorer/BluetoothLEExplorer/Views/AdvertisementBeaconPage.xaml.cs
@@ -0,0 +1,52 @@
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+//
+//----------------------------------------------------------------------------------------------
+using Windows.UI.Xaml.Controls;
+using System.Text.RegularExpressions;
+
+namespace BluetoothLEExplorer.Views
+{
+ //public class ValidationTextBox : TextBox
+ //{
+ // public Regex ValidationExpression { get; set; }
+
+ // protected override void OnPreviewTextInput(TextCompositionEventArgs e)
+ // {
+
+
+ // string currentText = this.Text;
+ // string newText = currentText + e.Text;
+ // }
+
+ ///
+ /// An empty page that can be used on its own or navigated to within a Frame.
+ ///
+ public sealed partial class AdvertisementBeaconPage : Page
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AdvertisementBeaconPage()
+ {
+ this.InitializeComponent();
+ }
+
+ public void PreferredTxPower_BeforeTextChanging(TextBox sender, TextBoxBeforeTextChangingEventArgs e)
+ {
+ if (!string.IsNullOrWhiteSpace(e.NewText) && Regex.IsMatch(e.NewText, @"[^0-9\-]+"))
+ {
+ e.Cancel = true;
+ }
+ }
+
+ public void Payload_BeforeTextChanging(TextBox sender, TextBoxBeforeTextChangingEventArgs e)
+ {
+ if (!string.IsNullOrWhiteSpace(e.NewText) &&
+ !Regex.IsMatch(e.NewText, @"^([0-9a-f]{1,2}[\-]{0,1})*$", RegexOptions.IgnoreCase))
+ {
+ e.Cancel = true;
+ }
+ }
+ }
+}
diff --git a/BluetoothLEExplorer/BluetoothLEExplorer/Views/AdvertisementMonitorPage.xaml b/BluetoothLEExplorer/BluetoothLEExplorer/Views/AdvertisementMonitorPage.xaml
new file mode 100644
index 0000000..40b2bed
--- /dev/null
+++ b/BluetoothLEExplorer/BluetoothLEExplorer/Views/AdvertisementMonitorPage.xaml
@@ -0,0 +1,210 @@
+
+
+
+
+
+
+
+
+
+ True
+
+
+ Visible
+
+
+ Collapsed
+
+
+
+
+
+ False
+
+
+ Visible
+
+
+ Collapsed
+
+
+
+
+
+ True
+
+
+ Stop
+
+
+ Start
+
+
+
+
+
+ 0
+
+
+ Collapsed
+
+
+ Visible
+
+
+
+
+
+ -1
+
+
+ Collapsed
+
+
+ Visible
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/BluetoothLEExplorer/BluetoothLEExplorer/Views/AdvertisementMonitorPage.xaml.cs b/BluetoothLEExplorer/BluetoothLEExplorer/Views/AdvertisementMonitorPage.xaml.cs
new file mode 100644
index 0000000..d67d45b
--- /dev/null
+++ b/BluetoothLEExplorer/BluetoothLEExplorer/Views/AdvertisementMonitorPage.xaml.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices.WindowsRuntime;
+using Windows.Foundation;
+using Windows.Foundation.Collections;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Controls.Primitives;
+using Windows.UI.Xaml.Data;
+using Windows.UI.Xaml.Input;
+using Windows.UI.Xaml.Media;
+using Windows.UI.Xaml.Navigation;
+
+// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238
+
+namespace BluetoothLEExplorer.Views
+{
+ ///
+ /// An empty page that can be used on its own or navigated to within a Frame.
+ ///
+ public sealed partial class AdvertisementMonitorPage : Page
+ {
+ public AdvertisementMonitorPage()
+ {
+ this.InitializeComponent();
+ }
+ }
+}
diff --git a/BluetoothLEExplorer/BluetoothLEExplorer/Views/AdvertisementPage.xaml b/BluetoothLEExplorer/BluetoothLEExplorer/Views/AdvertisementPage.xaml
new file mode 100644
index 0000000..ed4df0d
--- /dev/null
+++ b/BluetoothLEExplorer/BluetoothLEExplorer/Views/AdvertisementPage.xaml
@@ -0,0 +1,163 @@
+
+
+
+
+
+
+
+
+
+ Collapsed
+
+
+ Visible
+
+
+
+
+
+ True
+
+
+ Visible
+
+
+ Collapsed
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/BluetoothLEExplorer/BluetoothLEExplorer/Views/AdvertisementPage.xaml.cs b/BluetoothLEExplorer/BluetoothLEExplorer/Views/AdvertisementPage.xaml.cs
new file mode 100644
index 0000000..071801d
--- /dev/null
+++ b/BluetoothLEExplorer/BluetoothLEExplorer/Views/AdvertisementPage.xaml.cs
@@ -0,0 +1,22 @@
+using BluetoothLEExplorer.Models;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Media;
+using Windows.UI.Xaml.Navigation;
+
+namespace BluetoothLEExplorer.Views
+{
+ ///
+ /// An empty page that can be used on its own or navigated to within a Frame.
+ ///
+ public sealed partial class AdvertisementPage : Page
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public AdvertisementPage()
+ {
+ InitializeComponent();
+ NavigationCacheMode = NavigationCacheMode.Disabled;
+ }
+ }
+}
diff --git a/BluetoothLEExplorer/BluetoothLEExplorer/Views/Beacon.xaml b/BluetoothLEExplorer/BluetoothLEExplorer/Views/Beacon.xaml
deleted file mode 100644
index da181fd..0000000
--- a/BluetoothLEExplorer/BluetoothLEExplorer/Views/Beacon.xaml
+++ /dev/null
@@ -1,124 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/BluetoothLEExplorer/BluetoothLEExplorer/Views/Beacon.xaml.cs b/BluetoothLEExplorer/BluetoothLEExplorer/Views/Beacon.xaml.cs
deleted file mode 100644
index 5feb2f0..0000000
--- a/BluetoothLEExplorer/BluetoothLEExplorer/Views/Beacon.xaml.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-//
-// Copyright (c) Microsoft Corporation. All rights reserved.
-//
-//----------------------------------------------------------------------------------------------
-using Windows.UI.Xaml.Controls;
-
-//// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
-
-namespace BluetoothLEExplorer.Views
-{
- ///
- /// An empty page that can be used on its own or navigated to within a Frame.
- ///
- public sealed partial class Beacon : Page
- {
- ///
- /// Initializes a new instance of the class.
- ///
- public Beacon()
- {
- this.InitializeComponent();
- }
- }
-}
diff --git a/BluetoothLEExplorer/BluetoothLEExplorer/Views/CharacteristicPage.xaml b/BluetoothLEExplorer/BluetoothLEExplorer/Views/CharacteristicPage.xaml
index 497558b..f96f40e 100644
--- a/BluetoothLEExplorer/BluetoothLEExplorer/Views/CharacteristicPage.xaml
+++ b/BluetoothLEExplorer/BluetoothLEExplorer/Views/CharacteristicPage.xaml
@@ -231,6 +231,7 @@
+
diff --git a/BluetoothLEExplorer/BluetoothLEExplorer/Views/DeviceServicesPage.xaml b/BluetoothLEExplorer/BluetoothLEExplorer/Views/DeviceServicesPage.xaml
index afff486..395550d 100644
--- a/BluetoothLEExplorer/BluetoothLEExplorer/Views/DeviceServicesPage.xaml
+++ b/BluetoothLEExplorer/BluetoothLEExplorer/Views/DeviceServicesPage.xaml
@@ -8,6 +8,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:BluetoothLEExplorer.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:myconverters="using:BluetoothLEExplorer.Services.Converters"
xmlns:converters="using:Template10.Converters"
xmlns:vm="using:BluetoothLEExplorer.ViewModels" x:Name="ThisPage"
xmlns:models="using:BluetoothLEExplorer.Models"
@@ -39,7 +40,21 @@
+
+
+ False
+
+
+ Visible
+
+
+ Collapsed
+
+
+
+
+
@@ -97,6 +112,10 @@
+
+
+
+
@@ -107,12 +126,11 @@
-
+
+
+
-
-
+
+ IsItemClickEnabled="True"
+ SelectionMode="Single"
+ ItemsSource="{x:Bind Characteristics}"
+ ItemClick="CharacteristicsListView_ItemClick" >
@@ -146,6 +164,8 @@
+
+
diff --git a/BluetoothLEExplorer/BluetoothLEExplorer/Views/DeviceServicesPage.xaml.cs b/BluetoothLEExplorer/BluetoothLEExplorer/Views/DeviceServicesPage.xaml.cs
index 92f3ff9..2c44c60 100644
--- a/BluetoothLEExplorer/BluetoothLEExplorer/Views/DeviceServicesPage.xaml.cs
+++ b/BluetoothLEExplorer/BluetoothLEExplorer/Views/DeviceServicesPage.xaml.cs
@@ -27,7 +27,7 @@ public DeviceServicesPage()
}
///
- /// Updates the view model with the just selected characteristic
+ /// Updates the view model with the just selected characteristic
///
///
///
@@ -36,6 +36,11 @@ private void CharacteristicsListView_ItemClick(object sender, ItemClickEventArgs
ViewModel.SelectedCharacteristic = (ObservableGattCharacteristics)e.ClickedItem;
}
+ ///
+ /// Updates the view model with the just selected service
+ ///
+ ///
+ ///
private void ServicesListView_ItemClick(object sender, ItemClickEventArgs e)
{
ViewModel.SelectedService = (ObservableGattDeviceService)e.ClickedItem;
diff --git a/BluetoothLEExplorer/BluetoothLEExplorer/Views/Discover.xaml b/BluetoothLEExplorer/BluetoothLEExplorer/Views/Discover.xaml
index d4412a1..93b1503 100644
--- a/BluetoothLEExplorer/BluetoothLEExplorer/Views/Discover.xaml
+++ b/BluetoothLEExplorer/BluetoothLEExplorer/Views/Discover.xaml
@@ -116,7 +116,6 @@
-
diff --git a/BluetoothLEExplorer/BluetoothLEExplorer/Views/ServicePage.xaml b/BluetoothLEExplorer/BluetoothLEExplorer/Views/ServicePage.xaml
index d30a22b..896d6b2 100644
--- a/BluetoothLEExplorer/BluetoothLEExplorer/Views/ServicePage.xaml
+++ b/BluetoothLEExplorer/BluetoothLEExplorer/Views/ServicePage.xaml
@@ -1,4 +1,4 @@
-
-
-
+
\ No newline at end of file
diff --git a/BluetoothLEExplorer/BluetoothLEExplorer/Views/ServicePage.xaml.cs b/BluetoothLEExplorer/BluetoothLEExplorer/Views/ServicePage.xaml.cs
index 4803125..5f3fcf0 100644
--- a/BluetoothLEExplorer/BluetoothLEExplorer/Views/ServicePage.xaml.cs
+++ b/BluetoothLEExplorer/BluetoothLEExplorer/Views/ServicePage.xaml.cs
@@ -36,4 +36,4 @@ private void CharacteristicsListView_ItemClick(object sender, ItemClickEventArgs
ViewModel.SelectedCharacteristic = (ObservableGattCharacteristics)e.ClickedItem;
}
}
-}
+}
\ No newline at end of file
diff --git a/BluetoothLEExplorer/BluetoothLEExplorer/Views/Services/AlertNotificationServicePage.xaml b/BluetoothLEExplorer/BluetoothLEExplorer/Views/Services/AlertNotificationServicePage.xaml
index 082f05f..40f5cc2 100644
--- a/BluetoothLEExplorer/BluetoothLEExplorer/Views/Services/AlertNotificationServicePage.xaml
+++ b/BluetoothLEExplorer/BluetoothLEExplorer/Views/Services/AlertNotificationServicePage.xaml
@@ -116,7 +116,12 @@
-
+
+
+
-
-
diff --git a/BluetoothLEExplorer/BluetoothLEExplorer/Views/Services/BloodPressureServicePage.xaml b/BluetoothLEExplorer/BluetoothLEExplorer/Views/Services/BloodPressureServicePage.xaml
index 23e7cca..f33dcf0 100644
--- a/BluetoothLEExplorer/BluetoothLEExplorer/Views/Services/BloodPressureServicePage.xaml
+++ b/BluetoothLEExplorer/BluetoothLEExplorer/Views/Services/BloodPressureServicePage.xaml
@@ -102,7 +102,11 @@
-
+
+
diff --git a/BluetoothLEExplorer/BluetoothLEExplorer/Views/Services/CurrentTimeServicePage.xaml b/BluetoothLEExplorer/BluetoothLEExplorer/Views/Services/CurrentTimeServicePage.xaml
index 0f82333..2523047 100644
--- a/BluetoothLEExplorer/BluetoothLEExplorer/Views/Services/CurrentTimeServicePage.xaml
+++ b/BluetoothLEExplorer/BluetoothLEExplorer/Views/Services/CurrentTimeServicePage.xaml
@@ -89,7 +89,12 @@
-
+
+
+
-
+
+
diff --git a/BluetoothLEExplorer/BluetoothLEExplorer/Views/Services/MicrosoftServicePage.xaml b/BluetoothLEExplorer/BluetoothLEExplorer/Views/Services/MicrosoftServicePage.xaml
index d37665c..94da5c2 100644
--- a/BluetoothLEExplorer/BluetoothLEExplorer/Views/Services/MicrosoftServicePage.xaml
+++ b/BluetoothLEExplorer/BluetoothLEExplorer/Views/Services/MicrosoftServicePage.xaml
@@ -135,7 +135,19 @@
-
+
+
+
+
+
public sealed partial class SettingsPage : Page
{
- ///
- /// Serialization service
- ///
- private Template10.Services.SerializationService.ISerializationService serializationService;
-
///
/// Initializes a new instance of the class.
///
public SettingsPage()
{
InitializeComponent();
- serializationService = Template10.Services.SerializationService.SerializationService.Json;
}
///
@@ -32,8 +26,6 @@ public SettingsPage()
///
protected override void OnNavigatedTo(NavigationEventArgs e)
{
- var index = int.Parse(serializationService.Deserialize(e.Parameter?.ToString()).ToString());
- MyPivot.SelectedIndex = index;
}
}
}
diff --git a/BluetoothLEExplorer/BluetoothLEExplorer/Views/Shell.xaml b/BluetoothLEExplorer/BluetoothLEExplorer/Views/Shell.xaml
index 6b82073..8574b49 100644
--- a/BluetoothLEExplorer/BluetoothLEExplorer/Views/Shell.xaml
+++ b/BluetoothLEExplorer/BluetoothLEExplorer/Views/Shell.xaml
@@ -21,66 +21,68 @@
-
-
-
+
-
-
+
+
-
-
+
+
-
-
+
+
-
+
-
-
+
+
+
+
+
+
+
+
-
+
-
-
+
+
-
diff --git a/BluetoothLEExplorer/BluetoothLEExplorerUnitTests/BluetoothLEExplorerUnitTests.csproj b/BluetoothLEExplorer/BluetoothLEExplorerUnitTests/BluetoothLEExplorerUnitTests.csproj
index 5a87f75..0aece57 100644
--- a/BluetoothLEExplorer/BluetoothLEExplorerUnitTests/BluetoothLEExplorerUnitTests.csproj
+++ b/BluetoothLEExplorer/BluetoothLEExplorerUnitTests/BluetoothLEExplorerUnitTests.csproj
@@ -11,8 +11,8 @@
BluetoothLEExplorerUnitTests
en-US
UAP
- 10.0.17134.0
- 10.0.15063.0
+ 10.0.19041.0
+ 10.0.18362.0
14
512
{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
diff --git a/BluetoothLEExplorer/GattHelper/Converters/GattConvert.cs b/BluetoothLEExplorer/GattHelper/Converters/GattConvert.cs
index ae02e9e..1ffe53a 100644
--- a/BluetoothLEExplorer/GattHelper/Converters/GattConvert.cs
+++ b/BluetoothLEExplorer/GattHelper/Converters/GattConvert.cs
@@ -12,18 +12,25 @@ public static class GattConvert
{
public static IBuffer ToIBufferFromHexString(string data)
{
+ DataWriter writer = new DataWriter();
data = data.Replace("-", "");
- int NumberChars = data.Length;
- byte[] bytes = new byte[NumberChars / 2];
-
- for (int i = 0; i < NumberChars; i += 2)
+ if (data.Length > 0)
{
- bytes[i / 2] = Convert.ToByte(data.Substring(i, 2), 16);
+ if (data.Length % 2 != 0)
+ {
+ data = "0" + data;
+ }
+
+ int NumberChars = data.Length;
+ byte[] bytes = new byte[NumberChars / 2];
+
+ for (int i = 0; i < NumberChars; i += 2)
+ {
+ bytes[i / 2] = Convert.ToByte(data.Substring(i, 2), 16);
+ }
+ writer.WriteBytes(bytes);
}
-
- DataWriter writer = new DataWriter();
- writer.WriteBytes(bytes);
return writer.DetachBuffer();
}
diff --git a/BluetoothLEExplorer/GattHelper/GattHelper.csproj b/BluetoothLEExplorer/GattHelper/GattHelper.csproj
index 5d368a6..40e2e29 100644
--- a/BluetoothLEExplorer/GattHelper/GattHelper.csproj
+++ b/BluetoothLEExplorer/GattHelper/GattHelper.csproj
@@ -11,8 +11,8 @@
GattHelper
en-US
UAP
- 10.0.17134.0
- 10.0.15063.0
+ 10.0.19041.0
+ 10.0.18362.0
14
512
{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
diff --git a/GattServicesLibrary/GattServicesLibrary/GattServicesLibrary.csproj b/GattServicesLibrary/GattServicesLibrary/GattServicesLibrary.csproj
index 733ba26..7f14dc7 100644
--- a/GattServicesLibrary/GattServicesLibrary/GattServicesLibrary.csproj
+++ b/GattServicesLibrary/GattServicesLibrary/GattServicesLibrary.csproj
@@ -11,8 +11,8 @@
GattServicesLibrary
en-US
UAP
- 10.0.17134.0
- 10.0.15063.0
+ 10.0.19041.0
+ 10.0.18362.0
14
512
{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
@@ -143,6 +143,11 @@
GattHelper
+
+
+ 5.3.3
+
+
14.0
diff --git a/GattServicesLibrary/GattServicesLibrary/GenericGattService.cs b/GattServicesLibrary/GattServicesLibrary/GenericGattService.cs
index 4807b7d..99c9039 100644
--- a/GattServicesLibrary/GattServicesLibrary/GenericGattService.cs
+++ b/GattServicesLibrary/GattServicesLibrary/GenericGattService.cs
@@ -10,6 +10,8 @@
using GattServicesLibrary.Helpers;
using Windows.Devices.Bluetooth;
using Windows.Devices.Bluetooth.GenericAttributeProfile;
+using Windows.Foundation.Metadata;
+using Windows.Storage.Streams;
namespace GattServicesLibrary
{
@@ -95,6 +97,7 @@ public abstract string Name
}
private bool isPublishing = false;
+
public bool IsPublishing
{
get
@@ -104,7 +107,7 @@ public bool IsPublishing
private set
{
- if(value != isPublishing)
+ if (value != isPublishing)
{
isPublishing = value;
OnPropertyChanged(new PropertyChangedEventArgs("IsPublishing"));
@@ -112,6 +115,25 @@ private set
}
}
+ private bool isAdvertising = false;
+
+ public bool IsAdvertising
+ {
+ get
+ {
+ return isAdvertising;
+ }
+
+ private set
+ {
+ if(value != isAdvertising)
+ {
+ isAdvertising = value;
+ OnPropertyChanged(new PropertyChangedEventArgs("IsAdvertising"));
+ }
+ }
+ }
+
public bool IsConnectable
{
@@ -147,6 +169,40 @@ public bool IsDiscoverable
}
}
+ private bool includeServiceData;
+
+ public bool IncludeServiceData
+ {
+ get
+ {
+ return includeServiceData;
+ }
+
+ set
+ {
+ if (value != includeServiceData)
+ {
+ includeServiceData = value;
+ OnPropertyChanged(new PropertyChangedEventArgs("IncludeServiceData"));
+ }
+ }
+ }
+
+ private IBuffer serviceData = null;
+
+ public IBuffer ServiceData
+ {
+ get
+ {
+ return serviceData;
+ }
+
+ set
+ {
+ serviceData = value;
+ }
+ }
+
///
/// Internal ServiceProvider
///
@@ -177,9 +233,14 @@ protected set
private void ServiceProvider_AdvertisementStatusChanged(GattServiceProvider sender, GattServiceProviderAdvertisementStatusChangedEventArgs args)
{
- if (args.Status != GattServiceProviderAdvertisementStatus.Started)
+ if ((args.Status == GattServiceProviderAdvertisementStatus.Stopped) ||
+ (args.Status == GattServiceProviderAdvertisementStatus.Aborted))
{
- IsPublishing = false;
+ IsAdvertising = false;
+ }
+ else
+ {
+ IsAdvertising = true;
}
}
@@ -210,6 +271,18 @@ public virtual void Start()
{
try
{
+ if (IncludeServiceData)
+ {
+ if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 10))
+ {
+ ad.ServiceData = serviceData;
+ }
+ else
+ {
+ IncludeServiceData = false;
+ }
+ }
+
ServiceProvider.StartAdvertising(ad);
IsPublishing = true;
OnPropertyChanged(new PropertyChangedEventArgs("IsPublishing"));
diff --git a/GattServicesLibrary/GattServicesLibrary/Services/MicrosoftService.cs b/GattServicesLibrary/GattServicesLibrary/Services/MicrosoftService.cs
index 1421938..7d4ba82 100644
--- a/GattServicesLibrary/GattServicesLibrary/Services/MicrosoftService.cs
+++ b/GattServicesLibrary/GattServicesLibrary/Services/MicrosoftService.cs
@@ -5,10 +5,12 @@
using System;
using System.ComponentModel;
using System.Diagnostics;
+using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
using GattServicesLibrary;
using GattServicesLibrary.Helpers;
using Windows.Devices.Bluetooth.GenericAttributeProfile;
+using Windows.Foundation.Metadata;
using Windows.Storage.Streams;
namespace GattServicesLibrary.Services
@@ -196,6 +198,12 @@ public GenericGattCharacteristic ReadLongCharacteristic
/// Initialization Task
public override async Task Init()
{
+ var serviceData = new byte[] {
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ };
+ ServiceData = WindowsRuntimeBuffer.Create(serviceData, 0, serviceData.Length, serviceData.Length);
+
await CreateServiceProvider(MSFTServiceUuid);
GattLocalCharacteristicResult result = null;
diff --git a/SortedObservableCollection/SortedObservableCollection/SortedObservableCollection.csproj b/SortedObservableCollection/SortedObservableCollection/SortedObservableCollection.csproj
index 1ee229f..0c059f6 100644
--- a/SortedObservableCollection/SortedObservableCollection/SortedObservableCollection.csproj
+++ b/SortedObservableCollection/SortedObservableCollection/SortedObservableCollection.csproj
@@ -11,8 +11,8 @@
SortedObservableCollection
en-US
UAP
- 10.0.17134.0
- 10.0.10586.0
+ 10.0.19041.0
+ 10.0.18362.0
14
512
{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
@@ -111,6 +111,11 @@
+
+
+ 5.3.3
+
+
14.0
diff --git a/SortedObservableCollection/SortedObservableCollection/project.json b/SortedObservableCollection/SortedObservableCollection/project.json
index 3242d45..ec51351 100644
--- a/SortedObservableCollection/SortedObservableCollection/project.json
+++ b/SortedObservableCollection/SortedObservableCollection/project.json
@@ -1,6 +1,6 @@
{
"dependencies": {
- "Microsoft.NETCore.UniversalWindowsPlatform": "5.2.3"
+ "Microsoft.NETCore.UniversalWindowsPlatform": "5.3.3"
},
"frameworks": {
"uap10.0": {}