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.
}