Multiple Calls
The IMultiCallControl
interface is similar to ISingleCallControl
.
Following APIs are the identical:
MuteState
EndCall
SignalIncomingCall
RejectIncomingCall
Mute
Unmute
Handling new calls
Start Call command
The StartCall
command lets you handle multiple calls in parallel.
When you start a call while another is already active, the current call gets put on hold, and the new call is activated.
Whenever a call is started, the OngoingCalls
observable emits a count incremented by one.
See the following code sample:
try
{
IMultiCallControl multiCallControl; // assume `IMultiCallControl` is already instantiated
await multiCallControl.StartCall();
await multiCallControl.StartCall();
multiCallControl.OngoingCalls.Subscribe(
(ongoingCalls) =>
{
// OngoingCalls = 2
// because we started two calls.
});
}
catch (JabraException ex)
{
// Something bad happened. Inspect error.
}
Accept an incoming call
The AcceptIncomingCall
method generally works the same way as the ISingleCallControl
equivalent, which has already been described.
The IMultiCallControl
interface allows you to specify what happens when a call is already active, and you can either end the call (default) or put the current call on hold.
For details on how to accept the incoming call and put the current call on hold.
See the following code sample:
// End current call...
try
{
IMultiCallControl multiCallControl; // assume `IMultiCallControl` is already instantiated
await multiCallControl.StartCall(); // OngoingCalls = 1
multiCallControl.SignalIncomingCall();
await multiCallControl.AcceptIncomingCall(AcceptIncomingCallBehavior.EndCurrent);
multiCallControl.OngoingCalls.Subscribe(
(ongoingCalls) => {
// OngoingCalls = 1
// because we ended the current call and accepted the incoming call
});
}
catch (err)
{
// Something bad happened. Inspect error.
}
// Put current call on hold...
try
{
IMultiCallControl multiCallControl; // assume `IMultiCallControl` is already instantiated
await multiCallControl.StartCall(); // OngoingCalls = 1
multiCallControl.SignalIncomingCall();
await multiCallControl.AcceptIncomingCall(AcceptIncomingCallBehavior.HoldCurrent);
multiCallControl.OngoingCalls.Subscribe(
(ongoingCalls) =>
{
// OngoingCalls = 2
// because we put the current call on hold and accepted the incoming call
});
}
catch (JabraException ex)
{
// Something bad happened. Inspect error.
}
Handling active or ongoing calls
Put a call on hold or resume a call
The IMultiCallControl
interface lets you put an active call on hold with the Hold
command and resume it with the Resume
command.
These commands are only available when the device is in an active call. Otherwise, an exception is thrown.
The following sample code shows how to put a call on hold or to resume a call:
try
{
IMultiCallControl multiCallControl; // assume `IMultiCallControl` is already instantiated
await multiCallControl.StartCall(); // hold only works if a call is started
await multiCallControl.Hold();
multiCallControl.HoldState.Subscribe(
(holdState) =>
{
// HoldState = OnHold
});
}
catch (err)
{
// Something bad happened. Inspect error.
}
In the following code sample, you can see a full sequence, whereby HoldState
changes when calling the different commands in the sequence.
try
{
IMultiCallControl multiCallControl; // assume `IMultiCallControl` is already instantiated
await multiCallControl.StartCall(); // HoldState = NotOnHold
await multiCallControl.Hold(); // HoldState = OnHold
await multiCallControl.Resume(); // HoldState = NotOntHold
await multiCallControl.EndCall(); // HoldState = NoOngoingCalls
}
catch (err)
{
// Something bad happened. Inspect error.
}
Swap call requests
A swap call request means that the user wants to swap from the current (active) call to the next call on hold.
When starting two or more calls, the device can emit swap call requests. On your device, it is triggered by the same button that activates the Hold functionality when one call is ongoing.
See the following sample code for two ongoing calls:
IMultiCallControl multiCallControl; // assume `IMultiCallControl` is already instantiated
// Swap works with a minimum of two ongoing calls
await multiCallControl.StartCall();
await multiCallControl.StartCall();
// User long-presses the multi-function-button on the device
multiCallControl.SwapRequest.Subscribe(
(_) =>
{
// The observable callback is called
// On the softphone side, put the active call on hold and resume the previously held call
});
Example: Limitations in Swapping Calls
Easy Call Control provides limited handling of swap call events as the desired behavior varies between implementations.
Description:
- Two calls are ongoing.
- The first call has already been put on hold.
- The second call is now put on hold, too. Now both calls are on hold.
User action:
- The user long-presses the multi-function button to request a swap.
Solution:
This use case is typically handled by resuming the latest call put on hold. However, there are also implementations whereby swap call requests are interpreted as a wish to merge the two held calls. If in doubt, ignore the swap call request and handle it through the UI.
See the following code sample on how to resume the last current call.
// Example of how to resume the "last current call"
IMultiCallControl multiCallControl; // assume `IMultiCallControl` is already instantiated
await multiCallControl.StartCall(); // first call started
await multiCallControl.StartCall(); // second call started, it is now the current call while firstCall is on hold.
await multiCallControl.Hold(); // hold current call, meaning that both calls are on hold
// Subscribe to changes in HoldState so we know the current state
HoldState currentHoldState = new HoldState();
multiCallControl.HoldState.Subscribe(
(newHoldState) =>
{
currentHoldState = newHoldState;
});
// User long-presses the multi-function-button on the device
multiCallControl.SwapRequest.Subscribe(
async (_) =>
{
// Assess the current HoldState
if (currentHoldState == HoldState.OnHold)
{
// `Resume()` will put the device back into an active call state,
// however, you need to handle on the softphone side which of the held calls
// to resume
await multiCallControl.Resume();
}
});
Ongoing calls
As previously explained, you can subscribe to the OngoingCalls
counter.
See following code sample:
IMultiCallControl multiCallControl; // assume `IMultiCallControl` is already instantiated
multiCallControl.OngoingCalls.Subscribe(
(ongoingCalls) =>
{
// OngoingCalls equals to the number of calls started
});
Whenever you start a call -- either through StartCall
or AcceptIncomingCall
-- this count increments by one. It does not matter if the ongoing/active calls are on hold or not -- it is the total sum of calls.
At the same time, whenever a call is ended by calling EndCall
or by pressing the End call button on the device, the count decrements by one.
When the count gets back to zero, the device ends the active call state and returns to an idle or inactive state.
Ending a call
Option A) From your application
During an active call, the user may want to end the call. Your application may offer this functionality to the user, for example a red handset button in the UI, indicating the ability to hang up or end the call.
When the user presses the button, the call ends on your application. However, the Jabra device remains in an active call state until you indicate to it that the call has ended.
See following code sample:
try
{
IMultiCallControl multiCallControl; // assume `IMultiCallControl` is already instantiated
await multiCallControl.EndCall();
}
catch (JabraException ex)
{
// Something bad happened. Inspect error.
}
Option B) By interacting with the device
Jabra devices let you end a call from the device itself. Therefore, your application must stop the call when the device indicates that the user interacted with it and wants to hang up or end the call.
You can do this by subscribing to the CallActive
property and acting appropriately when state changes occur.
ISingleCallControl singleCallControl; // assume `ISingleCallControl` is already instantiated
singleCallControl.CallActive.Subscribe(
(callActive) =>
{
if (callActive)
{
// the device is in an "active call state", reflect on the changes (e.g. update the UI)
}
else
{
// the device is no longer in a call state, reflect on the changes (e.g. update the UI)
}
});
Change the device used in an active call
The Easy Call Control module uses a device-based abstraction.
Changing the device requires:
- Creating a new instance and passing a reference to the new device
- Using the
Teardown
method to indicate that the old device that is no longer used in that call - Unsubscribe all observables for the old device
See the following code sample:
var oldDeviceCallControl; // assume `ISingleCallControl` or `IMultiCallControl` is already instantiated
var newDeviceCallControl; // assume `ISingleCallControl` or `IMultiCallControl` is already instantiated
// For IMultiCallControl you should follow the same pattern for OngoingCalls, HoldState and SwapRequest
var muteStateSub = oldDeviceCallControl.MuteState.Subscribe(); // assume you have stored Subscriptions for MuteState
var callActiveSub = oldDeviceCallControl.CallActive.Subscribe(); // assume you have stored Subscriptions for CallActive
// End the call on the old device
await oldDeviceCallControl.EndCall();
// Unsubscribe
muteStateSub.Unsubscribe();
callActiveSub.Unsubscribe();
// Teardown old instance
oldDeviceCallControl.Teardown();
// Subscribe to state changes for the new device
muteStateSub = newDeviceCallControl.MuteState.Subscribe();
callActiveSub = newDeviceCallControl.CallState.Subscribe();
try
{
await newDeviceCallControl.StartCall();
}
catch (err)
{
// Something bad happened. Inspect error.
}