src/bindings/AppController.cs
using Microsoft.Lync.Model;
using Microsoft.Lync.Model.Conversation;
using Microsoft.Lync.Model.Conversation.AudioVideo;
using Microsoft.Lync.Model.Extensibility;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace SkypeClient
{
using Proxy = Func<object, Task<object>>;
class AppController
{
private static AppController instance;
private readonly LyncClient client;
private readonly Automation automation;
private AppController(LyncClient client, Automation automation)
{
this.client = client;
this.automation = automation;
// Always start video on AV modality connect
ExecuteAction.InState<AVModality>(client, ModalityTypes.AudioVideo, ModalityState.Connected, (conversation, modality) =>
{
CallMedia.StartVideo(modality);
});
// Always end the conversation (and close the window) on AV modality disconnect
ExecuteAction.OnAllConversations(client, conversation =>
{
var av = conversation.Modalities[ModalityTypes.AudioVideo];
av.ModalityStateChanged += (o, e) =>
{
if (e.NewState == ModalityState.Disconnected && e.OldState != ModalityState.Invalid)
{
conversation.End();
}
};
});
}
private static void BindNewInstance()
{
try
{
var client = LyncClient.GetClient();
var automation = LyncClient.GetAutomation();
instance = new AppController(client, automation);
}
catch (ClientNotFoundException)
{
throw new InvalidStateException("No Skype / Lync app running");
}
}
public static AppController Instance(bool requireAuthed = true)
{
if (instance == null)
{
BindNewInstance();
}
else
{
// This will trigger a NullReferenceException if our client reference
// is no longer valid (e.g. the app was restarted) and we need to rebind.
try
{
var canary = instance.client.ConversationManager.Conversations.ToArray();
}
catch (NullReferenceException)
{
BindNewInstance();
}
}
if (instance.client.State != ClientState.SignedIn && requireAuthed)
{
throw new InvalidStateException("Skype / Lync client is not signed in");
}
return instance;
}
public object GetActiveUser()
{
var self = client.Self.Contact;
return new {
uri = self.Uri,
name = self.GetContactInformation(ContactInformationType.DisplayName)
};
}
public string GetClientState()
{
return client.State.ToString();
}
public void SignIn(string user = null, string password = null)
{
if (client.State != ClientState.SignedOut)
{
throw new InvalidStateException("Skype / Lync client must be signed out in order to sign in a new user");
}
client.BeginSignIn(
user,
user,
password,
ar =>
{
client.EndSignIn(ar);
},
null);
}
public void SignOut()
{
if (client.State != ClientState.SignedIn)
{
throw new InvalidStateException("Skype / Lync client must be signed in in order to sign out");
}
client.BeginSignOut(
ar =>
{
client.EndSignIn(ar);
},
null);
}
public void Call(string uri, bool fullscreen = true, int display = 0)
{
List<string> participants = new List<string> { uri };
Call(participants, fullscreen, display);
}
public void Call(List<string> participants, bool fullscreen = true, int display = 0)
{
automation.BeginStartConversation(
AutomationModalities.Video,
participants,
null,
(ar) =>
{
ConversationWindow window = automation.EndStartConversation(ar);
if (fullscreen)
{
CallWindow.ShowFullscreen(window, display);
}
},
null);
}
public void JoinMeeting(string url, bool fullscreen = true, int display = 0)
{
automation.BeginStartConversation(
url,
0,
ar =>
{
ConversationWindow window = automation.EndStartConversation(ar);
if (fullscreen)
{
var av = window.Conversation.Modalities[ModalityTypes.AudioVideo];
ExecuteAction.InState(av, ModalityState.Connected, (modality) =>
{
CallWindow.ShowFullscreen(automation, modality.Conversation, display);
});
}
},
null);
}
public void EndConversation(Conversation conversation)
{
var av = conversation.Modalities[ModalityTypes.AudioVideo];
if (av.CanInvoke(ModalityAction.Disconnect))
{
av.BeginDisconnect(ModalityDisconnectReason.None,
ar =>
{
av.EndDisconnect(ar);
},
null);
}
}
public void HangupAll()
{
Util.ForEach(client.ConversationManager.Conversations, EndConversation);
}
public void StartVideo(Conversation conversation)
{
var av = (AVModality)conversation.Modalities[ModalityTypes.AudioVideo];
CallMedia.StartVideo(av);
}
public void StopVideo(Conversation conversation)
{
var av = (AVModality)conversation.Modalities[ModalityTypes.AudioVideo];
CallMedia.StopVideo(av);
}
public void SetMute(Conversation conversation, bool state)
{
var participant = conversation.SelfParticipant;
participant.BeginSetMute(
state,
ar =>
{
participant.EndSetMute(ar);
},
null);
}
public void MuteAll(bool state)
{
Action<Conversation> mute = c => SetMute(c, state);
Util.ForEach(client.ConversationManager.Conversations, mute);
}
public void OnIncoming(Proxy callback)
{
ExecuteAction.InState<AVModality>(client, ModalityTypes.AudioVideo, ModalityState.Notified, (conversation, modality) =>
{
var inviter = (Contact)conversation.Properties[ConversationProperty.Inviter];
#pragma warning disable 1998
Proxy AcceptCall = Bindings.CreateAction((dynamic kwargs) =>
{
modality.Accept();
if (kwargs.fullscreen) CallWindow.ShowFullscreen(automation, conversation, kwargs.display);
});
Proxy RejectCall = Bindings.CreateAction((dynamic kwargs) => modality.Reject(ModalityDisconnectReason.Decline));
#pragma warning restore 1998
callback(new
{
inviter = new UserDetails(inviter),
actions = new
{
accept = AcceptCall,
reject = RejectCall
}
});
});
}
public void OnConnect(Proxy callback)
{
ExecuteAction.InState<AVModality>(client, ModalityTypes.AudioVideo, ModalityState.Connected, (conversation, modality) =>
{
var participants = conversation.Participants.Where(p => !p.IsSelf).Select(p => new UserDetails(p.Contact));
#pragma warning disable 1998
Proxy Fullscreen = Bindings.CreateAction((dynamic kwargs) => CallWindow.ShowFullscreen(automation, conversation, kwargs.display));
Proxy Show = Bindings.CreateAction((dynamic kwargs) => CallWindow.Show(automation, conversation));
Proxy Hide = Bindings.CreateAction((dynamic kwargs) => CallWindow.Hide(automation, conversation));
Proxy Mute = Bindings.CreateAction((dynamic kwargs) => SetMute(conversation, kwargs.state));
Proxy StartVideo = Bindings.CreateAction((dynamic kwargs) => this.StartVideo(conversation));
Proxy StopVideo = Bindings.CreateAction((dynamic kwargs) => this.StopVideo(conversation));
Proxy End = Bindings.CreateAction((dynamic kwargs) => EndConversation(conversation));
#pragma warning restore 1998
callback(new
{
participants = participants,
actions = new
{
fullscreen = Fullscreen,
show = Show,
hide = Hide,
mute = Mute,
startVideo = StartVideo,
stopVideo = StopVideo,
end = End
}
});
});
}
public void OnDisconnect(Proxy callback)
{
ExecuteAction.OnConversationEnd(client, conversation => callback(null));
}
public void OnMuteChange(Proxy callback)
{
ExecuteAction.OnAllConversations(client, conversation =>
{
var self = conversation.SelfParticipant;
var av = (AVModality)conversation.Modalities[ModalityTypes.AudioVideo];
// The client raises multiple property changed events during call setup. Squash these so we one raise events on change.
var previousState = (bool)av.Properties[ModalityProperty.AVModalityAudioCaptureMute];
av.AVModalityPropertyChanged += (o, e) =>
{
if (e.Property == ModalityProperty.AVModalityAudioCaptureMute)
{
var newState = (bool)e.Value;
if (newState != previousState) callback(newState);
previousState = newState;
}
};
});
}
public void OnClientStateChange(Proxy callback)
{
client.StateChanged += (o, e) =>
{
callback(e.NewState.ToString());
};
}
}
}