using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Interop.Plantronics;
namespace My_UC_App
{
// interface for your application's class that can handle log debug tracing...
public interface DebugLogger
{
void DebugPrint(string methodname, string str);
}
// enumeration of Windows messages for your application's form can receive from Spokes...
public enum SpokesWM
{
MOBILE_INCOMING = (int)10001,
MOBILE_ONCALL = (int)10002,
MOBILE_CALLENDED = (int)10003,
MOBILE_CALLERID = (int)10004,
DEVICE_DON = (int)10005,
DEVICE_DOFF = (int)10006,
DEVICE_NEAR = (int)10007,
DEVICE_FAR = (int)10008,
DEVICE_PROXDISABLED = (int)10009,
DEVICE_PROXENABLED = (int)10010,
DEVICE_PROXUNKNOWN = (int)10011,
DEVICE_DOCK = (int)10012,
DEVICE_UNDOCK = (int)10013,
DEVICE_INRANGE = (int)10014,
DEVICE_OUTOFRANGE = (int)10015
}
// LC struct to hold info on device capabilities
public struct SpokesDeviceCaps
{
public bool HasProximity;
public bool HasCallerId;
public bool HasDocking;
public bool HasWearingSensor;
public bool IsWireless;
// Constructor:
public SpokesDeviceCaps(bool HasProximity, bool HasCallerId, bool HasDocking, bool HasWearingSensor, bool IsWireless)
{
this.HasProximity = HasProximity;
this.HasCallerId = HasCallerId;
this.HasDocking = HasDocking;
this.HasWearingSensor = HasWearingSensor;
this.IsWireless = IsWireless;
}
}
// Note: using singleton model to avoid possibility of multiple instantiation
// as specified in: http://msdn.microsoft.com/en-us/library/ff650316.aspx
//
public sealed class Spokes
{
private static volatile Spokes instance;
private static object syncRoot = new Object();
private Spokes() {
if (!isConnected)
isConnected = Connect();
}
~Spokes()
{
if (isConnected)
Disconnect();
}
public static Spokes Instance
{
get
{
if (instance == null)
{
lock (syncRoot)
{
// Instantiate a singleton Spokes object
if (instance == null)
instance = new Spokes();
}
}
return instance;
}
}
#region Spokes interfaces definitions
static ISessionCOMManager m_sessionComManager = null;
static IComSession m_comSession = null;
static IDevice m_activeDevice = null;
static ISessionCOMManagerEvents_Event m_sessionManagerEvents;
static ICOMCallEvents_Event m_sessionEvents;
static IDeviceCOMEvents_Event m_deviceComEvents;
static IDeviceListenerCOMEvents_Event m_deviceListenerEvents;
static IATDCommand m_atdCommand;
static IHostCommandExt m_hostCommandExt;
public static string m_devicename = "";
#endregion
DebugLogger m_debuglog = null;
public SpokesDeviceCaps DeviceCapabilities;
[DllImport("user32.dll")]
public static extern int PostMessage(IntPtr hWnd, int msg, int wParam, int lParam);
[DllImport("user32")]
public static extern int RegisterWindowMessage(string message);
public IntPtr m_hWnd = (IntPtr)0;
public const int HWND_BROADCAST = 0xffff;
public static readonly int WM_SHOWME = RegisterWindowMessage("WM_SHOWME");
bool isConnected = false;
// pass in aLogger (your class responsible for debug logging)
// pass in hWnd (your Windows Form responsible for handling Spokes messages)
public Spokes(DebugLogger aLogger, IntPtr hWnd)
{
m_debuglog = aLogger;
m_hWnd = hWnd;
}
public void SetLogger(DebugLogger aLogger)
{
m_debuglog = aLogger;
}
public void RegisterSpokesHandlerHWnd(IntPtr hWnd)
{
m_hWnd = hWnd;
}
internal bool Connect()
{
DeviceCapabilities =
new SpokesDeviceCaps(false, false, false, false, false); // we don't yet know what the capabilities are
bool success = false;
try
{
////////////////////////////////////////////////////////////////////////////////////////
// create session manager, and attach to session manager events
m_sessionComManager = new SessionComManagerClass();
m_sessionManagerEvents = m_sessionComManager as ISessionCOMManagerEvents_Event;
if (m_sessionManagerEvents != null)
{
m_sessionManagerEvents.CallStateChanged += m_sessionComManager_CallStateChanged;
m_sessionManagerEvents.DeviceStateChanged += m_sessionComManager_DeviceStateChanged;
}
else
success = false;
////////////////////////////////////////////////////////////////////////////////////////
// register session to spokes
m_comSession = m_sessionComManager.Register("COM Session");
if (m_comSession != null)
{
// attach to session call events
m_sessionEvents = m_comSession.CallEvents as ICOMCallEvents_Event;
if (m_sessionEvents != null)
{
m_sessionEvents.CallRequested += m_sessionEvents_CallRequested;
m_sessionEvents.CallStateChanged += m_sessionEvents_CallStateChanged;
}
else
success = false;
////////////////////////////////////////////////////////////////////////////////////////
// Attach to active device and print all device information
// and registers for proximity (if supported by device)
AttachDevice();
success = true;
}
}
catch (System.Exception)
{
success = false;
}
return success;
}
internal void Disconnect()
{
DetachDevice();
if (m_comSession != null)
{
if (m_sessionEvents != null)
{
// release session events
m_sessionEvents.CallRequested -= m_sessionEvents_CallRequested;
m_sessionEvents.CallStateChanged -= m_sessionEvents_CallStateChanged;
Marshal.ReleaseComObject(m_sessionEvents);
m_sessionEvents = null;
}
// unregister session
if (m_sessionEvents != null)
{
m_sessionManagerEvents.DeviceStateChanged -= m_sessionComManager_DeviceStateChanged;
}
m_sessionComManager.UnRegister(m_comSession);
Marshal.ReleaseComObject(m_comSession);
m_comSession = null;
}
if (m_sessionComManager != null)
{
Marshal.ReleaseComObject(m_sessionComManager);
m_sessionComManager = null;
}
}
#region Print Session and Device events to console
// print session manager events
void m_sessionComManager_DeviceStateChanged(object sender, _DeviceStateEventArgs e)
{
// if our "Active device" was unplugged, detach from it and attach to new one
if (e.State == DeviceState.DeviceState_Removed && m_activeDevice != null && string.Compare(e.DevicePath, m_activeDevice.DevicePath, true) == 0)
{
DetachDevice();
AttachDevice();
}
else if (e.State == DeviceState.DeviceState_Added && m_activeDevice == null)
{
// if device is plugged, and we don't have "Active device", just attach to it
AttachDevice();
}
}
// print session manager events
void m_sessionComManager_CallStateChanged(object sender, _CallStateEventArgs e)
{
// LC I don't think this is needed...
//string id = e.CallId != null ? e.CallId.Id.ToString() : "none";
//m_debuglog.m_sCallerID = GetCallerID();
DebugPrint(MethodInfo.GetCurrentMethod().Name, "Spokes: call state event = " + e.ToString());
// LC New, if a call is in progress we should:
// 1. prevent any locking and 2. stop any lock countdown!
switch (e.Action)
{
case CallState.CallState_AcceptCall:
case CallState.CallState_CallInProgress:
case CallState.CallState_CallRinging:
case CallState.CallState_HoldCall:
case CallState.CallState_MobileCallInProgress:
case CallState.CallState_MobileCallRinging:
case CallState.CallState_Resumecall:
case CallState.CallState_TransferToHeadSet:
case CallState.CallState_TransferToSpeaker:
// Getting here indicates user is ON A CALL!
DebugPrint(MethodInfo.GetCurrentMethod().Name, "Spokes: Calling activity detected!" + e.ToString());
break;
case CallState.CallState_CallEnded:
case CallState.CallState_CallIdle:
case CallState.CallState_MobileCallEnded:
case CallState.CallState_RejectCall:
case CallState.CallState_TerminateCall:
// Getting here indicates user HAS FINISHED A CALL!
DebugPrint(MethodInfo.GetCurrentMethod().Name, "Spokes: Calling activity ended." + e.ToString());
break;
default:
// ignore other call state events
break;
}
}
public string GetCallerID()
{
string retval = "";
if (m_atdCommand != null)
{
try
{
retval = m_atdCommand.CallerID;
}
catch (System.Exception e)
{
DebugPrint(MethodInfo.GetCurrentMethod().Name, "Spokes: INFO: Exception occured getting mobile caller id\r\nException = " + e.ToString());
}
}
return retval;
}
// print session events
void m_sessionEvents_CallStateChanged(object sender, _CallStateEventArgs e)
{
string id = e.CallId != null ? e.CallId.Id.ToString() : "none";
}
// print session events
void m_sessionEvents_CallRequested(object sender, _CallRequestEventArgs e)
{
string contact = e.Contact != null ? e.Contact.Name : "none";
DebugPrint(MethodInfo.GetCurrentMethod().Name, string.Format("Session CallRequested event: Contact:({0})", contact));
}
// print device listner events
void m_deviceListenerEvents_Handler(object sender, _DeviceListenerEventArgs e)
{
switch (e.DeviceEventType)
{
case DeviceEventType.DeviceEventType_ATDButtonPressed:
break;
case DeviceEventType.DeviceEventType_ATDStateChanged:
DeviceListener_ATDStateChanged(sender, e);
break;
case DeviceEventType.DeviceEventType_BaseButtonPressed:
case DeviceEventType.DeviceEventType_BaseStateChanged:
case DeviceEventType.DeviceEventType_HeadsetButtonPressed:
case DeviceEventType.DeviceEventType_HeadsetStateChanged:
default:
break;
}
}
void m_deviceListenerEvents_HandlerMethods(object sender, _DeviceListenerEventArgs e)
{
DebugPrint(MethodInfo.GetCurrentMethod().Name, "Spokes: Received Spokes Event: " + e.ToString());
switch (e.DeviceEventType)
{
case DeviceEventType.DeviceEventType_HeadsetStateChanged:
switch (e.HeadsetStateChange)
{
case HeadsetStateChange.HeadsetStateChange_Don:
PostMessageToRegisteredWindow((int)SpokesWM.DEVICE_DON, 0, 0);
break;
case HeadsetStateChange.HeadsetStateChange_Doff:
PostMessageToRegisteredWindow((int)SpokesWM.DEVICE_DOFF, 0, 0);
break;
case HeadsetStateChange.HeadsetStateChange_Near:
PostMessageToRegisteredWindow((int)SpokesWM.DEVICE_NEAR, 0, 0);
break;
case HeadsetStateChange.HeadsetStateChange_Far:
PostMessageToRegisteredWindow((int)SpokesWM.DEVICE_FAR, 0, 0);
break;
case HeadsetStateChange.HeadsetStateChange_ProximityDisabled:
// Note: intepret this event as that the mobile phone has gone out of Bluetooth
// range and is no longer paired to the headset.
// Lock the PC, but immediately re-enable proximity
PostMessageToRegisteredWindow((int)SpokesWM.DEVICE_PROXDISABLED, 0, 0);
RegisterForProximity(true);
break;
case HeadsetStateChange.HeadsetStateChange_ProximityEnabled:
PostMessageToRegisteredWindow((int)SpokesWM.DEVICE_PROXENABLED, 0, 0);
break;
case HeadsetStateChange.HeadsetStateChange_ProximityUnknown:
PostMessageToRegisteredWindow((int)SpokesWM.DEVICE_PROXUNKNOWN, 0, 0);
break;
case HeadsetStateChange.HeadsetStateChange_InRange:
RegisterForProximity(true);
PostMessageToRegisteredWindow((int)SpokesWM.DEVICE_INRANGE, 0, 0);
break;
case HeadsetStateChange.HeadsetStateChange_OutofRange:
PostMessageToRegisteredWindow((int)SpokesWM.DEVICE_OUTOFRANGE, 0, 0);
break;
case HeadsetStateChange.HeadsetStateChange_Docked:
PostMessageToRegisteredWindow((int)SpokesWM.DEVICE_DOCK, 0, 0);
break;
case HeadsetStateChange.HeadsetStateChange_UnDocked:
PostMessageToRegisteredWindow((int)SpokesWM.DEVICE_UNDOCK, 0, 0);
break;
default:
break;
}
break;
default:
break;
}
}
// #region DeviceListener events
void DeviceListener_ATDStateChanged(object sender, _DeviceListenerEventArgs e)
{
//HANDLE ATD State changes
switch (e.ATDStateChange)
{
case ATDStateChange.ATDStateChange_MobileInComing:
PostMessageToRegisteredWindow((int)SpokesWM.MOBILE_INCOMING, 0, 0);
break;
case ATDStateChange.ATDStateChange_MobileOnCall:
PostMessageToRegisteredWindow((int)SpokesWM.MOBILE_ONCALL, 0, 0);
break;
case ATDStateChange.ATDStateChange_MobileCallEnded:
PostMessageToRegisteredWindow((int)SpokesWM.MOBILE_CALLENDED, 0, 0);
break;
case ATDStateChange.ATDStateChange_MobileCallerID:
PostMessageToRegisteredWindow((int)SpokesWM.MOBILE_CALLERID, 0, 0);
break;
case ATDStateChange.ATDStateChange_MobileOutGoing:
break;
case ATDStateChange.ATDStateChange_PstnInComingCallRingOn:
break;
case ATDStateChange.ATDStateChange_PstnInComingCallRingOff:
break;
}
}
// print device events
void m_deviceComEvents_Handler(object sender, _DeviceEventArgs e)
{
DebugPrint(MethodInfo.GetCurrentMethod().Name, string.Format("Device Event: Audio:{0} Buton:{1} Mute:{2} Usage:{3}", e.AudioState, e.ButtonPressed, e.Mute, e.Usage.ToString()));
}
#endregion
// attach to device events
private void AttachDevice()
{
m_activeDevice = m_comSession.ActiveDevice;
if (m_activeDevice != null)
{
// LC assume minimum first set of device capabilities...
DeviceCapabilities =
new SpokesDeviceCaps(false, false, true, true, true);
// LC have seen case where ProductName was empty but InternalName was not...
if (m_activeDevice.ProductName.Length > 0)
{
m_devicename = m_activeDevice.ProductName;
}
else if (m_activeDevice.InternalName.Length > 0)
{
m_devicename = m_activeDevice.InternalName;
}
else
{
m_devicename = "Could not determine device name";
}
m_deviceComEvents = m_activeDevice.DeviceEvents as IDeviceCOMEvents_Event;
if (m_deviceComEvents != null)
{
// Attach to device events
m_deviceComEvents.ButtonPressed += m_deviceComEvents_Handler;
m_deviceComEvents.AudioStateChanged += m_deviceComEvents_Handler;
m_deviceComEvents.FlashPressed += m_deviceComEvents_Handler;
m_deviceComEvents.MuteStateChanged += m_deviceComEvents_Handler;
m_deviceComEvents.SmartPressed += m_deviceComEvents_Handler;
m_deviceComEvents.TalkPressed += m_deviceComEvents_Handler;
DebugPrint(MethodInfo.GetCurrentMethod().Name, "Spokes: Attached to device events");
}
else
{
DebugPrint(MethodInfo.GetCurrentMethod().Name, "Spokes: Error: unable to attach to device events");
return;
}
m_deviceListenerEvents = m_activeDevice.DeviceListener as IDeviceListenerCOMEvents_Event;
if (m_deviceListenerEvents != null)
{
// Attach to device listener events
m_deviceListenerEvents.ATDStateChanged += m_deviceListenerEvents_Handler;
m_deviceListenerEvents.BaseButtonPressed += m_deviceListenerEvents_Handler;
m_deviceListenerEvents.BaseStateChanged += m_deviceListenerEvents_Handler;
m_deviceListenerEvents.HeadsetButtonPressed += m_deviceListenerEvents_Handler;
m_deviceListenerEvents.HeadsetStateChanged += m_deviceListenerEvents_Handler;
m_deviceListenerEvents.HeadsetStateChanged += m_deviceListenerEvents_HandlerMethods;
}
else
{
DebugPrint(MethodInfo.GetCurrentMethod().Name, "Spokes: Error: unable to attach to device listener events");
return;
}
m_atdCommand = m_activeDevice.HostCommand as IATDCommand;
if (m_atdCommand == null) DebugPrint(MethodInfo.GetCurrentMethod().Name, "Spokes: Error: unable to obtain atd command interface");
m_hostCommandExt = m_activeDevice.HostCommand as IHostCommandExt;
if (m_hostCommandExt == null) DebugPrint(MethodInfo.GetCurrentMethod().Name, "Spokes: Error: unable to obtain host command ext interface");
RegisterForProximity(true);
GetMobileCallStatus(); // are we on a call?
GetInitialDonnedStatus(); // are we donned?
UpdateOtherDeviceCapabilities();
DebugPrint(MethodInfo.GetCurrentMethod().Name, "Spokes: Attached to device");
}
}
private void DebugPrint(string methodname, string message)
{
if (m_debuglog!=null)
DebugPrint(methodname, message);
}
// hard coded other device caps, beside caller id
private void UpdateOtherDeviceCapabilities()
{
// LC temporarily hard-code some device capabilities
// e.g. fact that Blackwire C710/C720 do not support proximity, docking and is not wireless
string devname = m_devicename;
if (devname != null)
{
devname = devname.ToUpper();
if (devname.Contains("BLACKWIRE"))
{
DeviceCapabilities.IsWireless = false;
DeviceCapabilities.HasDocking = false;
DeviceCapabilities.HasWearingSensor = false;
}
if (devname.Contains("C710") || devname.Contains("C720"))
{
DeviceCapabilities.HasProximity = false;
DeviceCapabilities.HasWearingSensor = true;
}
// LC new - if using vpro or vlegend then disable docking feature...
if (devname.Contains("BT300"))
{
DeviceCapabilities.HasDocking = false;
}
if (devname.Contains("SAVI 7"))
{
DeviceCapabilities.HasWearingSensor = false;
}
}
}
// detach from device events
void DetachDevice()
{
if (m_activeDevice != null)
{
if (m_deviceComEvents != null)
{
// unregister device event handlers
m_deviceComEvents.ButtonPressed -= m_deviceComEvents_Handler;
m_deviceComEvents.AudioStateChanged -= m_deviceComEvents_Handler;
m_deviceComEvents.FlashPressed -= m_deviceComEvents_Handler;
m_deviceComEvents.MuteStateChanged -= m_deviceComEvents_Handler;
m_deviceComEvents.SmartPressed -= m_deviceComEvents_Handler;
m_deviceComEvents.TalkPressed -= m_deviceComEvents_Handler;
Marshal.ReleaseComObject(m_deviceComEvents);
m_deviceComEvents = null;
}
if (m_deviceListenerEvents != null)
{
// unregister device listener event handlers
m_deviceListenerEvents.ATDStateChanged -= m_deviceListenerEvents_Handler;
m_deviceListenerEvents.BaseButtonPressed -= m_deviceListenerEvents_Handler;
m_deviceListenerEvents.BaseStateChanged -= m_deviceListenerEvents_Handler;
m_deviceListenerEvents.HeadsetButtonPressed -= m_deviceListenerEvents_Handler;
m_deviceListenerEvents.HeadsetStateChanged -= m_deviceListenerEvents_Handler;
m_deviceListenerEvents.HeadsetStateChanged -= m_deviceListenerEvents_HandlerMethods;
RegisterForProximity(false);
Marshal.ReleaseComObject(m_deviceListenerEvents);
m_deviceListenerEvents = null;
}
Marshal.ReleaseComObject(m_activeDevice);
m_activeDevice = null;
DebugPrint(MethodInfo.GetCurrentMethod().Name, "Spokes: Detached from device");
// LC Device was disconnected, disable lock options in options GUI
DeviceCapabilities = new SpokesDeviceCaps(false, false, false, false, false); // no device = no capabilities!
m_devicename = "";
// LC Device was disconnected, clear down the GUI state...
PostMessageToRegisteredWindow((int)SpokesWM.MOBILE_CALLENDED, 0, 0);
}
m_devicename = "";
}
public void RegisterForProximity(bool register)
{
DebugPrint(MethodInfo.GetCurrentMethod().Name, "Spokes: About to register for proximity.");
try
{
if (m_hostCommandExt != null)
{
m_hostCommandExt.EnableProximity(register); // enable proximity reporting for device
if (register) m_hostCommandExt.GetProximity(); // request to receive asyncrounous near/far proximity event to HeadsetStateChanged event handler. (note: will return it once. To get continuous updates of proximity you would need a to call GetProximity() repeatedly, e.g. in a worker thread).
DebugPrint(MethodInfo.GetCurrentMethod().Name, "Spokes: Completed request to register for proximity.");
DeviceCapabilities.HasProximity = true;
}
}
catch (System.Exception)
{
DebugPrint(MethodInfo.GetCurrentMethod().Name, "Spokes: INFO: proximity may not be supported on your device.");
// uh-oh proximity may not be supported... disable it as option in GUI
DeviceCapabilities.HasProximity = false;
}
}
public void AnswerMobileCall()
{
if (m_atdCommand != null)
{
m_atdCommand.AnswerMobileCall();
}
else
{
DebugPrint(MethodInfo.GetCurrentMethod().Name, "Spokes: Error, unable to answer mobile call. atd command is null.");
}
}
public void EndMobileCall()
{
if (m_atdCommand != null)
{
m_atdCommand.EndMobileCall();
}
else
{
DebugPrint(MethodInfo.GetCurrentMethod().Name, "Spokes: Error, unable to end mobile call. atd command is null.");
}
}
internal void GetMobileCallStatus()
{
if (m_atdCommand != null)
{
try
{
m_atdCommand.GetMobileCallStatus(); // are we on a call?
bool tmpHasCallerId = true; // device does support caller id feature
// LC temporarily hard-code some device capabilities
// e.g. fact that Blackwire C710/C720 do not support proximity, docking and is not wireless
string devname = m_devicename;
if (devname != null)
{
devname = devname.ToUpper();
if (devname.Contains("SAVI 7"))
{
tmpHasCallerId = false; // Savi 7xx does not support caller id feature
}
if (devname.Contains("BLACKWIRE"))
{
tmpHasCallerId = false; // Blackwire range does not support caller id feature
}
if (devname.Contains("C710") || devname.Contains("C720"))
{
tmpHasCallerId = false; // Blackwire 700 range does not support caller id feature
}
}
DeviceCapabilities.HasCallerId = tmpHasCallerId; // set whether device supports caller id feature
}
catch (System.Exception e)
{
DebugPrint(MethodInfo.GetCurrentMethod().Name, "Spokes: INFO: Exception occured getting mobile call status\r\nException = " + e.ToString());
DeviceCapabilities.HasCallerId = false;
}
}
else
{
DebugPrint(MethodInfo.GetCurrentMethod().Name, "Spokes: Error, unable to get mobile status. atd command is null.");
DeviceCapabilities.HasCallerId = false; // device does not support caller id feature
}
}
// new get last donned status of device when plugin first runs
public void GetInitialDonnedStatus()
{
try
{
if (m_hostCommandExt != null)
{
HeadsetState laststate = m_hostCommandExt.HeadsetState;
switch (laststate)
{
case HeadsetState.HeadsetState_Doff:
PostMessageToRegisteredWindow((int)SpokesWM.DEVICE_DOFF, 0, 0);
break;
case HeadsetState.HeadsetState_Don:
PostMessageToRegisteredWindow((int)SpokesWM.DEVICE_DON, 0, 0);
break;
}
DebugPrint(MethodInfo.GetCurrentMethod().Name, "Spokes: Last donned state was: "+laststate);
}
}
catch (Exception e)
{
DebugPrint(MethodInfo.GetCurrentMethod().Name, "Other Exception in GetInitialDonnedStatus(): " + e.ToString());
}
}
// Post a Spokes event to a Window if a window has been previously registered
// using SetSpokesMessageHandlerHWnd
private void PostMessageToRegisteredWindow(int messageid, int param1, int param2)
{
if (m_hWnd != (IntPtr)0)
PostMessage(m_hWnd, messageid, param1, param2);
}
}
}