audio and video call in android studio
audio and video call in android studio

In this post, I will create simple android app for audio calling and video call (app to app) using Sinch SDK. Before going forward, I want to tell you something important that is logic, I will be using for video call services for both audio and video call the difference is that i will not allow to video streaming for audio call initially, later user can allow video streaming. The benefit of using this logic is that you can easily switch from audio to video call during a call. If you use ordinary way, like for audio call you use audio services and for video call you use video service, so the problem of doing that is no switching from audio to video call during a call, Sinch SDK not allowing you that.



Advantage of using Sinch SDK for Audio and Video calling


Sinch is basically a product which is used for video, audio calling and instant messaging over 150 countries and it is supported in android, IOS and web. The good thing about it, is that it saves a lot of time in writing a code for audio management, handling complexity of signals, video management and networking, it contain build in functions to use loud speaker as on or off and mute the your voice or unmute, also you can handle your camera in video call, you can do all these complicated things in just 2 line of code. The other good thing about it is that it give 2500+ minutes free per month, which is very good for testing and also use.





How to Install Sinch SDK in your Android Studio (Sep 2019 method)



1. First Download (Sinch ARR File for android) this file and put this file lib directory  of your android studio project.


2.Open build gradle and add this line of code in dependency ( "implementation(name: 'sinch-android-rtc',version'+', ext: 'aar')")

audio and video in android part2
audio and video in android part2


3.Open build gradle as project and add this line of code in repositories ( " flatDir { dirs 'libs'} ").

audio and video in android part2
audio and video in android part3



4. Now Sync your android project to install Sinch sdk successfully. 




Importants things you must know about Sinch SDK.


First, in order to use Sinch services, you must bounded background services otherwise it will not work properly, background service will help you to run app functionality when your app is sleeping or destroyed. Other important thing is that sinch have its own database of user id. Suppose if your app have 5 users then you have initialize 5 different user id during creating sinch service, the best way is that when user login to app then initialize unique id for that user. You can call to other user this unique id, it is identifier for different users.


Practical implementation


Click on Tablayout, to view a code, First you have to create Sinch Service class and BaseActivity class. Sinch Service class is for using sinch services in background services and BaseActivity class is basically a interface which is used in other classes like Main Activity, Video call.



View Code

Below, you will see different buttons, these are basically different classes, click on button to view a code.


SinchService

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;

import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.ValueEventListener;
import com.saifyproduction.sunupayApp.view.Incoming_voice_call;
import com.saifyproduction.sunupayApp.view.incoming_video_call;
import com.sinch.android.rtc.AudioController;
import com.sinch.android.rtc.ClientRegistration;
import com.sinch.android.rtc.Sinch;
import com.sinch.android.rtc.SinchClient;
import com.sinch.android.rtc.SinchClientListener;
import com.sinch.android.rtc.SinchError;
import com.sinch.android.rtc.calling.Call;
import com.sinch.android.rtc.calling.CallClient;
import com.sinch.android.rtc.calling.CallClientListener;
import com.sinch.android.rtc.video.VideoController;

import androidx.annotation.NonNull;

public class SinchService extends Service {

    private static final String APP_KEY = "";
    private static final String APP_SECRET = "";
    private static final String ENVIRONMENT = "";
    static int state  ;


    public static final String CALL_ID = "CALL_ID";
    static final String TAG = SinchService.class.getSimpleName();

    private SinchServiceInterface mSinchServiceInterface = new SinchServiceInterface();
    private SinchClient mSinchClient;
    private String mUserId;

    private StartFailedListener mListener;

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public void onDestroy() {
        if (mSinchClient != null && mSinchClient.isStarted()) {
            mSinchClient.terminate();
        }
        super.onDestroy();
    }

    private void start(String userName) {
        if (mSinchClient == null) {
            mUserId = userName;
            mSinchClient = Sinch.getSinchClientBuilder().context(getApplicationContext()).userId(userName)
                    .applicationKey(APP_KEY)
                    .applicationSecret(APP_SECRET)
                    .environmentHost(ENVIRONMENT).build();

            mSinchClient.setSupportCalling(true);
            mSinchClient.startListeningOnActiveConnection();

            mSinchClient.addSinchClientListener(new MySinchClientListener());
            mSinchClient.getCallClient().addCallClientListener(new SinchCallClientListener());
            mSinchClient.start();
        }
    }

    private void stop() {
        if (mSinchClient != null) {
            mSinchClient.terminate();
            mSinchClient = null;
        }
    }

    private boolean isStarted() {
        return (mSinchClient != null && mSinchClient.isStarted());
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mSinchServiceInterface;
    }

    public class SinchServiceInterface extends Binder {

        public Call callUserVideo(String userId, int i ) {

            state  = i;
            return mSinchClient.getCallClient().callUserVideo(userId);
        }


        public Call callUseraudio(String userId) {

            return mSinchClient.getCallClient().callUser(userId);
        }




        public String getUserName() {
            return mUserId;
        }

        public boolean isStarted() {
            return SinchService.this.isStarted();
        }

        public void startClient(String userName) {
            start(userName);
        }

        public void stopClient() {
            stop();
        }

        public void setStartListener(StartFailedListener listener) {
            mListener = listener;
        }

        public Call getCall(String callId) {
            return mSinchClient.getCallClient().getCall(callId);
        }

        public VideoController getVideoController() {
            if (!isStarted()) {
                return null;
            }
            return mSinchClient.getVideoController();
        }

        public AudioController getAudioController() {
            if (!isStarted()) {
                return null;
            }
            return mSinchClient.getAudioController();
        }
    }

    public interface StartFailedListener {

        void onStartFailed(SinchError error);

        void onStarted();
    }

    private class MySinchClientListener implements SinchClientListener {

        @Override
        public void onClientFailed(SinchClient client, SinchError error) {
            if (mListener != null) {
                mListener.onStartFailed(error);
            }
            mSinchClient.terminate();
            mSinchClient = null;
        }

        @Override
        public void onClientStarted(SinchClient client) {
            Log.d(TAG, "SinchClient started");
            if (mListener != null) {
                mListener.onStarted();
            }
        }

        @Override
        public void onClientStopped(SinchClient client) {
            Log.d(TAG, "SinchClient stopped");
        }

        @Override
        public void onLogMessage(int level, String area, String message) {
            switch (level) {
                case Log.DEBUG:
                    Log.d(area, message);
                    break;
                case Log.ERROR:
                    Log.e(area, message);
                    break;
                case Log.INFO:
                    Log.i(area, message);
                    break;
                case Log.VERBOSE:
                    Log.v(area, message);
                    break;
                case Log.WARN:
                    Log.w(area, message);
                    break;
            }
        }

        @Override
        public void onRegistrationCredentialsRequired(SinchClient client,
                                                      ClientRegistration clientRegistration) {
        }
    }

    private class SinchCallClientListener implements CallClientListener {

        @Override
        public void onIncomingCall(CallClient callClient, final Call call) {


            FirebaseAuth auth = FirebaseAuth.getInstance();
            DatabaseReference cal = FirebaseDatabase.getInstance().getReference().child("call_detail").child(auth.getCurrentUser().getUid()).child(call.getCallId());


            cal.addListenerForSingleValueEvent(new ValueEventListener() {
                @Override
                public void onDataChange(@NonNull DataSnapshot dataSnapshot) {



                    if(dataSnapshot.exists()){

                       String cl = dataSnapshot.child("calltype").getValue(String.class);

                        if( cl.equalsIgnoreCase("audio") ){


                            Log.d(TAG, "Incoming call");
                            Intent intent = new Intent(SinchService.this, Incoming_voice_call.class);
                            intent.putExtra(CALL_ID, call.getCallId());
                            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                            SinchService.this.startActivity(intent);



                        }
                        else{


                            Log.d(TAG, "Incoming call");
                            Intent intent = new Intent(SinchService.this, incoming_video_call.class);
                            intent.putExtra(CALL_ID, call.getCallId());
                            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                            SinchService.this.startActivity(intent);



                        }


                    }


                }

                @Override
                public void onCancelled(@NonNull DatabaseError databaseError) {

                }
            });



        }
    }

}

BaseActivity

import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;

import com.saifyproduction.sunupayApp.controller.SinchService;

import androidx.appcompat.app.AppCompatActivity;

public abstract class BaseActivity extends AppCompatActivity implements ServiceConnection {

    private SinchService.SinchServiceInterface mSinchServiceInterface;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getApplicationContext().bindService(new Intent(this, SinchService.class), this,
                BIND_AUTO_CREATE);
    }

    @Override
    public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
        if (SinchService.class.getName().equals(componentName.getClassName())) {
            mSinchServiceInterface = (SinchService.SinchServiceInterface) iBinder;
            onServiceConnected();
        }
    }

    @Override
    public void onServiceDisconnected(ComponentName componentName) {
        if (SinchService.class.getName().equals(componentName.getClassName())) {
            mSinchServiceInterface = null;
            onServiceDisconnected();
        }
    }

    protected void onServiceConnected() {
        // for subclasses
    }

    protected void onServiceDisconnected() {
        // for subclasses
    }

    protected SinchService.SinchServiceInterface getSinchServiceInterface() {
        return mSinchServiceInterface;
    }

}

incoming_video_call

import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;

import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.ValueEventListener;
import com.saifyproduction.sunupayApp.R;
import com.saifyproduction.sunupayApp.controller.AudioPlayer;
import com.saifyproduction.sunupayApp.controller.SinchService;
import com.saifyproduction.sunupayApp.model.callhistory;
import com.sinch.android.rtc.PushPair;
import com.sinch.android.rtc.calling.Call;
import com.sinch.android.rtc.calling.CallEndCause;
import com.sinch.android.rtc.video.VideoCallListener;
import com.squareup.picasso.Picasso;
import com.squareup.picasso.Target;

import java.util.List;

import androidx.annotation.NonNull;

public class incoming_video_call extends BaseActivity {


    static final String TAG = incoming_video_call.class.getSimpleName();
    private String mCallId;
    private AudioPlayer mAudioPlayer;
    ImageView img, circleimage;

    TextView callstate, rejecttxt, calltxt, msgtxt;

    ImageView callattend,callreject, callmsg ;

    String fid ;

    callhistory history;

    FirebaseAuth auth = FirebaseAuth.getInstance();


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_incoming_video_call);


        callattend = (ImageView) findViewById(R.id.attend_call);
        callreject = (ImageView) findViewById(R.id.reject_call);
        callmsg = (ImageView) findViewById(R.id.call_msg);


        calltxt = (TextView) findViewById(R.id.call_txt);
        rejecttxt = (TextView) findViewById(R.id.reject_txt);
        msgtxt = (TextView) findViewById(R.id.msg_txt);


        callattend.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                answerClicked();

            }
        });


        callreject.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                declineClicked();

            }
        });


        callmsg.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {



            }
        });


        img = (ImageView)findViewById(R.id.pic22);
        circleimage = (ImageView)findViewById(R.id.displayimage);

        callstate = (TextView) findViewById(R.id.callState);

        mAudioPlayer = new AudioPlayer(this);
        mAudioPlayer.playRingtone();
        mCallId = getIntent().getStringExtra(SinchService.CALL_ID);
        callstate.setText("Callling");



    }





    public void getcalldetail(String id){


        DatabaseReference connectedRef = FirebaseDatabase.getInstance().getReference().child("call_detail").child(auth.getCurrentUser().getUid()).child(id);
        connectedRef.addListenerForSingleValueEvent(new ValueEventListener() {
            @Override
            public void onDataChange(@NonNull DataSnapshot snapshot) {



                if(snapshot.exists()){


                    history = snapshot.getValue(callhistory.class);

                }

            }

            @Override
            public void onCancelled(@NonNull DatabaseError error) {

            }
        });


    }



    @Override
    protected void onServiceConnected() {
        Call call = getSinchServiceInterface().getCall(mCallId);
        if (call != null) {
            call.addCallListener(new SinchCallListener());
            final TextView remoteUser = (TextView) findViewById(R.id.remote_user);


            DatabaseReference connectedRef = FirebaseDatabase.getInstance().getReference().child("user").child(call.getRemoteUserId());
            connectedRef.addListenerForSingleValueEvent(new ValueEventListener() {
                @Override
                public void onDataChange(@NonNull DataSnapshot snapshot) {


                    if(snapshot.exists()) {

                        System.out.println("key === "+snapshot.getKey());
                        System.out.println("key === "+snapshot.getChildrenCount());

                        String s = snapshot.child("name").getValue(String.class);
                        String u = snapshot.child("url").getValue(String.class);
                        remoteUser.setText(s);

                        Picasso.get().load(u).placeholder(R.drawable.profile_image).into(img);
                        Picasso.get().load(u).placeholder(R.drawable.profile_image).into(circleimage);
                        getcalldetail(mCallId);


                        callstate.setText("Ringing");
                    }


                }

                @Override
                public void onCancelled(@NonNull DatabaseError error) {

                }
            });



        } else {
            Log.e(TAG, "Started with invalid callId, aborting");
            finish();
        }
    }

    private void answerClicked() {



        history.setStatus("pick");
        DatabaseReference callRef = FirebaseDatabase.getInstance().getReference().child("call_detail").child(auth.getCurrentUser().getUid()).child(mCallId);


        callRef.setValue(history);



        mAudioPlayer.stopRingtone();
        Call call = getSinchServiceInterface().getCall(mCallId);
        if (call != null) {
            call.answer();
            Intent intent = new Intent(this, VideoCall.class);
            intent.putExtra(SinchService.CALL_ID, mCallId);
            startActivity(intent);
        } else {
            finish();
        }
    }

    private void declineClicked() {


        history.setStatus("reject");

        DatabaseReference callRef = FirebaseDatabase.getInstance().getReference().child("call_detail").child(auth.getCurrentUser().getUid()).child(mCallId);
        callRef.setValue(history);


        mAudioPlayer.stopRingtone();
        Call call = getSinchServiceInterface().getCall(mCallId);
        if (call != null) {
            call.hangup();
        }
        finish();
    }

    private class SinchCallListener implements VideoCallListener {

        @Override
        public void onCallEnded(Call call) {
            CallEndCause cause = call.getDetails().getEndCause();
            Log.d(TAG, "Call ended, cause: " + cause.toString());
            mAudioPlayer.stopRingtone();
            finish();
        }

        @Override
        public void onCallEstablished(Call call) {
            Log.d(TAG, "Call established");
        }

        @Override
        public void onCallProgressing(Call call) {
            Log.d(TAG, "Call progressing");
        }

        @Override
        public void onShouldSendPushNotification(Call call, List<PushPair> pushPairs) {
            // Send a push through your push provider here, e.g. GCM
        }

        @Override
        public void onVideoTrackAdded(Call call) {
            // Display some kind of icon showing it's a video call
        }

        @Override
        public void onVideoTrackPaused(Call call) {

        }

        @Override
        public void onVideoTrackResumed(Call call) {

        }
    }

    private View.OnClickListener mClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.attend_call:
                    answerClicked();
                    break;
                case R.id.reject_call:
                    declineClicked();
                    break;
            }
        }
    };




}

SinchService

import android.content.ClipData;
import android.content.ClipDescription;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.Log;
import android.view.DragEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.ValueEventListener;
import com.saifyproduction.sunupayApp.R;
import com.saifyproduction.sunupayApp.controller.AudioPlayer;
import com.saifyproduction.sunupayApp.controller.SinchService;
import com.saifyproduction.sunupayApp.model.callhistory;
import com.sinch.android.rtc.PushPair;
import com.sinch.android.rtc.calling.Call;
import com.sinch.android.rtc.calling.CallEndCause;
import com.sinch.android.rtc.calling.CallListener;
import com.sinch.android.rtc.video.VideoCallListener;
import com.squareup.picasso.Picasso;
import com.squareup.picasso.Target;

import java.util.List;

import androidx.annotation.NonNull;
import androidx.constraintlayout.widget.ConstraintLayout;

public class Incoming_voice_call extends BaseActivity {



    static final String TAG = Incoming_voice_call.class.getSimpleName();
    private String mCallId;
    private AudioPlayer mAudioPlayer;
    ImageView img, circleimage;

    TextView callstate, rejecttxt, calltxt, msgtxt;

    ImageView callattend,callreject, callmsg ;

    String fid ;

    callhistory history;

    FirebaseAuth auth = FirebaseAuth.getInstance();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_incoming_voice_call);

        callattend = (ImageView) findViewById(R.id.attend_call);
        callreject = (ImageView) findViewById(R.id.reject_call);
        callmsg = (ImageView) findViewById(R.id.call_msg);


        calltxt = (TextView) findViewById(R.id.call_txt);
        rejecttxt = (TextView) findViewById(R.id.reject_txt);
        msgtxt = (TextView) findViewById(R.id.msg_txt);


        callattend.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                answerClicked();

            }
        });


        callreject.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                declineClicked();

            }
        });


        callmsg.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {



            }
        });


        img = (ImageView)findViewById(R.id.pic22);
        circleimage = (ImageView)findViewById(R.id.displayimage);

        callstate = (TextView) findViewById(R.id.callState);

        mAudioPlayer = new AudioPlayer(this);
        mAudioPlayer.playRingtone();
        mCallId = getIntent().getStringExtra(SinchService.CALL_ID);
        callstate.setText("Callling");



    }








    @Override
    protected void onServiceConnected() {
        Call call = getSinchServiceInterface().getCall(mCallId);
        if (call != null) {
            call.addCallListener(new SinchCallListener());
            final TextView remoteUser = (TextView) findViewById(R.id.remote_user);



            DatabaseReference connectedRef = FirebaseDatabase.getInstance().getReference().child("user").child(call.getRemoteUserId());
            connectedRef.addListenerForSingleValueEvent(new ValueEventListener() {
                @Override
                public void onDataChange(@NonNull DataSnapshot snapshot) {


                    String s = snapshot.child("name").getValue(String.class);
                    String u = snapshot.child("url").getValue(String.class);
                    fid = snapshot.child("uid").getValue(String.class);
                    remoteUser.setText(s);
                    callstate.setText("Ringing");
                    Picasso.get().load(u).placeholder(R.drawable.profile_image).into(img);
                    Picasso.get().load(u).placeholder(R.drawable.profile_image).into(circleimage);
                    getcalldetail(mCallId);


                }

                @Override
                public void onCancelled(@NonNull DatabaseError error) {

                }
            });


        } else {
            Log.e(TAG, "Started with invalid callId, aborting");
            finish();
        }
    }


    public void getcalldetail(String id){


        DatabaseReference connectedRef = FirebaseDatabase.getInstance().getReference().child("call_detail").child(auth.getCurrentUser().getUid()).child(id);
        connectedRef.addListenerForSingleValueEvent(new ValueEventListener() {
            @Override
            public void onDataChange(@NonNull DataSnapshot snapshot) {



                if(snapshot.exists()){


                    history = snapshot.getValue(callhistory.class);

                }

            }

            @Override
            public void onCancelled(@NonNull DatabaseError error) {

            }
        });


    }

    private void answerClicked() {


        history.setStatus("pick");
        DatabaseReference callRef = FirebaseDatabase.getInstance().getReference().child("call_detail").child(auth.getCurrentUser().getUid()).child(mCallId);


        callRef.setValue(history);

        mAudioPlayer.stopRingtone();
        Call call = getSinchServiceInterface().getCall(mCallId);
        if (call != null) {
            call.answer();
            finish();
            Intent intent = new Intent(this, VoiceCall.class);
            intent.putExtra(SinchService.CALL_ID, mCallId);
            startActivity(intent);
        } else {
            finish();
        }
    }

    private void declineClicked() {

        history.setStatus("reject");

        DatabaseReference callRef = FirebaseDatabase.getInstance().getReference().child("call_detail").child(auth.getCurrentUser().getUid()).child(mCallId);
        callRef.setValue(history);

        mAudioPlayer.stopRingtone();
        Call call = getSinchServiceInterface().getCall(mCallId);
        if (call != null) {
            call.hangup();
        }
        finish();
    }

    private class SinchCallListener implements VideoCallListener {

        @Override
        public void onCallEnded(Call call) {
            CallEndCause cause = call.getDetails().getEndCause();
            Log.d(TAG, "Call ended, cause: " + cause.toString());
            mAudioPlayer.stopRingtone();
            finish();
        }

        @Override
        public void onCallEstablished(Call call) {
            Log.d(TAG, "Call established");
        }

        @Override
        public void onCallProgressing(Call call) {
            Log.d(TAG, "Call progressing");

            mAudioPlayer.playRingtone();
        }

        @Override
        public void onShouldSendPushNotification(Call call, List<PushPair> pushPairs) {
            // Send a push through your push provider here, e.g. GCM
        }


        @Override
        public void onVideoTrackAdded(Call call) {

        }

        @Override
        public void onVideoTrackPaused(Call call) {

        }

        @Override
        public void onVideoTrackResumed(Call call) {

        }
    }




}

Incoming_voice_call

import android.content.ClipData;
import android.content.ClipDescription;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.Log;
import android.view.DragEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.ValueEventListener;
import com.saifyproduction.sunupayApp.R;
import com.saifyproduction.sunupayApp.controller.AudioPlayer;
import com.saifyproduction.sunupayApp.controller.SinchService;
import com.saifyproduction.sunupayApp.model.callhistory;
import com.sinch.android.rtc.PushPair;
import com.sinch.android.rtc.calling.Call;
import com.sinch.android.rtc.calling.CallEndCause;
import com.sinch.android.rtc.calling.CallListener;
import com.sinch.android.rtc.video.VideoCallListener;
import com.squareup.picasso.Picasso;
import com.squareup.picasso.Target;

import java.util.List;

import androidx.annotation.NonNull;
import androidx.constraintlayout.widget.ConstraintLayout;

public class Incoming_voice_call extends BaseActivity {



    static final String TAG = Incoming_voice_call.class.getSimpleName();
    private String mCallId;
    private AudioPlayer mAudioPlayer;
    ImageView img, circleimage;

    TextView callstate, rejecttxt, calltxt, msgtxt;

    ImageView callattend,callreject, callmsg ;

    String fid ;

    callhistory history;

    FirebaseAuth auth = FirebaseAuth.getInstance();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_incoming_voice_call);

        callattend = (ImageView) findViewById(R.id.attend_call);
        callreject = (ImageView) findViewById(R.id.reject_call);
        callmsg = (ImageView) findViewById(R.id.call_msg);


        calltxt = (TextView) findViewById(R.id.call_txt);
        rejecttxt = (TextView) findViewById(R.id.reject_txt);
        msgtxt = (TextView) findViewById(R.id.msg_txt);


        callattend.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                answerClicked();

            }
        });


        callreject.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                declineClicked();

            }
        });


        callmsg.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {



            }
        });


        img = (ImageView)findViewById(R.id.pic22);
        circleimage = (ImageView)findViewById(R.id.displayimage);

        callstate = (TextView) findViewById(R.id.callState);

        mAudioPlayer = new AudioPlayer(this);
        mAudioPlayer.playRingtone();
        mCallId = getIntent().getStringExtra(SinchService.CALL_ID);
        callstate.setText("Callling");



    }








    @Override
    protected void onServiceConnected() {
        Call call = getSinchServiceInterface().getCall(mCallId);
        if (call != null) {
            call.addCallListener(new SinchCallListener());
            final TextView remoteUser = (TextView) findViewById(R.id.remote_user);



            DatabaseReference connectedRef = FirebaseDatabase.getInstance().getReference().child("user").child(call.getRemoteUserId());
            connectedRef.addListenerForSingleValueEvent(new ValueEventListener() {
                @Override
                public void onDataChange(@NonNull DataSnapshot snapshot) {


                    String s = snapshot.child("name").getValue(String.class);
                    String u = snapshot.child("url").getValue(String.class);
                    fid = snapshot.child("uid").getValue(String.class);
                    remoteUser.setText(s);
                    callstate.setText("Ringing");
                    Picasso.get().load(u).placeholder(R.drawable.profile_image).into(img);
                    Picasso.get().load(u).placeholder(R.drawable.profile_image).into(circleimage);
                    getcalldetail(mCallId);


                }

                @Override
                public void onCancelled(@NonNull DatabaseError error) {

                }
            });


        } else {
            Log.e(TAG, "Started with invalid callId, aborting");
            finish();
        }
    }


    public void getcalldetail(String id){


        DatabaseReference connectedRef = FirebaseDatabase.getInstance().getReference().child("call_detail").child(auth.getCurrentUser().getUid()).child(id);
        connectedRef.addListenerForSingleValueEvent(new ValueEventListener() {
            @Override
            public void onDataChange(@NonNull DataSnapshot snapshot) {



                if(snapshot.exists()){


                    history = snapshot.getValue(callhistory.class);

                }

            }

            @Override
            public void onCancelled(@NonNull DatabaseError error) {

            }
        });


    }

    private void answerClicked() {


        history.setStatus("pick");
        DatabaseReference callRef = FirebaseDatabase.getInstance().getReference().child("call_detail").child(auth.getCurrentUser().getUid()).child(mCallId);


        callRef.setValue(history);

        mAudioPlayer.stopRingtone();
        Call call = getSinchServiceInterface().getCall(mCallId);
        if (call != null) {
            call.answer();
            finish();
            Intent intent = new Intent(this, VoiceCall.class);
            intent.putExtra(SinchService.CALL_ID, mCallId);
            startActivity(intent);
        } else {
            finish();
        }
    }

    private void declineClicked() {

        history.setStatus("reject");

        DatabaseReference callRef = FirebaseDatabase.getInstance().getReference().child("call_detail").child(auth.getCurrentUser().getUid()).child(mCallId);
        callRef.setValue(history);

        mAudioPlayer.stopRingtone();
        Call call = getSinchServiceInterface().getCall(mCallId);
        if (call != null) {
            call.hangup();
        }
        finish();
    }

    private class SinchCallListener implements VideoCallListener {

        @Override
        public void onCallEnded(Call call) {
            CallEndCause cause = call.getDetails().getEndCause();
            Log.d(TAG, "Call ended, cause: " + cause.toString());
            mAudioPlayer.stopRingtone();
            finish();
        }

        @Override
        public void onCallEstablished(Call call) {
            Log.d(TAG, "Call established");
        }

        @Override
        public void onCallProgressing(Call call) {
            Log.d(TAG, "Call progressing");

            mAudioPlayer.playRingtone();
        }

        @Override
        public void onShouldSendPushNotification(Call call, List<PushPair> pushPairs) {
            // Send a push through your push provider here, e.g. GCM
        }


        @Override
        public void onVideoTrackAdded(Call call) {

        }

        @Override
        public void onVideoTrackPaused(Call call) {

        }

        @Override
        public void onVideoTrackResumed(Call call) {

        }
    }




}

VideoCall

 
import android.media.AudioManager;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;

import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.database.ChildEventListener;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.Query;
import com.google.firebase.database.ValueEventListener;
import com.saifyproduction.sunupayApp.R;
import com.saifyproduction.sunupayApp.controller.AudioPlayer;
import com.saifyproduction.sunupayApp.controller.SinchService;
import com.sinch.android.rtc.AudioController;
import com.sinch.android.rtc.PushPair;
import com.sinch.android.rtc.calling.Call;
import com.sinch.android.rtc.calling.CallEndCause;
import com.sinch.android.rtc.calling.CallState;
import com.sinch.android.rtc.video.VideoCallListener;
import com.sinch.android.rtc.video.VideoController;
import com.squareup.picasso.Picasso;

import java.util.List;
import java.util.Locale;
import java.util.Timer;
import java.util.TimerTask;

import androidx.annotation.NonNull;
import androidx.constraintlayout.widget.ConstraintLayout;

public class VideoCall extends BaseActivity {


    static final String TAG = VideoCall.class.getSimpleName();

    static final String CALL_START_TIME = "callStartTime";
    static final String ADDED_LISTENER = "addedListener";

    private AudioPlayer mAudioPlayer;
    private Timer mTimer;
    private UpdateCallDurationTask mDurationTask;

    private String mCallId;
    private long mCallStart = 0;
    private boolean mAddedListener = false;
    private boolean mVideoViewsAdded = false;

    private TextView mCallDuration;
    private TextView mCallState;
    private TextView mCallerName;

    ImageView img, loudspeaker, camera , mic;

    int loud_flag =0 , cam_flag =0 , mic_flag =0;

    ImageView camera_change, video_request_accept, video_request_reject;

    TextView connection;

    int state =0;

    int recieved_flag = 0;

    boolean localview = false ;
    boolean remoteview = false ;

    DatabaseReference call_ref = FirebaseDatabase.getInstance().getReference().child("Call");
    FirebaseAuth auth  = FirebaseAuth.getInstance();



    private class UpdateCallDurationTask extends TimerTask {

        @Override
        public void run() {
            VideoCall.this.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    updateCallDuration();
                }
            });
        }
    }


    @Override
    protected void onSaveInstanceState(Bundle savedInstanceState) {
        super.onSaveInstanceState(savedInstanceState);
        savedInstanceState.putLong(CALL_START_TIME, mCallStart);
        savedInstanceState.putBoolean(ADDED_LISTENER, mAddedListener);
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        mCallStart = savedInstanceState.getLong(CALL_START_TIME);
        mAddedListener = savedInstanceState.getBoolean(ADDED_LISTENER);
    }


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_voice_call);


        mAudioPlayer = new AudioPlayer(this);
        mCallDuration = (TextView) findViewById(R.id.callDuration);
        mCallerName = (TextView) findViewById(R.id.remoteUser);
        mCallState = (TextView) findViewById(R.id.callState);
        Button endCallButton = (Button) findViewById(R.id.hangup);


        img = (ImageView)findViewById(R.id.person_img);

        camera_change = (ImageView)findViewById(R.id.camera_chnage);

        video_request_accept = (ImageView)findViewById(R.id.acceptvideoreject);
        video_request_reject = (ImageView)findViewById(R.id.rejectvideorequest);

        connection = (TextView) findViewById(R.id.connection);

        video_request_accept.setVisibility(View.GONE);
        video_request_reject.setVisibility(View.GONE);



        endCallButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                endCall();
            }
        });

        mCallId = getIntent().getStringExtra(SinchService.CALL_ID);
        if (savedInstanceState == null) {
            mCallStart = System.currentTimeMillis();
        }

        mCallState.setText("Calling");



        loudspeaker = (ImageView) findViewById(R.id.loudspeaker);
        camera = (ImageView) findViewById(R.id.camera);
        mic = (ImageView) findViewById(R.id.mic);



        video_request_accept.setVisibility(View.GONE);
        video_request_reject.setVisibility(View.GONE);
        connection.setText("");


        loudspeaker.setBackground(getResources().getDrawable(R.drawable.phonecircle));



        video_request_accept.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                Call call = getSinchServiceInterface().getCall(mCallId);

                cam_flag = 1;


                img.setVisibility(View.GONE);

                addlocalView();
                addRemoteView();
                video_request_accept.setVisibility(View.GONE);
                video_request_reject.setVisibility(View.GONE);
                connection.setText("");



                camera.setBackground(getResources().getDrawable(R.drawable.phonecircle));


                call_ref.child(mCallId).child(auth.getCurrentUser().getUid()).setValue(1);
                call_ref.child(mCallId).child(call.getRemoteUserId()).setValue(1);
                call_ref.child(mCallId).child("active").setValue(1);







            }
        });


        video_request_reject.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {


                video_request_accept.setVisibility(View.GONE);
                video_request_reject.setVisibility(View.GONE);
                connection.setText("");

                removelocalview();
                removeremoteview();

                img.setVisibility(View.VISIBLE);
                cam_flag = 0;


                camera.setBackground(getResources().getDrawable(R.drawable.trans));



                Call call = getSinchServiceInterface().getCall(mCallId);
                call_ref.child(mCallId).child(auth.getCurrentUser().getUid()).setValue(0);
                call_ref.child(mCallId).child(call.getRemoteUserId()).setValue(0);

                call_ref.child(mCallId).child("active").setValue(0);






            }
        });





        Query query = FirebaseDatabase.getInstance()
                .getReference()
                .child("Call").child(mCallId);


        ChildEventListener childEventListener = new ChildEventListener() {
            @Override
            public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
                // ...

            }

            @Override
            public void onChildChanged(DataSnapshot dataSnapshot, String previousChildName) {
                // ...



                System.out.println("On child changed");

                DatabaseReference connectedRef = FirebaseDatabase.getInstance().getReference().child("Call").child(mCallId);
                connectedRef.addListenerForSingleValueEvent(new ValueEventListener() {
                    @Override
                    public void onDataChange(@NonNull DataSnapshot snapshot) {

                        System.out.println("On Enter");


                        if(snapshot.exists()){

                            System.out.println("On Data Exist");


                            Call call = getSinchServiceInterface().getCall(mCallId);

                            int me = snapshot.child(auth.getCurrentUser().getUid()).getValue(Integer.class);
                            int other = snapshot.child(call.getRemoteUserId()).getValue(Integer.class);
                            int active = snapshot.child("active").getValue(Integer.class);

                            System.out.println("me  == "+me +"\n"+ " other == "+other +"\n active == "+active);

                            if (active == 0) {


                                if (me == 0 && other == 0) {


                                    video_request_accept.setVisibility(View.GONE);
                                    video_request_reject.setVisibility(View.GONE);
                                    connection.setText("");

                                    camera.setBackground(getResources().getDrawable(R.drawable.trans));


                                    removelocalview();
                                    removeremoteview();

                                    cam_flag = 0;

                                    img.setVisibility(View.VISIBLE);


                                } else if (me == 0 && other == 1) {



                                    img.setVisibility(View.GONE);

                                    video_request_accept.setVisibility(View.VISIBLE);
                                    video_request_reject.setVisibility(View.VISIBLE);
                                    connection.setText("Video Call Request");


                                }
                                else if (me == 1 && other == 1) {

                                    img.setVisibility(View.GONE);

                                    addlocalView();
                                    addRemoteView();
                                    video_request_accept.setVisibility(View.GONE);
                                    video_request_reject.setVisibility(View.GONE);
                                    connection.setText("");

                                    camera.setBackground(getResources().getDrawable(R.drawable.phonecircle));


                                    loudspeaker.setBackground(getResources().getDrawable(R.drawable.phonecircle));
                                    getSinchServiceInterface().getAudioController().enableSpeaker();


                                    cam_flag = 1;



                                }


                            } else {


                                if (me == 0 && other == 0) {


                                    video_request_accept.setVisibility(View.GONE);
                                    video_request_reject.setVisibility(View.GONE);
                                    connection.setText("");
                                    call_ref.child(mCallId).child("active").setValue(0);

                                    camera.setBackground(getResources().getDrawable(R.drawable.trans));

                                    removelocalview();
                                    removeremoteview();

                                    cam_flag = 0;


                                    img.setVisibility(View.VISIBLE);

                                }

                                else if(other == 0){

                                    removeremoteview();


                                }

                                else if(other == 1){

                                    addRemoteView();

                                }



                            }

                        }



                    }

                    @Override
                    public void onCancelled(@NonNull DatabaseError error) {

                    }
                });






            }

            @Override
            public void onChildRemoved(DataSnapshot dataSnapshot) {
                // ...
            }

            @Override
            public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
                // ...
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {
                // ...
            }
        };

        query.addChildEventListener(childEventListener);




        camera.setBackground(getResources().getDrawable(R.drawable.phonecircle));
        cam_flag = 1;
        img.setVisibility(View.GONE);
        addlocalView();



    }




    public void addlocalView(){

        if (localview || getSinchServiceInterface() == null) {
            return; //early
        }

        final VideoController vc = getSinchServiceInterface().getVideoController();
        if (vc != null) {
            RelativeLayout localView = (RelativeLayout) findViewById(R.id.localVideo);
            localView.addView(vc.getLocalView());

            localView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    //this toggles the front camera to rear camera and vice versa
                    vc.toggleCaptureDevicePosition();
                }
            });

            localview = true;
        }

    }


    public void addRemoteView(){

        if (remoteview || getSinchServiceInterface() == null) {
            return; //early
        }

        final VideoController vc = getSinchServiceInterface().getVideoController();
        if (vc != null) {


            ConstraintLayout view = ( ConstraintLayout ) findViewById(R.id.remoteVideo);
            view.addView(vc.getRemoteView());
            remoteview = true;
        }


    }

    public void removelocalview(){

        if (getSinchServiceInterface() == null) {
            return; // early
        }

        VideoController vc = getSinchServiceInterface().getVideoController();
        if (vc != null) {

            RelativeLayout localView = (RelativeLayout) findViewById(R.id.localVideo);
            localView.removeView(vc.getLocalView());
            localview = false;
        }

    }


    public void removeremoteview(){


        if (getSinchServiceInterface() == null) {
            return; // early
        }

        VideoController vc = getSinchServiceInterface().getVideoController();

        if (vc != null) {
            ConstraintLayout view = (ConstraintLayout) findViewById(R.id.remoteVideo);
            view.removeView(vc.getRemoteView());

            remoteview = false;
        }


    }



    @Override
    public void onServiceConnected() {
        Call call = getSinchServiceInterface().getCall(mCallId);
        if (call != null) {
            if (!mAddedListener) {

                call.addCallListener(new SinchCallListener());
                mAddedListener = true;

                camera.setBackground(getResources().getDrawable(R.drawable.phonecircle));
                cam_flag = 1;
                img.setVisibility(View.GONE);
                addlocalView();



            }
        } else {
            Log.e(TAG, "Started with invalid callId, aborting.");
            finish();
        }

        updateUI();
    }

    //method to update video feeds in the UI
    private void updateUI() {
        if (getSinchServiceInterface() == null) {
            return; // early
        }

        Call call = getSinchServiceInterface().getCall(mCallId);
        if (call != null) {
            mCallerName.setText("");


            // online data


            call_ref.child(mCallId).child(auth.getCurrentUser().getUid()).setValue(0);
            call_ref.child(mCallId).child(call.getRemoteUserId()).setValue(0);
            call_ref.child(mCallId).child("active").setValue(0);




            DatabaseReference connectedRef = FirebaseDatabase.getInstance().getReference().child("user").child(call.getRemoteUserId());
            connectedRef.addListenerForSingleValueEvent(new ValueEventListener() {
                @Override
                public void onDataChange(@NonNull DataSnapshot snapshot) {


                    String s = snapshot.child("name").getValue(String.class);
                    String u = snapshot.child("url").getValue(String.class);
                    mCallerName.setText(s);
                    Picasso.get().load(u).placeholder(R.drawable.profile_image).into(img);


                    mCallState.setText("Ringing");


                }

                @Override
                public void onCancelled(@NonNull DatabaseError error) {

                }
            });




            mCallState.setText(call.getState().toString());
            if (call.getState() == CallState.ESTABLISHED) {
                //when the call is established, addVideoViews configures the video to  be shown

            }
        }
    }

    //stop the timer when call is ended
    @Override
    public void onStop() {
        super.onStop();
        removelocalview();
        removeremoteview();
    }

    //start the timer for the call duration here
    @Override
    public void onStart() {
        super.onStart();
        mTimer = new Timer();
        updateUI();
    }

    @Override
    public void onBackPressed() {
        // User should exit activity by ending call, not by going back.
    }

    //method to end the call
    private void endCall() {
        mAudioPlayer.stopProgressTone();
        Call call = getSinchServiceInterface().getCall(mCallId);
        if (call != null) {
            call.hangup();
        }
        finish();
    }

    private String formatTimespan(long timespan) {
        long totalSeconds = timespan / 1000;
        long minutes = totalSeconds / 60;
        long seconds = totalSeconds % 60;
        return String.format(Locale.US, "%02d:%02d", minutes, seconds);
    }

    //method to update live duration of the call
    private void updateCallDuration() {
        if (mCallStart > 0) {
            mCallDuration.setText(formatTimespan(System.currentTimeMillis() - mCallStart));
        }
    }




    private class SinchCallListener implements VideoCallListener {

        @Override
        public void onCallEnded(Call call) {
            CallEndCause cause = call.getDetails().getEndCause();
            Log.d(TAG, "Call ended. Reason: " + cause.toString());
            mAudioPlayer.stopProgressTone();
            setVolumeControlStream(AudioManager.USE_DEFAULT_STREAM_TYPE);
            String endMsg = "Call ended: " + call.getDetails().toString();

            //Toast.makeText(VideoCall.this, endMsg, Toast.LENGTH_LONG).show();


            if(recieved_flag == 1){

                mDurationTask.cancel();
                mTimer.cancel();

            }

            endCall();
        }

        @Override
        public void onCallEstablished(Call call) {

            recieved_flag = 1;
            Log.d(TAG, "Call established");
            mAudioPlayer.stopProgressTone();
            mCallState.setText(call.getState().toString());
            setVolumeControlStream(AudioManager.STREAM_VOICE_CALL);

             AudioController audioController = getSinchServiceInterface().getAudioController();
             audioController.enableSpeaker();

             mCallStart = System.currentTimeMillis();
             Log.d(TAG, "Call offered video: " + call.getDetails().isVideoOffered());

            loud_flag = 1;
            loudspeaker.setBackground(getResources().getDrawable(R.drawable.phonecircle));



            // camera

            addRemoteView();
            video_request_accept.setVisibility(View.GONE);
            video_request_reject.setVisibility(View.GONE);
            connection.setText("");


            call_ref.child(mCallId).child(auth.getCurrentUser().getUid()).setValue(1);
            call_ref.child(mCallId).child(call.getRemoteUserId()).setValue(1);
            call_ref.child(mCallId).child("active").setValue(1);






            mDurationTask = new UpdateCallDurationTask();
            mTimer.schedule(mDurationTask, 0, 500);






            loudspeaker.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {



                    if(loud_flag == 0){


                        loud_flag = 1;
                        loudspeaker.setBackground(getResources().getDrawable(R.drawable.phonecircle));
                        getSinchServiceInterface().getAudioController().enableSpeaker();



                    /*AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
                    audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);

                    System.out.println("speaker on= "+audioManager.isSpeakerphoneOn());

                    if(!audioManager.isSpeakerphoneOn()) {
                        audioManager.setSpeakerphoneOn(true);
                    }
                 */

                    }
                    else {

                        loudspeaker.setBackground(getResources().getDrawable(R.drawable.trans));

                        loud_flag = 0;
                        getSinchServiceInterface().getAudioController().disableSpeaker();

                   /* AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
                    audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);

                    System.out.println("speaker off= "+ (!audioManager.isSpeakerphoneOn()));
                    if(audioManager.isSpeakerphoneOn()) {
                        audioManager.setSpeakerphoneOn(false);
                    }
                    */

                    }


                }
            });


            camera.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {


                    if(cam_flag == 0){

                        camera.setBackground(getResources().getDrawable(R.drawable.phonecircle));
                        cam_flag = 1;


                        addlocalView();
                        img.setVisibility(View.GONE);


                        call_ref.child(mCallId).child(auth.getCurrentUser().getUid()).setValue(1);



                    }
                    else {

                        camera.setBackground(getResources().getDrawable(R.drawable.trans));
                        cam_flag = 0;
                        removelocalview();

                        //  img.setVisibility(View.VISIBLE);


                        call_ref.child(mCallId).child(auth.getCurrentUser().getUid()).setValue(0);





                    }


                }
            });


            mic.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {

                    if(mic_flag == 0){


                        mic.setBackground(getResources().getDrawable(R.drawable.phonecircle));
                        getSinchServiceInterface().getAudioController().mute();
                        mic_flag = 1;
                    }
                    else {

                        mic.setBackground(getResources().getDrawable(R.drawable.trans));
                        getSinchServiceInterface().getAudioController().unmute();
                        mic_flag = 0;
                    }


                }
            });


            camera_change.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {

                    final VideoController vc = getSinchServiceInterface().getVideoController();
                    vc.toggleCaptureDevicePosition();


                }
            });





        }

        @Override
        public void onCallProgressing(Call call) {
            Log.d(TAG, "Call progressing");
            mAudioPlayer.playProgressTone();



        }

        @Override
        public void onShouldSendPushNotification(Call call, List<PushPair> pushPairs) {
            // Send a push through your push provider here, e.g. GCM


        }


        @Override
        public void onVideoTrackAdded(Call call) {



        }





        @Override
        public void onVideoTrackPaused(Call call) {



        }

        @Override
        public void onVideoTrackResumed(Call call) {


        }
    }

}

VoiceCall

 
 
import android.media.AudioManager;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;

import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.database.ChildEventListener;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.Query;
import com.google.firebase.database.ValueEventListener;
import com.saifyproduction.sunupayApp.R;
import com.saifyproduction.sunupayApp.controller.AudioPlayer;
import com.saifyproduction.sunupayApp.controller.SinchService;
import com.sinch.android.rtc.PushPair;
import com.sinch.android.rtc.calling.Call;
import com.sinch.android.rtc.calling.CallEndCause;
import com.sinch.android.rtc.calling.CallState;
import com.sinch.android.rtc.video.VideoCallListener;
import com.sinch.android.rtc.video.VideoController;
import com.squareup.picasso.Picasso;

import java.util.List;
import java.util.Locale;
import java.util.Timer;
import java.util.TimerTask;

import androidx.annotation.NonNull;
import androidx.constraintlayout.widget.ConstraintLayout;

public class VoiceCall extends BaseActivity {


    static final String TAG = VoiceCall.class.getSimpleName();
    static final String CALL_START_TIME = "callStartTime";
    static final String ADDED_LISTENER = "addedListener";

    private AudioPlayer mAudioPlayer;
    private Timer mTimer;
    private UpdateCallDurationTask mDurationTask;

    private String mCallId;
    private long mCallStart = 0;
    private boolean mAddedListener = false;
    private boolean mVideoViewsAdded = false;

    private TextView mCallDuration;
    private TextView mCallState;
    private TextView mCallerName;

    ImageView img, loudspeaker, camera , mic;

    int loud_flag =0 , cam_flag =0 , mic_flag =0;

    ImageView camera_change, video_request_accept, video_request_reject;

    TextView connection;

    int state =0;

    int recieved_flag = 0;

    boolean localview = false ;
    boolean remoteview = false ;

    DatabaseReference call_ref = FirebaseDatabase.getInstance().getReference().child("Call");
    FirebaseAuth auth  = FirebaseAuth.getInstance();



    private class UpdateCallDurationTask extends TimerTask {

        @Override
        public void run() {
            VoiceCall.this.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    updateCallDuration();
                }
            });
        }
    }


    @Override
    protected void onSaveInstanceState(Bundle savedInstanceState) {
        super.onSaveInstanceState(savedInstanceState);
        savedInstanceState.putLong(CALL_START_TIME, mCallStart);
        savedInstanceState.putBoolean(ADDED_LISTENER, mAddedListener);
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        mCallStart = savedInstanceState.getLong(CALL_START_TIME);
        mAddedListener = savedInstanceState.getBoolean(ADDED_LISTENER);
    }


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_voice_call);


        mAudioPlayer = new AudioPlayer(this);
        mCallDuration = (TextView) findViewById(R.id.callDuration);
        mCallerName = (TextView) findViewById(R.id.remoteUser);
        mCallState = (TextView) findViewById(R.id.callState);
        Button endCallButton = (Button) findViewById(R.id.hangup);


        img = (ImageView)findViewById(R.id.person_img);

        camera_change = (ImageView)findViewById(R.id.camera_chnage);

        video_request_accept = (ImageView)findViewById(R.id.acceptvideoreject);
        video_request_reject = (ImageView)findViewById(R.id.rejectvideorequest);

        connection = (TextView) findViewById(R.id.connection);

        video_request_accept.setVisibility(View.GONE);
        video_request_reject.setVisibility(View.GONE);



        endCallButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                endCall();
            }
        });

        mCallId = getIntent().getStringExtra(SinchService.CALL_ID);
        if (savedInstanceState == null) {
            mCallStart = System.currentTimeMillis();
        }

        mCallState.setText("Calling");



        loudspeaker = (ImageView) findViewById(R.id.loudspeaker);
        camera = (ImageView) findViewById(R.id.camera);
        mic = (ImageView) findViewById(R.id.mic);



        video_request_accept.setVisibility(View.GONE);
        video_request_reject.setVisibility(View.GONE);
        connection.setText("");








        video_request_accept.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                Call call = getSinchServiceInterface().getCall(mCallId);

                cam_flag = 1;


                img.setVisibility(View.GONE);

                addlocalView();
                addRemoteView();
                video_request_accept.setVisibility(View.GONE);
                video_request_reject.setVisibility(View.GONE);
                connection.setText("");



                camera.setBackground(getResources().getDrawable(R.drawable.phonecircle));


                call_ref.child(mCallId).child(auth.getCurrentUser().getUid()).setValue(1);
                call_ref.child(mCallId).child(call.getRemoteUserId()).setValue(1);
                call_ref.child(mCallId).child("active").setValue(1);







            }
        });


        video_request_reject.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {


                video_request_accept.setVisibility(View.GONE);
                video_request_reject.setVisibility(View.GONE);
                connection.setText("");

                removelocalview();
                removeremoteview();

                img.setVisibility(View.VISIBLE);
                cam_flag = 0;


                camera.setBackground(getResources().getDrawable(R.drawable.trans));



                Call call = getSinchServiceInterface().getCall(mCallId);
                call_ref.child(mCallId).child(auth.getCurrentUser().getUid()).setValue(0);
                call_ref.child(mCallId).child(call.getRemoteUserId()).setValue(0);

                call_ref.child(mCallId).child("active").setValue(0);






            }
        });





        Query query = FirebaseDatabase.getInstance()
                .getReference()
                .child("Call").child(mCallId);


        ChildEventListener  childEventListener = new ChildEventListener() {
            @Override
            public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
                // ...

            }

            @Override
            public void onChildChanged(DataSnapshot dataSnapshot, String previousChildName) {
                // ...



                System.out.println("On child changed");

                DatabaseReference connectedRef = FirebaseDatabase.getInstance().getReference().child("Call").child(mCallId);
                connectedRef.addListenerForSingleValueEvent(new ValueEventListener() {
                    @Override
                    public void onDataChange(@NonNull DataSnapshot snapshot) {

                        System.out.println("On Enter");


                        if(snapshot.exists()){

                            System.out.println("On Data Exist");


                            Call call = getSinchServiceInterface().getCall(mCallId);

                        int me = snapshot.child(auth.getCurrentUser().getUid()).getValue(Integer.class);
                        int other = snapshot.child(call.getRemoteUserId()).getValue(Integer.class);
                        int active = snapshot.child("active").getValue(Integer.class);

                        System.out.println("me  == "+me +"\n"+ " other == "+other +"\n active == "+active);

                        if (active == 0) {


                            if (me == 0 && other == 0) {


                                video_request_accept.setVisibility(View.GONE);
                                video_request_reject.setVisibility(View.GONE);
                                connection.setText("");

                                camera.setBackground(getResources().getDrawable(R.drawable.trans));


                                removelocalview();
                                removeremoteview();

                                cam_flag = 0;

                                img.setVisibility(View.VISIBLE);


                            } else if (me == 0 && other == 1) {



                                img.setVisibility(View.GONE);

                                video_request_accept.setVisibility(View.VISIBLE);
                                video_request_reject.setVisibility(View.VISIBLE);
                                connection.setText("Video Call Request");


                            }
                            else if (me == 1 && other == 1) {

                                img.setVisibility(View.GONE);

                                addlocalView();
                                addRemoteView();
                                video_request_accept.setVisibility(View.GONE);
                                video_request_reject.setVisibility(View.GONE);
                                connection.setText("");

                                camera.setBackground(getResources().getDrawable(R.drawable.phonecircle));


                                loudspeaker.setBackground(getResources().getDrawable(R.drawable.phonecircle));
                                getSinchServiceInterface().getAudioController().enableSpeaker();


                                cam_flag = 1;



                            }


                        } else {


                            if (me == 0 && other == 0) {


                                video_request_accept.setVisibility(View.GONE);
                                video_request_reject.setVisibility(View.GONE);
                                connection.setText("");
                                call_ref.child(mCallId).child("active").setValue(0);

                                camera.setBackground(getResources().getDrawable(R.drawable.trans));

                                removelocalview();
                                removeremoteview();

                                cam_flag = 0;


                                img.setVisibility(View.VISIBLE);

                            }

                            else if(other == 0){

                              removeremoteview();


                            }

                            else if(other == 1){

                                addRemoteView();

                            }



                          }

                        }



                    }

                    @Override
                    public void onCancelled(@NonNull DatabaseError error) {

                    }
                });






            }

            @Override
            public void onChildRemoved(DataSnapshot dataSnapshot) {
                // ...
            }

            @Override
            public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
                // ...
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {
                // ...
            }
        };

        query.addChildEventListener(childEventListener);




    }




    public void addlocalView(){

        if (localview || getSinchServiceInterface() == null) {
            return; //early
        }

        final VideoController vc = getSinchServiceInterface().getVideoController();
        if (vc != null) {
            RelativeLayout localView = (RelativeLayout) findViewById(R.id.localVideo);
            localView.addView(vc.getLocalView());

            localView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    //this toggles the front camera to rear camera and vice versa
                    vc.toggleCaptureDevicePosition();
                }
            });

            localview = true;
        }

    }


    public void addRemoteView(){

        if (remoteview || getSinchServiceInterface() == null) {
            return; //early
        }

        final VideoController vc = getSinchServiceInterface().getVideoController();
        if (vc != null) {


            ConstraintLayout view = ( ConstraintLayout ) findViewById(R.id.remoteVideo);
            view.addView(vc.getRemoteView());
            remoteview = true;
        }


    }

    public void removelocalview(){

        if (getSinchServiceInterface() == null) {
            return; // early
        }

        VideoController vc = getSinchServiceInterface().getVideoController();
        if (vc != null) {

            RelativeLayout localView = (RelativeLayout) findViewById(R.id.localVideo);
            localView.removeView(vc.getLocalView());
            localview = false;
        }

    }


    public void removeremoteview(){


        if (getSinchServiceInterface() == null) {
            return; // early
        }

        VideoController vc = getSinchServiceInterface().getVideoController();

         if (vc != null) {
            ConstraintLayout view = (ConstraintLayout) findViewById(R.id.remoteVideo);
            view.removeView(vc.getRemoteView());

            remoteview = false;
          }


    }



    @Override
    public void onServiceConnected() {
        Call call = getSinchServiceInterface().getCall(mCallId);
        if (call != null) {
            if (!mAddedListener) {


                call.addCallListener(new SinchCallListener());
                mAddedListener = true;








            }
        } else {
            Log.e(TAG, "Started with invalid callId, aborting.");
            finish();
        }

        updateUI();
    }

    //method to update video feeds in the UI
    private void updateUI() {
        if (getSinchServiceInterface() == null) {
            return; // early
        }

        Call call = getSinchServiceInterface().getCall(mCallId);
        if (call != null) {
            mCallerName.setText("");


            // online data


            call_ref.child(mCallId).child(auth.getCurrentUser().getUid()).setValue(0);
            call_ref.child(mCallId).child(call.getRemoteUserId()).setValue(0);
            call_ref.child(mCallId).child("active").setValue(0);




            DatabaseReference connectedRef = FirebaseDatabase.getInstance().getReference().child("user").child(call.getRemoteUserId());
            connectedRef.addListenerForSingleValueEvent(new ValueEventListener() {
                @Override
                public void onDataChange(@NonNull DataSnapshot snapshot) {


                    String s = snapshot.child("name").getValue(String.class);
                    String u = snapshot.child("url").getValue(String.class);
                    mCallerName.setText(s);
                    Picasso.get().load(u).placeholder(R.drawable.profile_image).into(img);


                    mCallState.setText("Ringing");


                }

                @Override
                public void onCancelled(@NonNull DatabaseError error) {

                }
            });




            mCallState.setText(call.getState().toString());
            if (call.getState() == CallState.ESTABLISHED) {
                //when the call is established, addVideoViews configures the video to  be shown

            }
        }
    }

    //stop the timer when call is ended
    @Override
    public void onStop() {
        super.onStop();
        removelocalview();
        removeremoteview();
    }

    //start the timer for the call duration here
    @Override
    public void onStart() {
        super.onStart();
        mTimer = new Timer();
        updateUI();
    }

    @Override
    public void onBackPressed() {
        // User should exit activity by ending call, not by going back.
    }

    //method to end the call
    private void endCall() {
        mAudioPlayer.stopProgressTone();
        Call call = getSinchServiceInterface().getCall(mCallId);
        if (call != null) {
            call.hangup();
        }
        finish();
    }

    private String formatTimespan(long timespan) {
        long totalSeconds = timespan / 1000;
        long minutes = totalSeconds / 60;
        long seconds = totalSeconds % 60;
        return String.format(Locale.US, "%02d:%02d", minutes, seconds);
    }

    //method to update live duration of the call
    private void updateCallDuration() {
        if (mCallStart > 0) {
            mCallDuration.setText(formatTimespan(System.currentTimeMillis() - mCallStart));
        }
    }




    private class SinchCallListener implements VideoCallListener {

        @Override
        public void onCallEnded(Call call) {
            CallEndCause cause = call.getDetails().getEndCause();
            Log.d(TAG, "Call ended. Reason: " + cause.toString());
            mAudioPlayer.stopProgressTone();
            setVolumeControlStream(AudioManager.USE_DEFAULT_STREAM_TYPE);
            String endMsg = "Call ended: " + call.getDetails().toString();
            Toast.makeText(VoiceCall.this, endMsg, Toast.LENGTH_LONG).show();


            if(recieved_flag == 1){

                mDurationTask.cancel();
                mTimer.cancel();

            }

            endCall();
        }

        @Override
        public void onCallEstablished(Call call) {

            recieved_flag = 1;
            Log.d(TAG, "Call established");
            mAudioPlayer.stopProgressTone();
            mCallState.setText(call.getState().toString());
            setVolumeControlStream(AudioManager.STREAM_VOICE_CALL);
           // AudioController audioController = getSinchServiceInterface().getAudioController();
           // audioController.enableSpeaker();
            mCallStart = System.currentTimeMillis();
            Log.d(TAG, "Call offered video: " + call.getDetails().isVideoOffered());

            mDurationTask = new UpdateCallDurationTask();
            mTimer.schedule(mDurationTask, 0, 500);



            loudspeaker.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {



                    if(loud_flag == 0){


                        loud_flag = 1;
                        loudspeaker.setBackground(getResources().getDrawable(R.drawable.phonecircle));
                        getSinchServiceInterface().getAudioController().enableSpeaker();



                    /*AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
                    audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);

                    System.out.println("speaker on= "+audioManager.isSpeakerphoneOn());

                    if(!audioManager.isSpeakerphoneOn()) {
                        audioManager.setSpeakerphoneOn(true);
                    }
                 */

                    }
                    else {

                        loudspeaker.setBackground(getResources().getDrawable(R.drawable.trans));

                        loud_flag = 0;
                        getSinchServiceInterface().getAudioController().disableSpeaker();

                   /* AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
                    audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);

                    System.out.println("speaker off= "+ (!audioManager.isSpeakerphoneOn()));
                    if(audioManager.isSpeakerphoneOn()) {
                        audioManager.setSpeakerphoneOn(false);
                    }
                    */

                    }


                }
            });


            camera.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {


                    if(cam_flag == 0){

                        camera.setBackground(getResources().getDrawable(R.drawable.phonecircle));
                        cam_flag = 1;


                        addlocalView();
                        img.setVisibility(View.GONE);


                        call_ref.child(mCallId).child(auth.getCurrentUser().getUid()).setValue(1);



                    }
                    else {

                        camera.setBackground(getResources().getDrawable(R.drawable.trans));
                        cam_flag = 0;
                        removelocalview();

                        //  img.setVisibility(View.VISIBLE);


                        call_ref.child(mCallId).child(auth.getCurrentUser().getUid()).setValue(0);





                    }


                }
            });


            mic.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {

                    if(mic_flag == 0){


                        mic.setBackground(getResources().getDrawable(R.drawable.phonecircle));
                        getSinchServiceInterface().getAudioController().mute();
                        mic_flag = 1;
                    }
                    else {

                        mic.setBackground(getResources().getDrawable(R.drawable.trans));
                        getSinchServiceInterface().getAudioController().unmute();
                        mic_flag = 0;
                    }


                }
            });


            camera_change.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {

                    final VideoController vc = getSinchServiceInterface().getVideoController();
                    vc.toggleCaptureDevicePosition();


                }
            });







        }

        @Override
        public void onCallProgressing(Call call) {
            Log.d(TAG, "Call progressing");
            mAudioPlayer.playProgressTone();
        }

        @Override
        public void onShouldSendPushNotification(Call call, List<PushPair> pushPairs) {
            // Send a push through your push provider here, e.g. GCM


        }


        @Override
        public void onVideoTrackAdded(Call call) {



        }





        @Override
        public void onVideoTrackPaused(Call call) {



        }

        @Override
        public void onVideoTrackResumed(Call call) {


        }
    }

}

MainActivity

import com.sinch.android.rtc.SinchError;

import android.Manifest;
import android.app.ProgressDialog;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.RequiresApi;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends BaseActivity implements SinchService.StartFailedListener {

    private Button mLoginButton;
    private EditText mLoginName;
    private ProgressDialog mSpinner;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            requestPermissions(new String[]{Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA, Manifest.permission.ACCESS_NETWORK_STATE, Manifest.permission.READ_PHONE_STATE},100);
        }

        //initializing UI elements
        mLoginName = (EditText) findViewById(R.id.loginName);
        mLoginButton = (Button) findViewById(R.id.loginButton);
        mLoginButton.setEnabled(false);
        mLoginButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                loginClicked();
            }
        });
    }

    //this method is invoked when the connection is established with the SinchService
    @Override
    protected void onServiceConnected() {
        mLoginButton.setEnabled(true);
        getSinchServiceInterface().setStartListener(this);
    }

    @Override
    protected void onPause() {
        if (mSpinner != null) {
            mSpinner.dismiss();
        }
        super.onPause();
    }

    @Override
    public void onStartFailed(SinchError error) {
        Toast.makeText(this, error.toString(), Toast.LENGTH_LONG).show();
        if (mSpinner != null) {
            mSpinner.dismiss();
        }
    }

    //Invoked when just after the service is connected with Sinch
    @Override
    public void onStarted() {
        openPlaceCallActivity();
    }

    //Login is Clicked to manually to connect to the Sinch Service
    private void loginClicked() {
        String userName = mLoginName.getText().toString();

        if (userName.isEmpty()) {
            Toast.makeText(this, "Please enter a name", Toast.LENGTH_LONG).show();
            return;
        }

        if (!getSinchServiceInterface().isStarted()) {
            getSinchServiceInterface().startClient(userName);
            showSpinner();
        } else {
            openPlaceCallActivity();
        }
    }


    //Once the connection is made to the Sinch Service, It takes you to the next activity where you enter the name of the user to whom the call is to be placed
    private void openPlaceCallActivity() {
        Intent mainActivity = new Intent(this, PlaceCallActivity.class);
        startActivity(mainActivity);
    }

    private void showSpinner() {
        mSpinner = new ProgressDialog(this);
        mSpinner.setTitle("Logging in");
        mSpinner.setMessage("Please wait...");
        mSpinner.show();
    }
}


Important Points

Create account in Sinch, and get three detail from there and put in your code in Sinch Service class


private static final String APP_KEY = "";
private static final String APP_SECRET = "";
private static final String ENVIRONMENT = "";

Thats it, any question and issue you can ask in comment section. Thank you.





Previous Post Next Post