-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathCallSwitchedControl.xaml.cs
More file actions
376 lines (311 loc) · 13.1 KB
/
CallSwitchedControl.xaml.cs
File metadata and controls
376 lines (311 loc) · 13.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Interop;
using System.Windows.Media;
namespace SampleWpf;
///Provides controls for manipulating current/switched call
public partial class CallSwitchedControl : System.Windows.Controls.UserControl
{
enum UiMode { eUndef, eMain, eDtmf, eTransBlind, eTransAtt };
Siprix.CallModel? callModel_;
readonly Siprix.CallsListModel calls_;
readonly Siprix.ObjModel objModel_;
readonly Dictionary<Siprix.CallState, UiMode> uiModes_ = [];
readonly System.Windows.Threading.DispatcherTimer callDurationTimer_ = new();
readonly VideoControlHost[] receivedVideoHost_;
readonly Border[] receivedVideoBorders_;
readonly VideoControlHost previewVideoHost_ = new();
public delegate void AddCallHandler();
public event AddCallHandler? OnAddCall;
public CallSwitchedControl(Siprix.ObjModel objModel)
{
InitializeComponent();
objModel_ = objModel;
calls_ = calls_ = objModel_.Calls;
calls_.Collection.CollectionChanged += onCalls_CollectionChanged;
calls_.PropertyChanged += onCalls_PropertyChanged;
calls_.CallTerminated += onCalls_CallTerminated;
callDurationTimer_.Tick += onCallDurationTimer_Tick;
callDurationTimer_.Interval = TimeSpan.FromSeconds(1);
receivedVideoBorders_ = new Border[4];
receivedVideoHost_ = new VideoControlHost[receivedVideoBorders_.Length];
receivedVideoBorders_[0] = receivedVideo;
receivedVideoBorders_[1] = receivedVideo1;
receivedVideoBorders_[2] = receivedVideo2;
receivedVideoBorders_[3] = receivedVideo3;
for (int i = 0; i < receivedVideoBorders_.Length; ++i)
{
receivedVideoHost_[i] = new VideoControlHost();
receivedVideoBorders_[i].Child = receivedVideoHost_[i];
}
previewVideo.Child = previewVideoHost_;
}
private void onCalls_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
callDurationTimer_.IsEnabled = (calls_.Collection.Count > 0);
//when have 2 or more calls
ConferenceMenu.IsEnabled = (calls_.Collection.Count > 1);
TransferAttendedMenu.IsEnabled = (calls_.Collection.Count > 1);
}
private void onCalls_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if(e.PropertyName == nameof(Siprix.CallsListModel.SwitchedCall))
{
if (callModel_ != null) {
callModel_.PropertyChanged -= onCall_PropertyChanged;
callModel_.SetVideoWindow(IntPtr.Zero);
}
callModel_ = calls_.SwitchedCall;
DataContext = callModel_;
resetUiModes();
updateVisibility();
if (callModel_ != null) {
callModel_.PropertyChanged += onCall_PropertyChanged;
callModel_.SetVideoWindow(receivedVideoHost_[0].Hwnd);
}
calls_.SetPreviewVideoWindow(previewVideoHost_.Hwnd);
}
if (e.PropertyName == nameof(Siprix.CallsListModel.ConfModeStarted))
{
ConferenceMenu.Header = calls_.ConfModeStarted ? "End conference" : "Make conference";
SetVideoWindowConfMode();
}
}
private void onCalls_CallTerminated(uint callId, uint statusCode)
{
//if(statusCode==403)
//{
// string pathToDemoFile = AppDomain.CurrentDomain.BaseDirectory + "Resources\\music.mp3";
//
// var uri = new Uri(pathToDemoFile, UriKind.RelativeOrAbsolute);
// var player = new MediaPlayer();
// player.Open(uri);
// player.Play();
//}
}
private void onCall_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(Siprix.CallModel.CallState)||
(e.PropertyName == nameof(Siprix.CallModel.HoldState)))
{
updateVisibility();
}
}
void resetUiModes()
{
uiModes_[Siprix.CallState.Ringing] = UiMode.eUndef;
uiModes_[Siprix.CallState.Connected]= UiMode.eMain;
uiModes_[Siprix.CallState.Held] = UiMode.eMain;
}
UiMode getUiMode(Siprix.CallState? state)
{
UiMode mode = UiMode.eUndef;
if (state == null) return mode;
uiModes_.TryGetValue(state.Value, out mode);
return mode;
}
void setUiMode(Siprix.CallState? state, UiMode mode)
{
if (state == null) return;
if ((state == Siprix.CallState.Ringing) && (mode == UiMode.eMain))
mode = UiMode.eUndef;
uiModes_[state.Value] = mode;
}
void updateVisibility()
{
tbNameAndExt.Visibility = (callModel_ == null) ? Visibility.Collapsed : Visibility.Visible;
spDetails.Visibility = (callModel_ == null) ? Visibility.Collapsed : Visibility.Visible;
//Ringing
bool isConnected = (callModel_ != null) && callModel_.IsConnected;
bool isRinging = (callModel_ != null) && callModel_.IsRinging;
bool isVideo = (callModel_ != null) && callModel_.WithVideo;
spAcceptReject.Visibility = isRinging ? Visibility.Visible : Visibility.Collapsed;
bnDtmfMode.Visibility = isConnected ? Visibility.Visible : Visibility.Collapsed;
//Video
gridVideo.Visibility = isVideo ? Visibility.Visible : Visibility.Collapsed;
//Connected display depending on input mode
UiMode uiMode = getUiMode(callModel_?.CallState);
bnRedirect.Visibility = (uiMode == UiMode.eUndef)&&isRinging ? Visibility.Visible : Visibility.Collapsed;
gridDtmf.Visibility = (uiMode == UiMode.eDtmf) ? Visibility.Visible : Visibility.Collapsed;
gridMain.Visibility = (uiMode == UiMode.eMain) ? Visibility.Visible : Visibility.Collapsed;
gridTransBlind.Visibility = (uiMode == UiMode.eTransBlind) ? Visibility.Visible : Visibility.Collapsed;
gridTransAtt.Visibility = (uiMode == UiMode.eTransAtt) ? Visibility.Visible : Visibility.Collapsed;
//bnMakeCall.Visibility = (callModel_ == null) ? Visibility.Visible : Visibility.Collapsed;
bnHangup.Visibility = (callModel_ == null)|| isRinging ? Visibility.Collapsed : Visibility.Visible;
if (callModel_ != null)
bnTransfer.Content = callModel_.IsRinging ? "Redirect" : "Transfer";
}
private void onCallDurationTimer_Tick(object? sender, EventArgs e)
{
calls_.CalcDuration();
}
private void DtmfMode_Click(object sender, RoutedEventArgs e)
{
UiMode uiMode = getUiMode(callModel_?.CallState);
if (uiMode == UiMode.eDtmf)
{
//Hide DTMF keys if displayed
setUiMode(callModel_?.CallState, UiMode.eMain);
}
else
{
//Show DTMF keys
tbSentDtmf.Text = "";
setUiMode(callModel_?.CallState, UiMode.eDtmf);
}
updateVisibility();
}
private void DtmfSend_Click(object sender, RoutedEventArgs e)
{
if(sender is System.Windows.Controls.Button btnSender)
tbSentDtmf.Text += (string)btnSender.Content;
}
private void TransferBlindMode_Click(object sender, RoutedEventArgs e)
{
UiMode uiMode = getUiMode(callModel_?.CallState);
if (uiMode == UiMode.eTransBlind)
{
//Hide transfer/redirect edit if displayed
tbTransferToExt.Text = "";
setUiMode(callModel_?.CallState, UiMode.eMain);
}
else
{
//Show transfer/redirect edit if displayed
setUiMode(callModel_?.CallState, UiMode.eTransBlind);
}
updateVisibility();
}
private void TransferBlind_Click(object sender, RoutedEventArgs e)
{
callModel_?.TransferBlind(tbTransferToExt.Text);
}
private void TransferAttendedMode_Click(object sender, RoutedEventArgs e)
{
UiMode uiMode = getUiMode(callModel_?.CallState);
if (uiMode == UiMode.eTransAtt)
{
//Hide transfer/redirect edit if displayed
setUiMode(callModel_?.CallState, UiMode.eMain);
}
else
{
cbTransferCalls.Items.Clear();
foreach(Siprix.CallModel call in calls_.Collection)
{
if(call.ID != callModel_?.ID)
cbTransferCalls.Items.Add(call);
}
//Show transfer/redirect edit if displayed
setUiMode(callModel_?.CallState, UiMode.eTransAtt);
}
updateVisibility();
}
private void TransferAttended_Click(object sender, RoutedEventArgs e)
{
if (cbTransferCalls.SelectedItem is Siprix.CallModel call)
callModel_?.TransferAttended(call.ID);
}
private void PlayFile_Click(object sender, RoutedEventArgs e)
{
if (callModel_ == null) return;
if(callModel_.IsFilePlaying)
{
callModel_.StopPlayFile();
}
else
{
string pathToDemoFile = AppDomain.CurrentDomain.BaseDirectory + "Resources\\music.mp3";
callModel_.PlayFile(pathToDemoFile, false);
}
}
private void GetStats_Click(object sender, RoutedEventArgs e)
{
if ((callModel_ == null)) return;
string stats = callModel_.GetStats();
}
private void RecordFile_Click(object sender, RoutedEventArgs e)
{
if ((callModel_ == null)) return;
if (callModel_.IsFileRecording)
{
callModel_.StopRecordFile();
}
else
{
string recFile = AppDomain.CurrentDomain.BaseDirectory + DateTime.Now.ToString("yyyyMMdd_hhmmss.mp3");
callModel_.RecordFile(recFile);
}
}
private void Conference_Click(object sender, RoutedEventArgs e)
{
calls_.MakeConference();
}
private void SetVideoWindowConfMode()
{
//Set/unset video windows for other calls
for (int i = 1; i < Math.Min(calls_.Collection.Count, receivedVideoHost_.Length); ++i)
{
calls_.Collection[i].SetVideoWindow(calls_.ConfModeStarted ? receivedVideoHost_[i].Hwnd : IntPtr.Zero);
receivedVideoBorders_[i].Visibility = calls_.ConfModeStarted ? Visibility.Visible : Visibility.Collapsed;
}
//Hide rest video windows
for(int j = calls_.Collection.Count; j < receivedVideoHost_.Length; ++j)
{
receivedVideoBorders_[j].Visibility = Visibility.Collapsed;
}
if(!calls_.ConfModeStarted && calls_.SwitchedCall!=null)
calls_.SwitchedCall.SetVideoWindow(receivedVideoHost_[0].Hwnd);
calls_.SetPreviewVideoWindow(previewVideoHost_.Hwnd);
}
private void ButtonMenu_Click(object sender, RoutedEventArgs e)
{
if (sender is not System.Windows.Controls.Button btn) return;
ContextMenu contextMenu = btn.ContextMenu;
if (contextMenu == null) return;
contextMenu.PlacementTarget = btn;
contextMenu.Placement = PlacementMode.Left;
contextMenu.HorizontalOffset = btn.ActualWidth;
contextMenu.IsOpen = true;
e.Handled = true;
}
private void AddCall_Click(object sender, RoutedEventArgs e)
{
OnAddCall?.Invoke();
}
}
public class VideoControlHost : HwndHost
{
internal const int
WsChild = 0x40000000,
WsVisible = 0x10000000,
HostId = 0x00000002;
private IntPtr _hwndHost;
public IntPtr Hwnd { get { return _hwndHost; } }
protected override HandleRef BuildWindowCore(HandleRef hwndParent)
{
_hwndHost = CreateWindowEx(0, "static", "",
WsChild | WsVisible, 0, 0, 100, 100,
hwndParent.Handle, (IntPtr)HostId, IntPtr.Zero, IntPtr.Zero);
return new HandleRef(this, _hwndHost);
}
protected override IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
handled = false;
return IntPtr.Zero;
}
protected override void DestroyWindowCore(HandleRef hwnd)
{
DestroyWindow(hwnd.Handle);
}
//PInvoke declarations
[DllImport("user32.dll", EntryPoint = "CreateWindowEx", CharSet = CharSet.Unicode)]
internal static extern IntPtr CreateWindowEx(int dwExStyle,
string lpszClassName, string lpszWindowName, int style,
int x, int y, int width, int height,
IntPtr hwndParent, IntPtr hMenu, IntPtr hInst, IntPtr pvParam);
[DllImport("user32.dll", EntryPoint = "DestroyWindow", CharSet = CharSet.Unicode)]
internal static extern bool DestroyWindow(IntPtr hwnd);
}//VideoControlHost