Android softpone: Enable and disable codecs

In the following tutorial you will learn, how to make and receive calls with codecs encoding.

Related: VoIP SIP enabling codecs. If also you wish to build VoIP softphone on Windows, you might also be interested in a similar document, the VoIP SIP Enabling Codecs in Ozeki VoIP SDK for Windows

Quick steps

  1. Download and install the Ozeki Andorid SDK
  2. Create a new Xamarin Android application in Viusal Studio
  3. Add the OzekiSDK.dll as a reference to our project
  4. Create the layout of the application
  5. Create a Softphone class that implements the ISoftphone interface
  6. Create the MainActivity.cs activity which will ask for the login details
  7. Create the DialingActivity.cs file which will be responsible for making and receiving calls. You can manually change the encoding of the call by selecting the right one from a dropdown list.
  8. Run the example application and you can start modifying the encoding of the call.

The 06_Codecs_handler.zip file

You can download the 06_Codecs_handler example application bellow, and start making and receiving calls with it right now, or you can build it yourself step by step to understand the base concept of the Ozeki VoIP SIP SDK.

Download: 06_Codecs_handler.zip (2.18Mb)

Create a new Xamarin Android application in Visual Studio

To build our softphone application, we will create a new Xamarin Andorid project. In the following video I'll show you how to create a new Xamarin application in Visual Studio.

Video 1 - How to create a new Xamarin Andorid project in Visual Studio Community

How to add the OzekiSDK.dll to our project as a reference

In order to use the contents of the Ozeki.VoIP namespace, we have to include the OzekiSDK.dll in our project. In the following video I'll show you how to add the OzekiSDK.dll reference to your project. You can find the OzekiSDK.dll file in the following place: C:\Program Files\Ozeki\Ozeki SDK for Android\SDK\MonoAndroid.

how to add a dll reference to a visual studio project
Figure 1 - Right click on the "References" folder, than choose the "Add Reference..." option

how to add a dll reference to a visual studio project
Figure 2 - Click on the Browse... button, and search for the OzekiSDK.dll file on your disk

how to add a dll reference to a visual studio project
Figure 3 - Select the .dll file, and click on the Add button

how to add a dll reference to a visual studio project
Figure 4 - After you have added the reference, you can import the components of the OzekiSDK.dll file

Video 2 - How to add the OzekiSDK dll reference to you project

The layout of the application

For the MainActivity activity we are going to use the layout of the Android softphone: SIP registration example. This activity will be responsible for the SIP registration. For the DialingActivity activity we have modified the layout of the Android softphone: Make calls project.

You can simply drag and drop these .xml files into you layout folder, or you can create your own layout.

Download: layout.zip (1.96Kb)

Video 3 - How to add the .xml files below to your project

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <GridLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:rowCount="9"
    >

        <TextView
            android:layout_width="300dp"
            android:layout_gravity="center_vertical|center_horizontal"
            android:layout_height="wrap_content"
            android:layout_row="0"
            android:text="SIP registration"
            android:textSize="12pt"
            android:textAlignment="center"
            android:layout_marginTop="10dp"
        />

        <EditText
            android:id="@+id/inputDisplayName"
            android:layout_width="300dp"
            android:layout_gravity="center_vertical|center_horizontal"
            android:layout_height="wrap_content"
            android:layout_column="0"
            android:layout_row="1"
            android:hint="Display name"
            android:inputType="text" 
        />

        <EditText
            android:id="@+id/inputUserName"
            android:layout_width="300dp"
            android:layout_gravity="center_vertical|center_horizontal"
            android:layout_height="wrap_content"
            android:layout_column="0"
            android:layout_row="2"
            android:hint="Username"
            android:inputType="text" 
        />

        <EditText
            android:id="@+id/inputAuthenticationID"
            android:layout_width="300dp"
            android:layout_gravity="center_vertical|center_horizontal"
            android:layout_height="wrap_content"
            android:layout_column="0"
            android:layout_row="3"
            android:hint="Authentication ID"
            android:inputType="text" 
        />

        <EditText
            android:id="@+id/inputRegisterPassword"
            android:layout_width="300dp"
            android:layout_gravity="center_vertical|center_horizontal"
            android:layout_height="wrap_content"
            android:layout_column="0"
            android:layout_row="4"
            android:inputType="textPassword"
            android:hint="Password"
        />

        <EditText
            android:id="@+id/inputDomainHost"
            android:layout_width="300dp"
            android:layout_gravity="center_vertical|center_horizontal"
            android:layout_height="wrap_content"
            android:layout_column="0"
            android:layout_row="5"
            android:hint="Host e.g.: 127.0.0.1"
            android:inputType="text" 
        />

        <EditText
            android:id="@+id/inputDomainPort"
            android:layout_width="300dp"
            android:layout_gravity="center_vertical|center_horizontal"
            android:layout_height="wrap_content"
            android:layout_column="0"
            android:layout_row="6"
            android:hint="Port e.g.: 5060"
            android:inputType="text" 
        />

        <Button
            android:id="@+id/btnLogin"
            android:layout_width="300dp"
            android:layout_gravity="center_vertical|center_horizontal"
            android:layout_height="wrap_content"
            android:layout_column="0"
            android:backgroundTint="@android:color/holo_red_light"
            android:textColor="@android:color/white"
            android:layout_row="7"
            android:text="Login"
        />

        <GridLayout
            android:layout_width="300dp"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical|center_horizontal"
            android:layout_row="8"
            android:layout_column="0"
            android:rowCount="2"
            android:columnCount="1"
        >

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textSize="10pt"
                android:text="Log:"
                android:layout_row="0"/>

            <TextView
                android:id="@+id/log"
                android:layout_width="match_parent"
                android:layout_height="120dp"/>

        </GridLayout>

    </GridLayout>

</androidx.coordinatorlayout.widget.CoordinatorLayout>
	

Code 1 - activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior">

    <GridLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:rowCount="8"
        android:columnCount="1"
    >

        <TextView
            android:id="@+id/btnLogOut"
            android:layout_width="match_parent"
            android:layout_marginTop="10dp"
            android:layout_marginLeft="10dp"
            android:layout_height="wrap_content"
            android:text="Logout"
            android:layout_row="0"
            android:layout_column="0"
        />
       
        <TextView
            android:layout_width="300dp"
            android:layout_height="50dp"
            android:layout_marginTop="25dp"
            android:layout_gravity="center_vertical|center_horizontal"
            android:textAlignment="center"
            android:textSize="12pt"
            android:layout_row="1"
            android:layout_column="0"
            android:text="SIP registration"/>

        <TextView
            android:id="@+id/status"
            android:layout_width="250dp"
            android:layout_height="50dp"
            android:textAlignment="center"
            android:layout_row="2"
            android:layout_column="0"
            android:layout_gravity="center_vertical|center_horizontal"
            android:text="Not registered"
        />

        <TextView
            android:id="@+id/inputNumberToCall"
            android:layout_width="300dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="100dp"
            android:textAlignment="center"
            android:layout_row="3"
            android:textSize="18pt"
            android:layout_column="0"
            android:layout_gravity="center_vertical|center_horizontal"
        />

        <GridLayout
            android:layout_width="300dp"
            android:layout_gravity="center_horizontal"
            android:layout_height="30dp"
            android:rowCount="1"
            android:layout_row="1"
            android:columnCount="2">

            <TextView
                android:layout_width="100dp"
                android:layout_height="30dp"
                android:text="Codec:"
                android:layout_row="0"
                android:layout_column="0"
                android:textSize="8pt"/>

            <Spinner
                android:id="@+id/spinner"
                android:layout_width="200dp"
                android:layout_height="30dp"
                android:layout_row="0"
                android:layout_column="1"/>

        </GridLayout>

        <GridLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="5dp"
            android:layout_gravity="center_vertical|center_horizontal"
            android:layout_row="5"
            android:layout_column="0"
            android:rowCount="5"
            android:columnCount="3"
        >

            <Button
                android:id="@+id/btnOne"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="1"
                android:layout_row="0"
                android:layout_margin="2dp"
                android:layout_column="0"
                android:backgroundTint="@android:color/holo_red_light"
                android:textColor="@android:color/white"
            />

            <Button
                android:id="@+id/btnTwo"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="2"
                android:layout_row="0"
                android:layout_margin="2dp"
                android:layout_column="1"
                android:backgroundTint="@android:color/holo_red_light"
                android:textColor="@android:color/white"
            />

            <Button
                android:id="@+id/btnThree"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="3"
                android:layout_row="0"
                android:layout_margin="2dp"
                android:layout_column="2"
                android:backgroundTint="@android:color/holo_red_light"
                android:textColor="@android:color/white"
            />

            <Button
                android:id="@+id/btnFour"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="4"
                android:layout_row="1"
                android:layout_margin="2dp"
                android:layout_column="0"
                android:backgroundTint="@android:color/holo_red_light"
                android:textColor="@android:color/white"
            />

            <Button
                android:id="@+id/btnFive"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="5"
                android:layout_row="1"
                android:layout_margin="2dp"
                android:layout_column="1"
                android:backgroundTint="@android:color/holo_red_light"
                android:textColor="@android:color/white"
            />

            <Button
                android:id="@+id/btnSix"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="6"
                android:layout_row="1"
                android:layout_margin="2dp"
                android:layout_column="2"
                android:backgroundTint="@android:color/holo_red_light"
                android:textColor="@android:color/white"
            />

            <Button
                android:id="@+id/btnSeven"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="7"
                android:layout_row="2"
                android:layout_margin="2dp"
                android:layout_column="0"
                android:backgroundTint="@android:color/holo_red_light"
                android:textColor="@android:color/white"
            />

            <Button
                android:id="@+id/btnEight"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="8"
                android:layout_row="2"
                android:layout_margin="2dp"
                android:layout_column="1"
                android:backgroundTint="@android:color/holo_red_light"
                android:textColor="@android:color/white"
            />

            <Button
                android:id="@+id/btnNine"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="9"
                android:layout_row="2"
                android:layout_margin="2dp"
                android:layout_column="2"
                android:backgroundTint="@android:color/holo_red_light"
                android:textColor="@android:color/white"
            />

            <Button
                android:id="@+id/btnStar"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="*"
                android:layout_row="3"
                android:layout_margin="2dp"
                android:layout_column="0"
                android:backgroundTint="@android:color/holo_red_light"
                android:textColor="@android:color/white"
            />

            <Button
                android:id="@+id/btnZero"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="0"
                android:layout_row="3"
                android:layout_margin="2dp"
                android:layout_column="1"
                android:backgroundTint="@android:color/holo_red_light"
                android:textColor="@android:color/white"
            />

            <Button
                android:id="@+id/btnHashtag"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="#"
                android:layout_row="3"
                android:layout_margin="2dp"
                android:layout_column="2"
                android:backgroundTint="@android:color/holo_red_light"
                android:textColor="@android:color/white"
            />
        </GridLayout>

        <Button
            android:id="@+id/btnInteractions"
            android:layout_width="250dp"
            android:layout_height="wrap_content"
            android:layout_row="6"
            android:layout_column="0"
            android:layout_margin="2dp"
            android:backgroundTint="@android:color/holo_red_light"
            android:textColor="@android:color/white"
            android:layout_gravity="center_vertical|center_horizontal"
            android:text="Dial"
        />

        <TextView
            android:id="@+id/btnClear"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical|center_horizontal"
            android:layout_row="7"
            android:layout_column="0"
            android:text="Clear"
        />

    </GridLayout>

</RelativeLayout>

	

Code 2 - activity_dialing.xml

Create the Softphone.cs file

With the softphone class, you can initialize a sofphone instance which will be responsible for making calls with codecs. In the following video you can see how to create the Softphone class.

Video 4 - Creating the Softphone class

Softphone.cs

		using System;
		using System.Collections.Generic;
		using Ozeki.VoIP;
		using Ozeki.Media;
		using System.Threading;
		
		namespace _06_Codecs_handler
		{
		    class Softphone
		    {
		        /* softphone object */
		        ISoftPhone _softphone;
		        /* phone line object */
		        IPhoneLine _phoneLine;
		        /* the call object */
		        IPhoneCall _call;
		        Microphone _microphone;
		        Speaker _speaker;
		        /* connects the devices to each other (eg. microphone, speaker, mediaSender, mediaReceiver) */
		        MediaConnector _connector;
		        /* after connected with the microphone, this will be attached to the call */
		        PhoneCallAudioSender _mediaSender;
		        /* after connected with the speaker, this will be attached to the call */
		        PhoneCallAudioReceiver _mediaReceiver;
		
		        /* indicates wheter we have an incoming call (so, the phone is ringing) */
		        bool _incomingCall;
		
		        /* Occurs when an incoming call received. */
		        public event EventHandler IncomingCall;
		
		        /* Occurs when the registration state of the phone line has changed. */
		        public event EventHandler<RegistrationStateChangedArgs> PhoneLineStateChanged;
		
		        /* Occurs when the state of the call has changed. */
		        public event EventHandler<CallStateChangedArgs> CallStateChanged;
		
		
		
		        /* Default constructor, initalizes the softphone with deafult parameters. */
		        public Softphone()
		        {
		            _softphone = SoftPhoneFactory.CreateSoftPhone(5000, 10000);
		
		            _microphone = Microphone.GetDefaultDevice();
		            _speaker = Speaker.GetDefaultDevice();
		            _connector = new MediaConnector();
		            _mediaSender = new PhoneCallAudioSender();
		            _mediaReceiver = new PhoneCallAudioReceiver();
		
		            _incomingCall = false;
		        }
		
		        /*
		            Registers the SIP account to the PBX. 
		            Calls cannot be made while the SIP account is not registered.
		            If the SIP account requires no registration, the RegisterPhoneLine()
		        must be called too to register the SIP account to the ISoftPhone.
		        */
		        public void Register(bool registrationRequired, string displayName,
		            string userName, string authenticationId, string registerPassword,
		            string domainHost, int domainPort)
		        {
		            try
		            {
		                /* To register to a PBX, we need to create a SIP account*/
		                var account = new SIPAccount(registrationRequired, displayName, userName,
		                    authenticationId, registerPassword, domainHost, domainPort);
		
		                /* With the SIP account and the NAT configuration, we can create a phoneline. */
		                _phoneLine = _softphone.CreatePhoneLine(account);
		
		                /* The phoneline has states, we need to handle the event, when it is being changed. */
		                _phoneLine.RegistrationStateChanged += phoneLine_PhoneLineStateChanged;
		
		                // If our phoneline is created, we can register that.
		                _softphone.RegisterPhoneLine(_phoneLine);
		
		                // For further information about the calling of the ConnectMedia(), please check the implementation of this method.
		                ConnectMedia();
		            }
		            catch (Exception ex)
		            {
		                Console.WriteLine("Error during SIP registration: " + ex);
		            }
		        }
		
		
		        /*
		            This will be called when the registration state of the phone line has changed.
		        */
		        private void phoneLine_PhoneLineStateChanged(object sender, RegistrationStateChangedArgs e)
		        {
		            DispatchAsync(() =>
		            {
		                var handler = PhoneLineStateChanged;
		                if (handler != null)
		                    handler(this, e);
		            });
		        }
		
		
		        /*
		            Starts the capturing and playing audio/video devices.
		            Other devices can be used (and started), for example: WebCamera or WaveStreamPlayback.
		        */
		        private void StartDevices()
		        {
		            if (_microphone != null)
		            {
		                _microphone.Start();
		            }
		
		            if (_speaker != null)
		            {
		                _speaker.Start();
		            }
		        }
		
		
		        /*
		            Stops the capturing and playing audio/video devices.
		            Other devices can be stopped, for example: WebCamera.
		        */
		        private void StopDevices()
		        {
		            if (_microphone != null)
		            {
		                _microphone.Stop();
		            }
		
		            if (_speaker != null)
		            {
		                _speaker.Stop();
		            }
		        }
		
		
		        /*
		            To send our voice through the microphone to the other client's speaker, we need to connect them.
		            We send our voice through the mediaSender, and we get the other client's
		            voice through the mediaSender to our speaker object. 
		          
		            To disconnect these handlers, we will use the DisconnectMedia() method.
				           
		            It is possible to use other mediahandlers with the connector, for example we can connect a
		            WaveStreamPlayback or an MP3StreamPlayback object to the MediaSender, so we can play music/voice
		            during the call. For exmaple: when can create an IVR (Interactive Voice Response),
		            we can create voice recorder etc.
		         
		             For example:
		             We can connect an .mp3 file player (which plays an mp3 file into the voice call) by
		            the "connector.Connect(Mp3StreamPlayback, mediaSender);  " line.
		             (We should also create an MP3StreamPlayback object: "MP3StreamPlayback Mp3StreamPlayback; "
		             and we need to tell to this object the details (what to play into the speaker, etc.))
		         */
		
		        /*
		            Connects the audio handling devices to each other.
		            The audio data will flow from the source to the destination.
		        */
		        private void ConnectMedia()
		        {
		            if (_microphone != null)
		            {
		                _connector.Connect(_microphone, _mediaSender);
		            }
		
		            if (_speaker != null)
		            {
		                _connector.Connect(_mediaReceiver, _speaker);
		            }
		        }
		
		
		        /*
		            Disconnects the audio handling devices from each other.
		        */
		        private void DisconnectMedia()
		        {
		            if (_microphone != null)
		            {
		                _connector.Disconnect(_microphone, _mediaSender);
		            }
		
		            if (_speaker != null)
		            {
		                _connector.Disconnect(_mediaReceiver, _speaker);
		            }
		
		            /*
		                You can close all of the connections by using: connector.Dispose();
		            */
		        }
		
		
		        /*
		            Subscribes to the events of a call to receive notifications such as the state of the call has changed.
		            In this sample subscribes only to the state changed and error occurred events.
		        */
		        private void WireUpCallEvents()
		        {
		            _call.CallStateChanged += (call_CallStateChanged);
		        }
		
		
		        /*
		            Unsubscribes from the events of a call.
		        */
		        private void WireDownCallEvents()
		        {
		            _call.CallStateChanged -= (call_CallStateChanged);
		        }
		
		
		        /*
		            This will be called when the state of the call call has changed.
		
		            In this sample only three states will be handled: Answered, InCall, Ended
		             
		            Answered: when the call has been answered, the audio devices will be started and attached to the call.
		            It is required to comminicate with the other party and hear them.
		            The devices are connected at softphone initialization time,
		            so no need to connect them every time when a call is being answered.
		    
		            InCall: when the call is in an active state, the audio deveices will be started.
		        
		            Ended: when the call ends, the audio devices will be stopped and detached from the call.
		        */
		        private void call_CallStateChanged(object sender, CallStateChangedArgs e)
		        {
		            /* the call has been answered */
		            if (e.State == CallState.Answered)
		            {
		                StartDevices();
		
		                _mediaReceiver.AttachToCall(_call);
		                _mediaSender.AttachToCall(_call);
		            }
		
		            /*
		                The call is in active communication state
		                IMPORTANT: this state can occur multiple times. for example when
		                answering the call or the call has been taken off hold.
		            */
		            if (e.State == CallState.InCall)
		            {
		                StartDevices();
		            }
		
		            /* the call has ended */
		            if (e.State.IsCallEnded())
		            {
		                if (_call != null)
		                {
		                    CallFinished();
		                }
		            }
		
		            DispatchAsync(() =>
		            {
		                var handler = CallStateChanged;
		                if (handler != null)
		                    handler(this, e);
		            });
		        }
		
		
		        /*
		            Starts calling the specified number.
		            In this sample an outgoing call can be made if there is no current call (outgoing or incoming) on the phone line.
		        */
		        public void StartCall(string numberToDial)
		        {
		            if (_call == null)
		            {
		                _call = _softphone.CreateCallObject(_phoneLine, numberToDial);
		                WireUpCallEvents();
		
		                /* To make a call simply call the Start() method of the call object. */
		                _call.Start();
		            }
		        }
		
		
		        /*
		            Hangs up the current call.
		        */ 
		        public void HangUp()
		        {
		            if (_call != null)
		            {
		                _call.HangUp();
		                _call = null;
		            }
		        }
		
		
		        /*
		            If the call ends, we won't need our speaker and microphone anymore to communicate,
		            until we enter into a call again, so we are calling the StopDevices() method.
		            The mediaHandlers are getting detached from the call object
		            (since we are not using our microphone and speaker, we have no media to send).
		            We won't need the call's events anymore, becouse our call is about to be ended,
		            and with setting the call to null, we are ending it.
		        */
		        public void CallFinished()
		        {
		            StopDevices();
		
		            _mediaReceiver.Detach();
		            _mediaSender.Detach();
		
		            WireDownCallEvents();
		
		            _call = null;
		        }
		
		
		        /*
		            This method is used to solve the task blockings.
		        */
		        private void DispatchAsync(Action action)
		        {
		            var task = new WaitCallback(o => action.Invoke());
		            ThreadPool.QueueUserWorkItem(task);
		        }
		
		
		        /*
		            This method returns the available codecs.
		        */
		        public IEnumerable<CodecInfo> Codecs()
		        {
		            return _softphone.Codecs;
		        }
		
		
		        /*
		            This method enables the codecs received as parameters.
		        */
		        public void EnableCodec(int codec)
		        {
		            _softphone.EnableCodec(codec);
		        }
		
		
		        /*
		            This method disables the codecs received as parameters.
		        */
		        public void DisableCodec(int codec)
		        {
		            _softphone.DisableCodec(codec);
		        }
		    }
		}
	

Code 3 - Softphone.cs

Create the MainActivity.cs file

This activity will be responsible for collecting the login details of the phone extension. After if have collected the credentials, it will store the values in the Xamarin.Essentials.Preferences, what you can use throughout the whole application.

Video 5 - Creating the MainActivity.cs file

MainActivty.cs

		using System;
		using Android.App;
		using Android.OS;
		using Android.Runtime;
		using Android.Views;
		using AndroidX.AppCompat.App;
		using Android.Widget;
		
		namespace _06_Codecs_handler
		{
		    [Activity(Label = "@string/app_name", Theme = "@style/AppTheme.NoActionBar", MainLauncher = true)]
		    public class MainActivity : AppCompatActivity
		    {
		        Button _btnLogin;
		        TextView _log;
		        protected override void OnCreate(Bundle savedInstanceState)
		        {
		            base.OnCreate(savedInstanceState);
		            Xamarin.Essentials.Platform.Init(this, savedInstanceState);
		            SetContentView(Resource.Layout.activity_main);
		
		            TestIfUserLoggedIn();
		
		            _btnLogin = FindViewById<Button>(Resource.Id.btnLogin);
		            _btnLogin.Click += OnClick__btnLogin;
		
		            _log = FindViewById<TextView>(Resource.Id.log);
		        }
		
		        private void OnClick__btnLogin(object sender, EventArgs e)
		        {
		            string displayName = FindViewById<EditText>(Resource.Id.inputDisplayName).Text;
		            string userName = FindViewById<EditText>(Resource.Id.inputUserName).Text;
		            string authenticationId = FindViewById<EditText>(Resource.Id.inputAuthenticationID).Text;
		            string registerPassword = FindViewById<EditText>(Resource.Id.inputRegisterPassword).Text;
		            string domainHost = FindViewById<EditText>(Resource.Id.inputDomainHost).Text;
		            int domainPort = -1;
		
		            try
		            {
		                domainPort = Int32.Parse(FindViewById<EditText>(Resource.Id.inputDomainPort).Text);
		            }
		            catch (Exception exception)
		            {
		                _log.Text += "Please provide a valid PORT number!\n";
		            }
		
		            if (!string.IsNullOrEmpty(displayName) && !string.IsNullOrEmpty(userName)
		                && !string.IsNullOrEmpty(authenticationId) && !string.IsNullOrEmpty(registerPassword)
		                && !string.IsNullOrEmpty(domainHost) && domainPort != -1)
		            {
		                /*
		                    In this part of the code we use the Xamarin.Essentials.Preferences
		                    to store our session details.
		                */
		
		                Xamarin.Essentials.Preferences.Set("display_name", displayName);
		                Xamarin.Essentials.Preferences.Set("user_name", userName);
		                Xamarin.Essentials.Preferences.Set("authentication_id", authenticationId);
		                Xamarin.Essentials.Preferences.Set("register_password", registerPassword);
		                Xamarin.Essentials.Preferences.Set("domain_host", domainHost);
		                Xamarin.Essentials.Preferences.Set("domain_port", domainPort);
		
		                StartActivity(typeof(DialingActivity));
		            }
		            else
		            {
		                _log.Text += "Please fill all the required fields!\n";
		            }
		        }
		
		        /*
		            This method tests if the user logged in, by testing if the
		            Xamarin.Essentials.Preferences contains the "display_name" key.
		            If it doesn't, it means that we do not have a stored session because
		            when we press the logout button in the MessaingActivity, it clears
		            the Preferences.
		        */
		
		        private void TestIfUserLoggedIn()
		        {
		            if (Xamarin.Essentials.Preferences.ContainsKey("display_name"))
		            {
		                StartActivity(typeof(DialingActivity));
		            }
		        }
		
		        public override bool OnCreateOptionsMenu(IMenu menu)
		        {
		            MenuInflater.Inflate(Resource.Menu.menu_main, menu);
		            return true;
		        }
		
		        public override bool OnOptionsItemSelected(IMenuItem item)
		        {
		            int id = item.ItemId;
		            if (id == Resource.Id.action_settings)
		            {
		                return true;
		            }
		
		            return base.OnOptionsItemSelected(item);
		        }
		
		        public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
		        {
		            Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
		
		            base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
		        }
		    }
		}
	

Code 4 - MainActivty.cs

Create the DialingActivity.cs file

This activity will be responsible for making calls with codecs. With the dropdown menu you can select the right encoding for you, and the softphopne will use it for the call. In the following video I'll show you how to create the DialingActivity activity.

Video 6 - Creating the DialingActivity activity

DialingActivty.cs

		using Android.App;
		using Android.OS;
		using Android.Widget;
		using Ozeki.Media;
		using Ozeki.VoIP;
		using System;
		using System.Collections.Generic;
		
		namespace _06_Codecs_handler
		{
		    [Activity(Label = "DialingActivity")]
		    public class DialingActivity : Activity
		    {
		        Softphone _mySoftphone;
		
		        TextView _status;
		        bool _registered;
		
		        Button _btnInteractions;
		        Button _btnZero;
		        Button _btnOne;
		        Button _btnTwo;
		        Button _btnThree;
		        Button _btnFour;
		        Button _btnFive;
		        Button _btnSix;
		        Button _btnSeven;
		        Button _btnEight;
		        Button _btnNine;
		        Button _btnStar;
		        Button _btnHashtag;
		        TextView _btnClear;
		        TextView _btnLogOut;
		
		        TextView _inputNumberToCall;
		
		        Spinner _spinner;
		        CallState _callState;
		        CodecInfo _actualCodec;
		
		        protected override void OnCreate(Bundle savedInstanceState)
		        {
		            base.OnCreate(savedInstanceState);
		            Xamarin.Essentials.Platform.Init(this, savedInstanceState);
		            SetContentView(Resource.Layout.activity_dialing);
		
		            InitSoftphone();
		            _registered = false; // It has to be initialized before the Register() method.
		            Register();
		            PopulateSpinner();
		
		            _btnInteractions = FindViewById<Button>(Resource.Id.btnInteractions);
		            _btnInteractions.Click += OnClick__btnInteractions;
		
		            _btnLogOut = FindViewById<TextView>(Resource.Id.btnLogOut);
		            _btnLogOut.Click += delegate { Xamarin.Essentials.Preferences.Clear(); StartActivity(typeof(MainActivity)); };
		
		            _btnClear = FindViewById<TextView>(Resource.Id.btnClear);
		            _btnClear.Click += delegate { _inputNumberToCall.Text = ""; };
		
		            _btnZero = FindViewById<Button>(Resource.Id.btnZero);
		            _btnZero.Click += delegate { _inputNumberToCall.Text += "0"; };
		
		            _btnOne = FindViewById<Button>(Resource.Id.btnOne);
		            _btnOne.Click += delegate { _inputNumberToCall.Text += "1"; };
		
		            _btnTwo = FindViewById<Button>(Resource.Id.btnTwo);
		            _btnTwo.Click += delegate { _inputNumberToCall.Text += "2"; };
		
		            _btnThree = FindViewById<Button>(Resource.Id.btnThree);
		            _btnThree.Click += delegate { _inputNumberToCall.Text += "3"; };
		
		            _btnFour = FindViewById<Button>(Resource.Id.btnFour);
		            _btnFour.Click += delegate { _inputNumberToCall.Text += "4"; };
		
		            _btnFive = FindViewById<Button>(Resource.Id.btnFive);
		            _btnFive.Click += delegate { _inputNumberToCall.Text += "5"; };
		
		            _btnSix = FindViewById<Button>(Resource.Id.btnSix);
		            _btnSix.Click += delegate { _inputNumberToCall.Text += "6"; };
		
		            _btnSeven = FindViewById<Button>(Resource.Id.btnSeven);
		            _btnSeven.Click += delegate { _inputNumberToCall.Text += "7"; };
		
		            _btnEight = FindViewById<Button>(Resource.Id.btnEight);
		            _btnEight.Click += delegate { _inputNumberToCall.Text += "8"; };
		
		            _btnNine = FindViewById<Button>(Resource.Id.btnNine);
		            _btnNine.Click += delegate { _inputNumberToCall.Text += "9"; };
		
		            _btnStar = FindViewById<Button>(Resource.Id.btnStar);
		            _btnStar.Click += delegate { _inputNumberToCall.Text += "*"; };
		
		            _btnHashtag = FindViewById<Button>(Resource.Id.btnHashtag);
		            _btnHashtag.Click += delegate { _inputNumberToCall.Text += "#"; };
		
		            _inputNumberToCall = FindViewById<TextView>(Resource.Id.inputNumberToCall);
		            _status = FindViewById<TextView>(Resource.Id.status);
		
		            _spinner = FindViewById<Spinner>(Resource.Id.spinner);
		        }
		
		        private void InitSoftphone()
		        {
		            _mySoftphone = new Softphone();
		            _mySoftphone.PhoneLineStateChanged += mySoftphone_PhoneLineStateChanged;
		            _mySoftphone.CallStateChanged += _mySoftphone_CallStateChanged;
		        }
		
		        private void OnClick__btnInteractions(object sender, EventArgs e)
		        {
		            if (_callState == CallState.Answered || _callState == CallState.InCall
		                || _callState == CallState.Ringing)
		            {
		                _mySoftphone.HangUp();
		
		
		                if (_actualCodec != null)
		                {
		                    _mySoftphone.DisableCodec(_actualCodec.PayloadType);
		                    _spinner.SetSelection(0);
		                }
		            }
		            else
		            {
		                string currentNumber = _inputNumberToCall.Text;
		                _inputNumberToCall.Text = "";
		
		                if (!string.IsNullOrEmpty(currentNumber) && _registered)
		                {
		                    if (_spinner.SelectedItem.ToString() != "None")
		                    {
		                        foreach (var codec in _mySoftphone.Codecs())
		                        {
		                            if (codec.CodecName == _spinner.SelectedItem.ToString())
		                            {
		                                _mySoftphone.EnableCodec(codec.PayloadType);
		                                _actualCodec = codec;
		                            }
		                        }
		                    }
		
		                    _mySoftphone.StartCall(currentNumber);
		                }
		            }
		        }
		
		        /*
		            This method will fill the dropdown menu, that contains the name of
		            the aviable codecs.
		        */
		
		        private void PopulateSpinner()
		        {
		            Spinner spinner = FindViewById<Spinner>(Resource.Id.spinner);
		            ArrayAdapter<string> adapter = new ArrayAdapter<string>(this,
		                Android.Resource.Layout.SimpleSpinnerItem, getCodecs());
		            adapter.SetDropDownViewResource(Android.Resource.Layout.SimpleSpinnerDropDownItem);
		            spinner.Adapter = adapter;
		        }
		
		        /*
		            This method will collect the name of all the aviable codecs. 
		        */
		
		        private List<string> getCodecs()
		        {
		            List<string> codecs = new List<string>();
		            codecs.Add("None");
		            foreach(var codec in _mySoftphone.Codecs())
		            {
		                if (codec.MediaType == CodecMediaType.Audio)
		                {
		                    codecs.Add(codec.CodecName);
		                }
		            }
		
		            return codecs;
		        }
		
		        /*
		            Everytime the PhoneLineState changes this method will test, what is the
		            current PhoneLineState.
		        */
		
		        private void mySoftphone_PhoneLineStateChanged(object sender, RegistrationStateChangedArgs e)
		        {
		            if (e.State == RegState.Error || e.State == RegState.NotRegistered)
		            {
		                _registered = false;
		                _status.Text = "Not registered.";
		            }
		            else if (e.State == RegState.RegistrationSucceeded)
		            {
		                _registered = true;
		                _status.Text = "Registered.";
		            }
		        }
		
		        /*
		            Everytime the CallState changes this method will test, what is the
		            current CallState.
		        */
		
		        private void _mySoftphone_CallStateChanged(object sender, CallStateChangedArgs e)
		        {
		            _callState = e.State;
		
		            if (_callState == CallState.Answered || _callState == CallState.InCall
		                || _callState == CallState.Setup || _callState == CallState.Ringing)
		            {
		                _btnInteractions.Text = "Hang up";
		            }
		            else
		            {
		                _btnInteractions.Text = "Dial";
		            }
		        }
		
		
		        /*
		            The Register method will issue a SIP registration to the Ozeki Phone
		            System.
		        */
		
		        private void Register()
		        {
		            bool registrationRequired = true;
		            string displayName = Xamarin.Essentials.Preferences.Get("display_name", "");
		            string userName = Xamarin.Essentials.Preferences.Get("user_name", "");
		            string authenticationId = Xamarin.Essentials.Preferences.Get("authentication_id", "");
		            string registerPassword = Xamarin.Essentials.Preferences.Get("register_password", "");
		            string domainHost = Xamarin.Essentials.Preferences.Get("domain_host", "");
		            int domainPort = Xamarin.Essentials.Preferences.Get("domain_port", 5060);
		
		            _mySoftphone.Register(registrationRequired, displayName, userName, authenticationId,
		                registerPassword, domainHost, domainPort);
		        }
		
		        /*
		            This function overwrites the OnBackPressed() method of the class
		            so if you press the back button, you will not be able to go back to
		            the previous page. The only way you can go back to the registration
		            page is if you press the logout button.
		        */
		
		        public override void OnBackPressed()
		        {
		            return;
		        }
		
		    }
		}
	

Code 5 - DialingActivity.cs

How to ask for microphone permission

If you want to build a sopftphone application, you will need to ask for microphone permission for your application. In the following video I'll show you, how to allow the microphone in your Xamarin Android application.

Video 7 - How to make your application ask for microphone permission

how to enable microphone for you android application in xamarin
Figure 5 - Select the Android Manifest tab

how to enable microphone for you android application in xamarin
Figure 6 - Search for the RECORD_AUDIO option

how to enable microphone for you android application in xamarin
Figure 7 - Check the checkbox

Running the Softphone application

After the softphone is ready we can start debugging the application on an Android device with a version that's higher than Android 8.0. If we don't have an android device we can create an android emulator in the Visual Studio Community. In the following video you will also see, how to allow microphone for your application, and how to test if the codec is working.

Video 8 - Running the example application

Figure 8 - The codecs handler example application

Figure 9 - The call using the codec