org.appspot.apprtc.CallActivity.java Source code

Java tutorial

Introduction

Here is the source code for org.appspot.apprtc.CallActivity.java

Source

/*
 *  Copyright 2015 The WebRTC Project Authors. All rights reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

package org.appspot.apprtc;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.FragmentTransaction;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.graphics.PixelFormat;
import android.media.projection.MediaProjection;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.support.design.widget.Snackbar;
import android.support.v4.app.NotificationCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

import com.example.sharedresourceslib.BroadcastTypes;
import com.github.clans.fab.FloatingActionButton;

import org.appspot.apprtc.AppRTCAudioManager.AudioDevice;
import org.appspot.apprtc.AppRTCAudioManager.AudioManagerEvents;
import org.appspot.apprtc.AppRTCClient.RoomConnectionParameters;
import org.appspot.apprtc.AppRTCClient.SignalingParameters;
import org.appspot.apprtc.PeerConnectionClient.DataChannelParameters;
import org.appspot.apprtc.PeerConnectionClient.PeerConnectionParameters;
import org.appspot.apprtc.fragment.CallListFragment;
import org.appspot.apprtc.fragment.ChatFragment;
import org.appspot.apprtc.receiver.CustomPhoneStateListener;
import org.appspot.apprtc.service.WebsocketService;
import org.appspot.apprtc.sound.SoundPlayer;

import org.appspot.apprtc.util.SessionIdentifierGenerator;
import org.appspot.apprtc.util.ThumbnailsCacheManager;

import org.json.JSONException;
import org.json.JSONObject;
import org.webrtc.Camera1Enumerator;
import org.webrtc.Camera2Enumerator;
import org.webrtc.CameraEnumerator;
import org.webrtc.DataChannel;
import org.webrtc.EglBase;
import org.webrtc.FileVideoCapturer;
import org.webrtc.IceCandidate;
import org.webrtc.Logging;
import org.webrtc.PeerConnectionFactory;
import org.webrtc.RendererCommon.ScalingType;
import org.webrtc.ScreenCapturerAndroid;
import org.webrtc.SessionDescription;
import org.webrtc.StatsReport;
import org.webrtc.SurfaceViewRenderer;
import org.webrtc.VideoCapturer;
import org.webrtc.VideoFileRenderer;
import org.webrtc.VideoRenderer;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.TimeZone;

import static com.example.sharedresourceslib.BroadcastTypes.ACTION_SEND_SESSION_DESCRIPTION;
import static com.example.sharedresourceslib.BroadcastTypes.EXTRA_CANDIDATES;
import static com.example.sharedresourceslib.BroadcastTypes.EXTRA_SIGNALING;
import static org.appspot.apprtc.RoomActivity.ACTION_SHARE_FILE;

/**
 * Activity for peer connection call setup, call waiting
 * and call view.
 */
public class CallActivity extends AppCompatActivity
        implements AppRTCClient.SignalingEvents, PeerConnectionClient.PeerConnectionEvents,
        CallFragment.OnCallEvents, ChatFragment.OnChatEvents, InitiateCallFragment.OnInitiateCallEvents,
        AdditionalPeerConnection.AdditionalPeerConnectionEvents, PeerConnectionClient.DataChannelCallback {
    public static final String ACTION_NEW_CALL = "org.appspot.apprtc.ACTION_NEW_CALL";
    public static final String ACTION_RESUME_CALL = "org.appspot.apprtc.ACTION_RESUME_CALL";
    public static final String EXTRA_PEER_ID = "org.appspot.apprtc.EXTRA_PEER_ID";
    public static final String ACTION_HANG_UP = "org.appspot.apprtc.ACTION_HANG_UP";
    public static final String ACTION_TOGGLE_VIDEO = "org.appspot.apprtc.ACTION_TOGGLE_VIDEO";
    public static final String ACTION_TOGGLE_MIC = "org.appspot.apprtc.ACTION_TOGGLE_MIC";
    public static final String ACTION_SEND_MESSAGE = "org.appspot.apprtc.ACTION_SEND_MESSAGE";
    public static final String EXTRA_MESSAGE = "org.appspot.apprtc.EXTRA_MESSAGE";
    public static final String EXTRA_MIC_ENABLED = "org.appspot.apprtc.EXTRA_MIC_ENABLED";
    public static final String EXTRA_ROOMID = "org.appspot.apprtc.ROOMID";
    public static final String EXTRA_LOOPBACK = "org.appspot.apprtc.LOOPBACK";
    public static final String EXTRA_VIDEO_CALL = "org.appspot.apprtc.VIDEO_CALL";
    public static final String EXTRA_SCREENCAPTURE = "org.appspot.apprtc.SCREENCAPTURE";
    public static final String EXTRA_CAMERA2 = "org.appspot.apprtc.CAMERA2";
    public static final String EXTRA_VIDEO_WIDTH = "org.appspot.apprtc.VIDEO_WIDTH";
    public static final String EXTRA_VIDEO_HEIGHT = "org.appspot.apprtc.VIDEO_HEIGHT";
    public static final String EXTRA_VIDEO_FPS = "org.appspot.apprtc.VIDEO_FPS";
    public static final String EXTRA_VIDEO_CAPTUREQUALITYSLIDER_ENABLED = "org.appsopt.apprtc.VIDEO_CAPTUREQUALITYSLIDER";
    public static final String EXTRA_VIDEO_BITRATE = "org.appspot.apprtc.VIDEO_BITRATE";
    public static final String EXTRA_VIDEOCODEC = "org.appspot.apprtc.VIDEOCODEC";
    public static final String EXTRA_HWCODEC_ENABLED = "org.appspot.apprtc.HWCODEC";
    public static final String EXTRA_CAPTURETOTEXTURE_ENABLED = "org.appspot.apprtc.CAPTURETOTEXTURE";
    public static final String EXTRA_FLEXFEC_ENABLED = "org.appspot.apprtc.FLEXFEC";
    public static final String EXTRA_AUDIO_BITRATE = "org.appspot.apprtc.AUDIO_BITRATE";
    public static final String EXTRA_AUDIOCODEC = "org.appspot.apprtc.AUDIOCODEC";
    public static final String EXTRA_NOAUDIOPROCESSING_ENABLED = "org.appspot.apprtc.NOAUDIOPROCESSING";
    public static final String EXTRA_AECDUMP_ENABLED = "org.appspot.apprtc.AECDUMP";
    public static final String EXTRA_OPENSLES_ENABLED = "org.appspot.apprtc.OPENSLES";
    public static final String EXTRA_DISABLE_BUILT_IN_AEC = "org.appspot.apprtc.DISABLE_BUILT_IN_AEC";
    public static final String EXTRA_DISABLE_BUILT_IN_AGC = "org.appspot.apprtc.DISABLE_BUILT_IN_AGC";
    public static final String EXTRA_DISABLE_BUILT_IN_NS = "org.appspot.apprtc.DISABLE_BUILT_IN_NS";
    public static final String EXTRA_ENABLE_LEVEL_CONTROL = "org.appspot.apprtc.ENABLE_LEVEL_CONTROL";
    public static final String EXTRA_DISPLAY_HUD = "org.appspot.apprtc.DISPLAY_HUD";
    public static final String EXTRA_TRACING = "org.appspot.apprtc.TRACING";
    public static final String EXTRA_CMDLINE = "org.appspot.apprtc.CMDLINE";
    public static final String EXTRA_RUNTIME = "org.appspot.apprtc.RUNTIME";
    public static final String EXTRA_VIDEO_FILE_AS_CAMERA = "org.appspot.apprtc.VIDEO_FILE_AS_CAMERA";
    public static final String EXTRA_SAVE_REMOTE_VIDEO_TO_FILE = "org.appspot.apprtc.SAVE_REMOTE_VIDEO_TO_FILE";
    public static final String EXTRA_SAVE_REMOTE_VIDEO_TO_FILE_WIDTH = "org.appspot.apprtc.SAVE_REMOTE_VIDEO_TO_FILE_WIDTH";
    public static final String EXTRA_SAVE_REMOTE_VIDEO_TO_FILE_HEIGHT = "org.appspot.apprtc.SAVE_REMOTE_VIDEO_TO_FILE_HEIGHT";
    public static final String EXTRA_USE_VALUES_FROM_INTENT = "org.appspot.apprtc.USE_VALUES_FROM_INTENT";
    public static final String EXTRA_DATA_CHANNEL_ENABLED = "org.appspot.apprtc.DATA_CHANNEL_ENABLED";
    public static final String EXTRA_ORDERED = "org.appspot.apprtc.ORDERED";
    public static final String EXTRA_MAX_RETRANSMITS_MS = "org.appspot.apprtc.MAX_RETRANSMITS_MS";
    public static final String EXTRA_MAX_RETRANSMITS = "org.appspot.apprtc.MAX_RETRANSMITS";
    public static final String EXTRA_PROTOCOL = "org.appspot.apprtc.PROTOCOL";
    public static final String EXTRA_NEGOTIATED = "org.appspot.apprtc.NEGOTIATED";
    public static final String EXTRA_ID = "org.appspot.apprtc.ID";

    private static final int FILE_CODE = 1;

    private static final String TAG = "CallRTCClient";

    // Peer connection statistics callback period in ms.
    private static final int STAT_CALLBACK_PERIOD = 1000;
    // Local preview screen position before call is connected.
    private static final int LOCAL_X_CONNECTING = 0;
    private static final int LOCAL_Y_CONNECTING = 0;
    private static final int LOCAL_WIDTH_CONNECTING = 100;
    private static final int LOCAL_HEIGHT_CONNECTING = 100;
    // Local preview screen position after call is connected.
    private static final int LOCAL_X_CONNECTED = 72;
    private static final int LOCAL_Y_CONNECTED = 62;
    private static final int LOCAL_WIDTH_CONNECTED = 25;
    private static final int LOCAL_HEIGHT_CONNECTED = 25;
    // Remote video screen position
    private static final int REMOTE_X = 0;
    private static final int REMOTE_X2 = 50;
    private static final int REMOTE_Y = 0;
    private static final int REMOTE_WIDTH = 100;
    private static final int REMOTE_WIDTH2 = 50;
    private static final int REMOTE_HEIGHT = 100;
    private static final int REMOTE_HEIGHT2 = 50;
    private PeerConnectionClient peerConnectionClient = null;

    //private AppRTCClient appRtcClient;
    private String mPeerId = "";
    private String mPeerName = "";
    private String mOwnId = "";
    WebsocketService mService;
    boolean mWebsocketServiceBound = false;
    private SerializableSessionDescription mRemoteSdp = null;
    private SessionDescription mLocalSdp = null;
    private boolean initiator = false;
    private SignalingParameters signalingParameters;
    static private AppRTCAudioManager audioManager = null;
    static private EglBase rootEglBase;
    private SurfaceViewRenderer localRender;
    private SurfaceViewRenderer screenshareRender;
    private SurfaceViewRenderer remoteRenderScreen;
    private SurfaceViewRenderer remoteRenderScreen2;
    private SurfaceViewRenderer remoteRenderScreen3;
    private SurfaceViewRenderer remoteRenderScreen4;
    private TextView remoteVideoLabel;
    private TextView remoteVideoLabel2;
    private TextView remoteVideoLabel3;
    private TextView remoteVideoLabel4;
    private ImageView remoteUserImage;
    private ImageView remoteUserImage2;
    private ImageView remoteUserImage3;
    private ImageView remoteUserImage4;
    private ArrayList<RemoteConnectionViews> remoteViewsList = new ArrayList<RemoteConnectionViews>();
    private ArrayList<RemoteConnectionViews> remoteViewsInUseList = new ArrayList<RemoteConnectionViews>();
    private VideoFileRenderer videoFileRenderer;
    private final List<VideoRenderer.Callbacks> remoteRenderers = new ArrayList<VideoRenderer.Callbacks>();
    private PercentFrameLayout localRenderLayout;
    private PercentFrameLayout screenshareRenderLayout;
    private PercentFrameLayout remoteRenderLayout;
    private PercentFrameLayout remoteRenderLayout2;
    private PercentFrameLayout remoteRenderLayout3;
    private PercentFrameLayout remoteRenderLayout4;
    private ScalingType scalingType;
    private Toast logToast;
    private boolean commandLineRun;
    private int runTimeMs;
    private boolean activityRunning;
    private RoomConnectionParameters roomConnectionParameters;
    private PeerConnectionParameters peerConnectionParameters;
    private boolean iceConnected;
    private boolean isError;
    private boolean callControlFragmentVisible = true;
    private long callStartedTimeMs = 0;
    private boolean micEnabled = true;
    private boolean videoEnabled = true;
    private boolean screencaptureEnabled = false;
    private static Intent mediaProjectionPermissionResultData;
    private static int mediaProjectionPermissionResultCode;
    private boolean disconnected = false;

    private String keyprefVideoCallEnabled;
    private String keyprefScreencapture;
    private String keyprefCamera2;
    private String keyprefResolution;
    private String keyprefFps;
    private String keyprefCaptureQualitySlider;
    private String keyprefVideoBitrateType;
    private String keyprefVideoBitrateValue;
    private String keyprefVideoCodec;
    private String keyprefAudioBitrateType;
    private String keyprefAudioBitrateValue;
    private String keyprefAudioCodec;
    private String keyprefHwCodecAcceleration;
    private String keyprefCaptureToTexture;
    private String keyprefFlexfec;
    private String keyprefNoAudioProcessingPipeline;
    private String keyprefAecDump;
    private String keyprefOpenSLES;
    private String keyprefDisableBuiltInAec;
    private String keyprefDisableBuiltInAgc;
    private String keyprefDisableBuiltInNs;
    private String keyprefEnableLevelControl;
    private String keyprefDisplayHud;
    private String keyprefTracing;
    private String keyprefRoomServerUrl;
    private String keyprefEnableDataChannel;
    private String keyprefOrdered;
    private String keyprefMaxRetransmitTimeMs;
    private String keyprefMaxRetransmits;
    private String keyprefDataProtocol;
    private String keyprefNegotiated;
    private String keyprefDataId;

    // Controls
    private InitiateCallFragment initiateCallFragment;
    private CallFragment callFragment;
    private CallListFragment callListFragment;
    private HudFragment hudFragment;
    //private CpuMonitor cpuMonitor;

    private SharedPreferences sharedPref;

    private IntentFilter mIntentFilter;

    String mConferenceId = null;
    ArrayList<String> connectedUserIds = new ArrayList<String>();
    private boolean mSentAnswer;
    private boolean mAnswerPressed;
    private boolean speakerPhoneEnabled = true;
    private boolean userListShown;
    private ImageView remoteUserHoldStatus;
    private ImageView remoteUserHoldStatus2;
    private ImageView remoteUserHoldStatus3;
    private ImageView remoteUserHoldStatus4;
    private RemoteConnectionViews screenshareRemoteView;
    private boolean maximizeScreenshare = true;
    private int screenshareFrontIndex;
    private String mSignaling = "spreed";
    private String mOwnJid;
    private String mSid;
    private ChatFragment chatFragment;
    private String mFileRecipient;

    @Override
    public void onBinaryMessage(DataChannel.Buffer buffer) {

    }

    @Override
    public void onTextMessage(String text) {
        try {
            JSONObject json = new JSONObject(text);
            String type = json.optString("Type");

            if (type.equals("Screenshare")) {
                String userId = json.optString("Id");
                String screenshareTxt = json.optString("Screenshare");
                JSONObject screnshareJson = null;
                screnshareJson = new JSONObject(screenshareTxt);

                String id = screnshareJson.optString("id");
                onScreenShare(mPeerId, id, peerConnectionClient);
            } else if (type.equals("Chat")) {
                String chatTxt = json.optString("Chat");
                JSONObject chatJson = new JSONObject(chatTxt);
                String message = chatJson.optString("Message");
                SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
                fmt.setTimeZone(TimeZone.getTimeZone("GMT"));
                String time = fmt.format(new Date());
                String status = "";
                String statusTxt = chatJson.optString("Status");
                if (statusTxt != null && !statusTxt.equals("null") && statusTxt.length() > 0) {
                    JSONObject jsonStatus = new JSONObject(statusTxt);
                    if (jsonStatus.has("Typing")) {
                        status = " is Typing";
                    } else if (jsonStatus.has("FileInfo")) {
                        // {"FileInfo":{"id":"511832825.560452.PNG_2X0jvV2T8nN4c5ymaxa0cmkDgE5rvJB0upD3KB+h","chunks":22,"name":"511832825.560452.PNG","size":1296472,"type":"image\/png"}}
                        JSONObject jsonFileInfo = new JSONObject(jsonStatus.getString("FileInfo"));
                        String id = jsonFileInfo.getString("id");
                        String chunks = jsonFileInfo.getString("chunks");
                        String name = jsonFileInfo.getString("name");
                        String size = jsonFileInfo.getString("size");
                        String filetype = jsonFileInfo.getString("type");
                        mService.onFileMessage(time, id, chunks, name, size, filetype, mPeerId,
                                mService.getCurrentRoomName());
                    }
                } else {
                    mService.onChatMessage(message, time, status, mOwnId, mPeerId, mService.getCurrentRoomName());
                }
            } else if (type.equals("Answer")) {
                String answerTxt = json.optString("Answer");
                JSONObject answerJson = new JSONObject(answerTxt);
                SessionDescription sdp = new SessionDescription(SessionDescription.Type.fromCanonicalForm(type),
                        answerJson.getString("sdp"));
                String token = answerJson.optString("_token");
                String id = answerJson.optString("_id");
                String conferenceId = answerJson.optString("_conference");
                AdditionalPeerConnection peer = mTokenPeers.get(mPeerId);
                peer.setRemoteDescription(sdp);
            } else if (type.equals("Candidate")) {
                String candidateTxt = json.optString("Candidate");
                JSONObject candidateJson = new JSONObject(candidateTxt);
                String id = candidateJson.optString("_id");
                String token = candidateJson.optString("_token");
                IceCandidate candidate = new IceCandidate(candidateJson.getString("sdpMid"),
                        candidateJson.getInt("sdpMLineIndex"), candidateJson.getString("candidate"));
                AdditionalPeerConnection peer = mTokenPeers.get(mPeerId);
                peer.addRemoteIceCandidate(candidate);

            } else if (type.equals("Hold")) {
                String holdTxt = json.optString("Hold");
                JSONObject holdJson = null;
                holdJson = new JSONObject(holdTxt);

                boolean hold = holdJson.getBoolean("state");

                showHoldMessage(hold);
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onStateChange(DataChannel.State state) {

    }

    @Override
    public void onSendChatMessage(String time, String displayName, String buddyPicture, String message, String to) {
        if (mService != null) {
            mService.sendChatMessage(time, displayName, buddyPicture, message, to, mService.getCurrentRoomName());
        }
    }

    @Override
    public void onMessageRead() {

    }

    @Override
    public void onSendFile(String time, String displayName, String buddyPicture, String message, long size,
            String name, String mime, String to) {

    }

    @Override
    public void onDownload(int position, String id, FileInfo fileinfo) {

    }

    public String getCurrentRoomName() {
        if (mService != null) {
            return mService.getCurrentRoomName();
        }
        return "";
    }

    public class RemoteConnection {
        SerializableSessionDescription sdp;
        User user;

        public RemoteConnection(SerializableSessionDescription sdp, User user) {
            this.sdp = sdp;
            this.user = user;
        }
    }

    public class RemoteConnectionViews {
        String id = "";
        String mName = "";
        PercentFrameLayout frameLayout;
        SurfaceViewRenderer surfaceViewRenderer;
        ImageView imageView;
        TextView textView;
        ImageView remoteUserHoldStatus;

        public RemoteConnectionViews(String name, PercentFrameLayout frameLayout,
                SurfaceViewRenderer surfaceViewRenderer, ImageView imageView, TextView textView,
                ImageView remoteUserHoldStatus) {
            this.mName = name;
            this.frameLayout = frameLayout;
            this.surfaceViewRenderer = surfaceViewRenderer;
            this.imageView = imageView;
            this.textView = textView;
            this.remoteUserHoldStatus = remoteUserHoldStatus;
        }

        public PercentFrameLayout getFrameLayout() {
            return frameLayout;
        }

        public SurfaceViewRenderer getSurfaceViewRenderer() {
            return this.surfaceViewRenderer;
        }

        public ImageView getImageView() {
            return imageView;
        }

        public TextView getTextView() {
            return textView;
        }

        public void setPosition(int remoteX, int remoteY, int remoteWidth, int remoteHeight) {
            getFrameLayout().setPosition(remoteX, remoteY, remoteWidth, remoteHeight);
        }

        public String getName() {
            return mName;
        }

        public void setHoldStatus(boolean on) {
            if (on) {
                remoteUserHoldStatus.setVisibility(View.VISIBLE);
            } else {
                remoteUserHoldStatus.setVisibility(View.GONE);
            }
        }

        public String getId() {
            return id;
        }

        public void setId(String remoteId) {
            id = remoteId;
        }
    }

    /** Defines callbacks for service binding, passed to bindService() */
    private ServiceConnection mConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName className, IBinder service) {
            // We've bound to LocalService, cast the IBinder and get LocalService instance
            WebsocketService.WebsocketBinder binder = (WebsocketService.WebsocketBinder) service;
            mService = binder.getService();
            mWebsocketServiceBound = true;
            mOwnId = mService.getId();
            signalingParameters = new SignalingParameters(WebsocketService.getIceServers(), initiator, "", "", "",
                    null, null);
            callListFragment.init(mService.getUsersInRoom(mService.getCurrentRoomName()));
        }

        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            mWebsocketServiceBound = false;
        }
    };
    private HashMap<String, SerializableIceCandidate> mIceCandidates = new HashMap<String, SerializableIceCandidate>();
    private boolean mWaitingToStartCall;
    private boolean mCaptureToTexture;
    private SoundPlayer mSoundPlayer;

    private boolean mVideoCallEnabled;
    private boolean mUseCamera2;
    private String mProtocol;
    private int mVideoWidth = 0;
    private int mVideoHeight = 0;
    private boolean mLoopback = false;
    private String mVideoCodec;
    private boolean mHwCodecEnabled = false;
    private boolean mFlexfecEnabled;
    private boolean mDisableBuiltInAGC;
    private boolean mEnableLevelControl;
    private boolean mAecDump;
    private String mAudioCodec;
    private int mId = -1;
    private boolean mDisableBuiltInNS;
    private boolean mNoAudioProcessing;
    private boolean mUseOpenSLES;
    private int mMaxRetr;
    private boolean mDisableBuiltInAEC;
    private int mCameraFps;
    private boolean mCaptureQualitySlider;
    private int mVideoStartBitrate;
    private int mAudioStartBitrate;
    private boolean mDisplayHud;
    private boolean mTracing;
    private boolean mDataChannelEnabled;
    private boolean mOrdered;
    private boolean mNegotiated;
    private int mMaxRetrMs = -1;
    private SerializableIceCandidate mRemoteIceCandidate;
    private boolean mFinishCalled;

    private HashMap<String, AdditionalPeerConnection> mAdditionalPeers = new HashMap<String, AdditionalPeerConnection>();
    private HashMap<String, AdditionalPeerConnection> mTokenPeers = new HashMap<String, AdditionalPeerConnection>();

    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals(CustomPhoneStateListener.ACTION_HOLD_ON)) {
                putCallOnHold(true);
            } else if (intent.getAction().equals(CustomPhoneStateListener.ACTION_HOLD_OFF)) {
                putCallOnHold(false);
            } else if (intent.getAction().equals(WebsocketService.ACTION_SCREENSHARE)) {
                User user = (User) intent.getSerializableExtra(WebsocketService.EXTRA_USER);
                String token = intent.getStringExtra(WebsocketService.EXTRA_TOKEN);
                addToCall(user, token, false, null);
            } else if (intent.getAction().equals(WebsocketService.ACTION_BYE)) {

                String reason = intent.getStringExtra(WebsocketService.EXTRA_REASON);
                User user = (User) intent.getSerializableExtra(WebsocketService.EXTRA_USER);
                String id = intent.getStringExtra(WebsocketService.EXTRA_ID);
                if (user != null) {
                    ArrayList<User> users = getUsers();
                    for (User u : users) {
                        if (u.Id.equals(user.Id)) {
                            u.setCallState(User.CallState.NONE);
                            break;
                        }
                    }
                }

                if (mAdditionalPeers.containsKey(id)) {
                    updateRemoteViewList(mAdditionalPeers.get(id).getRemoteViews());
                    mAdditionalPeers.get(id).close();
                    mAdditionalPeers.remove(id);
                } else if (reason.equals("ringertimeout")) {
                    if (!mFinishCalled) {
                        mFinishCalled = true;
                        finish();
                    }
                } else if (reason.equals("pickuptimeout")) {
                    initiateCallFragment.showPickupTimeout(user);
                    if (!mFinishCalled) {
                        mFinishCalled = true;
                        finish();
                    }
                } else if (user != null && user.Id.equals(mPeerId)) {
                    // normal call clearing
                    if (!mFinishCalled) {
                        mFinishCalled = true;
                        finish();
                    }
                }
            } else if (intent.getAction().equals(WebsocketService.ACTION_USER_ENTERED)) {
                User user = (User) intent.getSerializableExtra(WebsocketService.EXTRA_USER);
                if (callFragment != null) {
                    callFragment.onUserEntered(user);
                }
                if (callListFragment != null) {
                    callListFragment.onUserEntered(user);
                }
            } else if (intent.getAction().equals(WebsocketService.ACTION_USER_LEFT)) {
                User user = (User) intent.getSerializableExtra(WebsocketService.EXTRA_USER);
                if (callFragment != null) {
                    callFragment.onUserLeft(user);
                }
                if (callListFragment != null) {
                    callListFragment.onUserLeft(user);
                }
            } else if (intent.getAction().equals(WebsocketService.ACTION_CHAT_MESSAGE)) {
                // pass the intent to the callFragment and callListFragment
                if (callFragment != null) {
                    callFragment.onChatMessage(intent, mServer);
                }
            } else if (intent.getAction().equals(WebsocketService.ACTION_FILE_MESSAGE)) {
                if (callFragment != null) {
                    callFragment.onFileMessage(intent, mServer);
                }
            }

        }
    };

    private void updateRemoteViewList(RemoteConnectionViews remoteViews) {
        remoteViewsInUseList.remove(remoteViews);
        remoteViewsList.add(remoteViews);

        updateVideoView();
        if (remoteViews != null) {
            Log.i(TAG, "remove view " + remoteViews.getName());
        }
    }

    private String mToken;
    private String mSdpId = "";
    private boolean mForceTurn;
    private ArrayList<User> mQueuedPeers = new ArrayList<User>();
    private ArrayList<RemoteConnection> mQueuedRemoteConnections = new ArrayList<RemoteConnection>();
    private String mServer;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Thread.setDefaultUncaughtExceptionHandler(new UnhandledExceptionHandler(this));

        // Set window styles for fullscreen-window size. Needs to be done before
        // adding content.
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().addFlags(LayoutParams.FLAG_KEEP_SCREEN_ON | LayoutParams.FLAG_DISMISS_KEYGUARD
                | LayoutParams.FLAG_SHOW_WHEN_LOCKED | LayoutParams.FLAG_TURN_SCREEN_ON);
        getWindow().getDecorView()
                .setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
        setContentView(R.layout.activity_call);

        mIntentFilter = new IntentFilter();
        mIntentFilter.addAction(WebsocketService.ACTION_BYE);
        mIntentFilter.addAction(WebsocketService.ACTION_USER_ENTERED);
        mIntentFilter.addAction(WebsocketService.ACTION_USER_LEFT);
        mIntentFilter.addAction(WebsocketService.ACTION_SCREENSHARE);
        mIntentFilter.addAction(CustomPhoneStateListener.ACTION_HOLD_ON);
        mIntentFilter.addAction(CustomPhoneStateListener.ACTION_HOLD_OFF);
        mIntentFilter.addAction(WebsocketService.ACTION_CHAT_MESSAGE);
        mIntentFilter.addAction(WebsocketService.ACTION_FILE_MESSAGE);
        registerReceiver(mReceiver, mIntentFilter);

        // Get setting keys.
        PreferenceManager.setDefaultValues(this, R.xml.webrtc_preferences, false);
        sharedPref = PreferenceManager.getDefaultSharedPreferences(this);

        keyprefVideoCallEnabled = getString(R.string.pref_videocall_key);
        keyprefScreencapture = getString(R.string.pref_screencapture_key);
        keyprefCamera2 = getString(R.string.pref_camera2_key);
        keyprefResolution = getString(R.string.pref_resolution_key);
        keyprefFps = getString(R.string.pref_fps_key);
        keyprefCaptureQualitySlider = getString(R.string.pref_capturequalityslider_key);
        keyprefVideoBitrateType = getString(R.string.pref_maxvideobitrate_key);
        keyprefVideoBitrateValue = getString(R.string.pref_maxvideobitratevalue_key);
        keyprefVideoCodec = getString(R.string.pref_videocodec_key);
        keyprefHwCodecAcceleration = getString(R.string.pref_hwcodec_key);
        keyprefCaptureToTexture = getString(R.string.pref_capturetotexture_key);
        keyprefFlexfec = getString(R.string.pref_flexfec_key);
        keyprefAudioBitrateType = getString(R.string.pref_startaudiobitrate_key);
        keyprefAudioBitrateValue = getString(R.string.pref_startaudiobitratevalue_key);
        keyprefAudioCodec = getString(R.string.pref_audiocodec_key);
        keyprefNoAudioProcessingPipeline = getString(R.string.pref_noaudioprocessing_key);
        keyprefAecDump = getString(R.string.pref_aecdump_key);
        keyprefOpenSLES = getString(R.string.pref_opensles_key);
        keyprefDisableBuiltInAec = getString(R.string.pref_disable_built_in_aec_key);
        keyprefDisableBuiltInAgc = getString(R.string.pref_disable_built_in_agc_key);
        keyprefDisableBuiltInNs = getString(R.string.pref_disable_built_in_ns_key);
        keyprefEnableLevelControl = getString(R.string.pref_enable_level_control_key);
        keyprefDisplayHud = getString(R.string.pref_displayhud_key);
        keyprefTracing = getString(R.string.pref_tracing_key);
        keyprefRoomServerUrl = getString(R.string.pref_room_server_url_key);
        keyprefEnableDataChannel = getString(R.string.pref_enable_datachannel_key);
        keyprefOrdered = getString(R.string.pref_ordered_key);
        keyprefMaxRetransmitTimeMs = getString(R.string.pref_max_retransmit_time_ms_key);
        keyprefMaxRetransmits = getString(R.string.pref_max_retransmits_key);
        keyprefDataProtocol = getString(R.string.pref_data_protocol_key);
        keyprefNegotiated = getString(R.string.pref_negotiated_key);
        keyprefDataId = getString(R.string.pref_data_id_key);

        readPrefs();

        final Intent intent = getIntent();

        if (intent.getAction().equals(WebsocketService.ACTION_REMOTE_ICE_CANDIDATE)) {
            finish(); // don't start the activity on this intent
        } else if (intent.getAction().equals(WebsocketService.ACTION_REMOTE_DESCRIPTION)) {
            SerializableSessionDescription sdp = (SerializableSessionDescription) intent
                    .getSerializableExtra(WebsocketService.EXTRA_REMOTE_DESCRIPTION);
            mVideoCallEnabled = sdp.description.contains("m=video");
        }

        iceConnected = false;
        signalingParameters = new SignalingParameters(WebsocketService.getIceServers(), initiator, "", "", "", null,
                null);
        scalingType = ScalingType.SCALE_ASPECT_FILL;

        // Create UI controls.

        localRender = (SurfaceViewRenderer) findViewById(R.id.local_video_view);
        screenshareRender = (SurfaceViewRenderer) findViewById(R.id.screenshare_video_view);
        remoteRenderScreen = (SurfaceViewRenderer) findViewById(R.id.remote_video_view);

        remoteRenderScreen2 = (SurfaceViewRenderer) findViewById(R.id.remote_video_view2);
        remoteRenderScreen3 = (SurfaceViewRenderer) findViewById(R.id.remote_video_view3);
        remoteRenderScreen4 = (SurfaceViewRenderer) findViewById(R.id.remote_video_view4);
        remoteVideoLabel = (TextView) findViewById(R.id.remote_video_label);
        remoteVideoLabel2 = (TextView) findViewById(R.id.remote_video_label2);
        remoteVideoLabel3 = (TextView) findViewById(R.id.remote_video_label3);
        remoteVideoLabel4 = (TextView) findViewById(R.id.remote_video_label4);
        remoteUserImage = (ImageView) findViewById(R.id.remote_user_image1);
        remoteUserImage2 = (ImageView) findViewById(R.id.remote_user_image2);
        remoteUserImage3 = (ImageView) findViewById(R.id.remote_user_image3);
        remoteUserImage4 = (ImageView) findViewById(R.id.remote_user_image4);
        remoteUserHoldStatus = (ImageView) findViewById(R.id.remote_user_hold_status);
        remoteUserHoldStatus2 = (ImageView) findViewById(R.id.remote_user_hold_status2);
        remoteUserHoldStatus3 = (ImageView) findViewById(R.id.remote_user_hold_status3);
        remoteUserHoldStatus4 = (ImageView) findViewById(R.id.remote_user_hold_status4);
        localRenderLayout = (PercentFrameLayout) findViewById(R.id.local_video_layout);
        screenshareRenderLayout = (PercentFrameLayout) findViewById(R.id.screenshare_video_layout);
        remoteRenderLayout = (PercentFrameLayout) findViewById(R.id.remote_video_layout);
        remoteRenderLayout2 = (PercentFrameLayout) findViewById(R.id.remote_video_layout2);
        remoteRenderLayout3 = (PercentFrameLayout) findViewById(R.id.remote_video_layout3);
        remoteRenderLayout4 = (PercentFrameLayout) findViewById(R.id.remote_video_layout4);
        initiateCallFragment = new InitiateCallFragment();
        callListFragment = new CallListFragment();
        callFragment = new CallFragment();
        hudFragment = new HudFragment();
        chatFragment = new ChatFragment();

        screenshareRemoteView = new RemoteConnectionViews("screenshare", screenshareRenderLayout, screenshareRender,
                null, null, null);

        remoteViewsInUseList.add(new RemoteConnectionViews("remote 1", remoteRenderLayout, remoteRenderScreen,
                remoteUserImage, remoteVideoLabel, remoteUserHoldStatus)); // first one is always in use
        remoteViewsList.add(new RemoteConnectionViews("remote 2", remoteRenderLayout2, remoteRenderScreen2,
                remoteUserImage2, remoteVideoLabel2, remoteUserHoldStatus2));
        remoteViewsList.add(new RemoteConnectionViews("remote 3", remoteRenderLayout3, remoteRenderScreen3,
                remoteUserImage3, remoteVideoLabel3, remoteUserHoldStatus3));
        remoteViewsList.add(new RemoteConnectionViews("remote 4", remoteRenderLayout4, remoteRenderScreen4,
                remoteUserImage4, remoteVideoLabel4, remoteUserHoldStatus4));

        // Show/hide call control fragment on view click.
        View.OnClickListener listener = new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                toggleCallControlFragmentVisibility();
            }
        };

        View.OnClickListener screenshareListener = new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                maximizeScreenshare = !maximizeScreenshare;
                updateVideoView();
            }
        };

        localRender.setOnClickListener(listener);
        screenshareRender.setOnClickListener(screenshareListener);
        remoteRenderScreen.setOnClickListener(listener);
        remoteRenderScreen2.setOnClickListener(listener);
        remoteRenderScreen3.setOnClickListener(listener);
        remoteRenderScreen4.setOnClickListener(listener);
        remoteRenderers.add(remoteRenderScreen);

        // Create video renderers.
        if (rootEglBase == null) {
            rootEglBase = EglBase.create();
        }
        localRender.init(rootEglBase.getEglBaseContext(), null);
        screenshareRender.init(rootEglBase.getEglBaseContext(), null);
        String saveRemoteVideoToFile = intent.getStringExtra(EXTRA_SAVE_REMOTE_VIDEO_TO_FILE);

        // When saveRemoteVideoToFile is set we save the video from the remote to a file.
        if (saveRemoteVideoToFile != null) {
            int videoOutWidth = intent.getIntExtra(EXTRA_SAVE_REMOTE_VIDEO_TO_FILE_WIDTH, 0);
            int videoOutHeight = intent.getIntExtra(EXTRA_SAVE_REMOTE_VIDEO_TO_FILE_HEIGHT, 0);
            try {
                videoFileRenderer = new VideoFileRenderer(saveRemoteVideoToFile, videoOutWidth, videoOutHeight,
                        rootEglBase.getEglBaseContext());
                remoteRenderers.add(videoFileRenderer);
            } catch (IOException e) {
                throw new RuntimeException("Failed to open video file for output: " + saveRemoteVideoToFile, e);
            }
        }
        remoteRenderScreen.init(rootEglBase.getEglBaseContext(), null);
        remoteRenderScreen2.init(rootEglBase.getEglBaseContext(), null);
        remoteRenderScreen3.init(rootEglBase.getEglBaseContext(), null);
        remoteRenderScreen4.init(rootEglBase.getEglBaseContext(), null);

        localRender.setZOrderMediaOverlay(true);
        localRender.setEnableHardwareScaler(true /* enabled */);

        screenshareRender.getHolder().setFormat(PixelFormat.TRANSLUCENT);
        screenshareRenderLayout.setVisibility(View.GONE);
        screenshareRender.setVisibility(View.GONE);

        if (intent.getAction().equals(ACTION_RESUME_CALL)) {
            iceConnected = true;
        }

        updateVideoView();

        // If capturing format is not specified for screencapture, use screen resolution.
        if (screencaptureEnabled && mVideoWidth == 0 && mVideoHeight == 0) {
            DisplayMetrics displayMetrics = new DisplayMetrics();
            WindowManager windowManager = (WindowManager) getApplication().getSystemService(Context.WINDOW_SERVICE);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
                windowManager.getDefaultDisplay().getRealMetrics(displayMetrics);
            }
            mVideoWidth = displayMetrics.widthPixels;
            mVideoHeight = displayMetrics.heightPixels;
        }
        DataChannelParameters dataChannelParameters = null;
        if (mDataChannelEnabled) {
            dataChannelParameters = new DataChannelParameters(mOrdered, mMaxRetrMs, mMaxRetr, mProtocol,
                    mNegotiated, mId);
        }
        peerConnectionParameters = new PeerConnectionParameters(mVideoCallEnabled, false, false, mVideoWidth,
                mVideoHeight, mCameraFps, mVideoStartBitrate, mVideoCodec, mHwCodecEnabled, mFlexfecEnabled,
                mAudioStartBitrate, mAudioCodec, mNoAudioProcessing, mAecDump, mUseOpenSLES, mDisableBuiltInAEC,
                mDisableBuiltInAGC, mDisableBuiltInNS, mEnableLevelControl, dataChannelParameters);
        commandLineRun = intent.getBooleanExtra(EXTRA_CMDLINE, false);
        runTimeMs = intent.getIntExtra(EXTRA_RUNTIME, 0);

        Log.d(TAG, "VIDEO_FILE: '" + intent.getStringExtra(EXTRA_VIDEO_FILE_AS_CAMERA) + "'");

        // Create connection client. Use DirectRTCClient if room name is an IP otherwise use the
        // standard WebSocketRTCClient.
        //if (loopback || !DirectRTCClient.IP_PATTERN.matcher(roomId).matches()) {
        //   appRtcClient = new WebSocketRTCClient(this);
        // } else {
        //   Log.i(TAG, "Using DirectRTCClient because room name looks like an IP.");
        //   appRtcClient = new DirectRTCClient(this);
        // }
        // Create connection parameters.
        //roomConnectionParameters = new RoomConnectionParameters(roomUri.toString(), roomId, loopback);

        // Create CPU monitor
        //cpuMonitor = new CpuMonitor(this);
        //hudFragment.setCpuMonitor(cpuMonitor);
        Bundle extras = intent.getExtras();
        extras.putBoolean(CallActivity.EXTRA_VIDEO_CALL, mVideoCallEnabled);

        // Send intent arguments to fragments.
        chatFragment.setArguments(getIntent().getExtras());
        initiateCallFragment.setArguments(extras);
        callFragment.setArguments(extras);
        callListFragment.setArguments(extras);
        hudFragment.setArguments(intent.getExtras());
        // Activate call and HUD fragments and start the call.
        remoteUserImage.setVisibility(View.INVISIBLE);
        FragmentTransaction ft = getFragmentManager().beginTransaction();
        if (intent.getAction().equals(ACTION_RESUME_CALL)) {
            ft.add(R.id.call_fragment_container, callFragment);
        } else {
            ft.add(R.id.call_fragment_container, initiateCallFragment);
        }
        ft.add(R.id.hud_fragment_container, hudFragment);
        ft.commit();

        // For command line execution run connection for <runTimeMs> and exit.
        if (commandLineRun && runTimeMs > 0) {
            (new Handler()).postDelayed(new Runnable() {
                @Override
                public void run() {
                    disconnect();
                }
            }, runTimeMs);
        }

        if (!intent.getAction().equals(ACTION_RESUME_CALL)) {
            peerConnectionClient = PeerConnectionClient.getInstance();

            PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
            options.disableNetworkMonitor = true;
            peerConnectionClient.setPeerConnectionFactoryOptions(options);

            if (mForceTurn) {
                peerConnectionParameters.forceTurn = true;
            }

            peerConnectionClient.createPeerConnectionFactory(CallActivity.this, peerConnectionParameters,
                    CallActivity.this);

            peerConnectionClient.setDataChannelCallback(this);

            ThumbnailsCacheManager.ThumbnailsCacheManagerInit(this);
            handleIntent(intent);

            if (screencaptureEnabled) {
                /*MediaProjectionManager mediaProjectionManager =
                    (MediaProjectionManager) getApplication().getSystemService(
                        Context.MEDIA_PROJECTION_SERVICE);
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                  startActivityForResult(
                      mediaProjectionManager.createScreenCaptureIntent(), CAPTURE_PERMISSION_REQUEST_CODE);
                }*/
            } else {
                startCall();
            }
        } else {
            mPeerId = intent.getStringExtra(EXTRA_PEER_ID);
            iceConnected = true;
            if (peerConnectionClient != null) {
                peerConnectionClient.updateActivity(this, this);
                peerConnectionClient.updateLocalRenderer(localRender);
                peerConnectionClient.updateRemoteRenderers(remoteRenderers);
            }
        }

        AddRunningIntent();
    }

    private void AddRunningIntent() {
        String notificationText = String.format(getString(R.string.in_call_with), mPeerName);

        if (mAdditionalPeers.size() != 0) {
            String users = "";
            for (RemoteConnectionViews rc : remoteViewsInUseList) {
                if (users.length() != 0) {
                    users += " " + getString(R.string.and) + " ";
                }
                users += rc.getTextView().getText();
            }
            notificationText = String.format(getString(R.string.in_conference_with), users);
        }
        NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this)
                .setSmallIcon(mVideoCallEnabled ? R.drawable.ic_videocam_white_24dp : R.drawable.ic_call_white_48dp)
                .setContentTitle(getString(R.string.drawer_video_chat)).setOngoing(true)
                .setContentText(notificationText);

        Intent callIntent = new Intent(getApplicationContext(), CallActivity.class);
        callIntent.setAction(ACTION_RESUME_CALL);
        callIntent.putExtra(CallActivity.EXTRA_VIDEO_CALL, mVideoCallEnabled);
        callIntent.putExtra(CallActivity.EXTRA_PEER_ID, mPeerId);
        callIntent.putExtra(WebsocketService.EXTRA_OWN_ID, mOwnId);

        PendingIntent resultPendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, callIntent,
                PendingIntent.FLAG_UPDATE_CURRENT);

        mBuilder.setContentIntent(resultPendingIntent);

        // Gets an instance of the NotificationManager service
        NotificationManager mNotifyMgr = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        // Builds the notification and issues it.
        mNotifyMgr.notify(1001, mBuilder.build());
    }

    RemoteConnectionViews getRemoteRenderScreen(String remoteId, String name, String url) {
        if (remoteViewsList.size() != 0) {
            RemoteConnectionViews remoteConnectionViews = remoteViewsList.get(0);
            remoteViewsList.remove(0);
            remoteConnectionViews.setId(remoteId);
            remoteViewsInUseList.add(remoteConnectionViews);

            if (remoteConnectionViews.getTextView() != null) {
                remoteConnectionViews.getTextView().setText(name);
            }
            ThumbnailsCacheManager.LoadImage(url, remoteConnectionViews.getImageView(), name, true, true);
            return remoteConnectionViews;
        }
        return null;
    }

    protected void callUser(User user) {
        mPeerId = user.Id;
        mPeerName = user.displayName;

        ThumbnailsCacheManager.LoadImage(getUrl(user.buddyPicture), remoteUserImage, user.displayName, true, true);
        initiator = true;
        signalingParameters = new SignalingParameters(WebsocketService.getIceServers(), initiator, "", "", "", null,
                null);
    }

    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
        if (requestCode == FILE_CODE && resultCode == Activity.RESULT_OK) {
            Uri path = intent.getData();
            long size = 0;
            String name = "";
            ContentResolver cr = this.getContentResolver();
            String mime = cr.getType(path);

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                size = TokenPeerConnection.getContentSize(path, this);
                name = TokenPeerConnection.getContentName(path, this);

                final int takeFlags = intent.getFlags()
                        & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
                // Check for the freshest data.
                getContentResolver().takePersistableUriPermission(path,
                        Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
            } else {
                size = TokenPeerConnection.getContentSize(path, this);
                name = TokenPeerConnection.getContentName(path, this);
            }

            // Do something with the result...
            if (mService != null) {
                SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
                fmt.setTimeZone(TimeZone.getTimeZone("GMT"));
                String time = fmt.format(new Date());
                FileInfo fileInfo = new FileInfo("", "", name, String.valueOf(size), mime);
                mService.sendFileMessage(time, mService.getAccountName(), "self", fileInfo, path.toString(), size,
                        name, mime, mFileRecipient, mService.getCurrentRoomName());

                ChatItem item = new ChatItem(time, mService.getAccountName(), fileInfo, "self", mFileRecipient);
                item.setOutgoing();

                chatFragment.addOutgoingMessage(item);
            }

        }
    }

    @Override
    protected void onNewIntent(Intent intent) {
        handleIntent(intent);
    }

    void handleIntent(Intent intent) {
        if (intent == null || intent.getAction() == null) {
            return;
        }

        if (intent.hasExtra(WebsocketService.EXTRA_ADDRESS)) {
            mServer = intent.getStringExtra(WebsocketService.EXTRA_ADDRESS);
        }

        if (intent.getAction().equals(ACTION_SHARE_FILE)) {
            User user = (User) intent.getSerializableExtra(WebsocketService.EXTRA_USER);
            mFileRecipient = user.Id;
            Intent i;
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
                i = new Intent();
                i.setAction(Intent.ACTION_GET_CONTENT);
                i.setType("*/*");
            } else {
                i = new Intent(Intent.ACTION_OPEN_DOCUMENT);
                i.addCategory(Intent.CATEGORY_OPENABLE);
                i.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
                i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                i.setType("*/*");
            }
            startActivityForResult(i, FILE_CODE);
        } else if (intent.getAction().equals(ACTION_HANG_UP)) {
            User user = (User) intent.getSerializableExtra(WebsocketService.EXTRA_USER);
            if (mAdditionalPeers.containsKey(user.Id)) {
                mAdditionalPeers.get(user.Id).getRemoteViews().setId("");
                updateRemoteViewList(mAdditionalPeers.get(user.Id).getRemoteViews());
                mAdditionalPeers.get(user.Id).close();
                mAdditionalPeers.remove(user.Id);
            } else if (mPeerId.equals(user.Id)) {
                onCallHangUp();
            }
        } else if (intent.getAction().equals(ACTION_TOGGLE_VIDEO)) {
            User user = (User) intent.getSerializableExtra(WebsocketService.EXTRA_USER);
            boolean enabled = intent.getBooleanExtra(CallActivity.EXTRA_VIDEO_CALL, true);

            if (mAdditionalPeers.containsKey(user.Id)) {
                mAdditionalPeers.get(user.Id).setVideoEnabled(enabled);
            } else if (mPeerId.equals(user.Id)) {
                onToggleVideo();
            }
        } else if (intent.getAction().equals(ACTION_TOGGLE_MIC)) {
            User user = (User) intent.getSerializableExtra(WebsocketService.EXTRA_USER);
            boolean enabled = intent.getBooleanExtra(CallActivity.EXTRA_MIC_ENABLED, true);

            if (mAdditionalPeers.containsKey(user.Id)) {
                mAdditionalPeers.get(user.Id).setAudioEnabled(enabled);
            } else if (mPeerId.equals(user.Id)) {
                onToggleMic();
            }
        } else if (intent.getAction().equals(ACTION_SEND_MESSAGE)) {
            User user = (User) intent.getSerializableExtra(WebsocketService.EXTRA_USER);
            String messageText = intent.getStringExtra(CallActivity.EXTRA_MESSAGE);

            if (mService != null) {

                SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
                fmt.setTimeZone(TimeZone.getTimeZone("GMT"));
                String time = fmt.format(new Date());
                mService.sendChatMessage(time, mService.getAccountName(), "Self", messageText, user.Id,
                        mService.getCurrentRoomName());
            }
        } else if (intent.getAction().equals(ACTION_NEW_CALL)) {

            String signaling = intent.getStringExtra(EXTRA_SIGNALING);
            if (signaling != null) {
                mSignaling = signaling;
            }

            if (mSignaling.equals("xmpp")) {
                mOwnJid = intent.getStringExtra(BroadcastTypes.EXTRA_ACCOUNT_JID);
                mPeerId = intent.getStringExtra(BroadcastTypes.EXTRA_JID);
                mPeerName = mPeerId;

                initiator = true;
                signalingParameters = new SignalingParameters(WebsocketService.getIceServers(), initiator, "", "",
                        "", null, null);
            } else {
                User user = (User) intent.getSerializableExtra(WebsocketService.EXTRA_USER);
                if (intent.hasExtra(WebsocketService.EXTRA_USERACTION)) {
                    if (mConferenceId == null) {
                        SessionIdentifierGenerator gen = new SessionIdentifierGenerator();
                        mConferenceId = mOwnId + "_" + gen.nextSessionId();
                    }
                }

                if (mPeerId.length() == 0) {
                    callUser(user);
                } else if (!mPeerId.equals(user.Id)) { // don't call if already in call
                    AdditionalPeerConnection additionalPeerConnection = new AdditionalPeerConnection(this, this,
                            this, true, user.Id, WebsocketService.getIceServers(), peerConnectionParameters,
                            rootEglBase, localRender,
                            getRemoteRenderScreen(user.Id, user.displayName, getUrl(user.buddyPicture)),
                            peerConnectionClient.getMediaStream(), "",
                            peerConnectionClient.getPeerConnectionFactory());

                    mAdditionalPeers.put(user.Id, additionalPeerConnection);
                    updateVideoView();
                }
            }
        } else if (intent.getAction().equals(WebsocketService.ACTION_ADD_ALL_CONFERENCE)) {
            ArrayList<User> users = getUsers();

            for (User user : users) {
                boolean added = false;
                if (!mPeerId.equals(user.Id) && !mAdditionalPeers.containsKey(user.Id)
                        && (mOwnId.compareTo(user.Id) < 0)) {
                    AdditionalPeerConnection additionalPeerConnection = new AdditionalPeerConnection(this, this,
                            this, true, user.Id, WebsocketService.getIceServers(), peerConnectionParameters,
                            rootEglBase, localRender,
                            getRemoteRenderScreen(user.Id, user.displayName, getUrl(user.buddyPicture)),
                            peerConnectionClient.getMediaStream(), mConferenceId,
                            peerConnectionClient.getPeerConnectionFactory());

                    mAdditionalPeers.put(user.Id, additionalPeerConnection);
                    updateVideoView();
                    added = true;
                }

            }

            if (mConferenceId == null) {
                SessionIdentifierGenerator gen = new SessionIdentifierGenerator();
                mConferenceId = mOwnId + "_" + gen.nextSessionId();
            }
        } else if (intent.getAction().equals(WebsocketService.ACTION_ADD_CONFERENCE_USER)) {
            User user = (User) intent.getSerializableExtra(WebsocketService.EXTRA_USER);
            String conferenceId = intent.getStringExtra(WebsocketService.EXTRA_CONFERENCE_ID);
            mOwnId = intent.getStringExtra(WebsocketService.EXTRA_OWN_ID);
            String userId = intent.getStringExtra(WebsocketService.EXTRA_ID);

            if (mConferenceId == null) {
                mConferenceId = conferenceId;
            }
            boolean added = false;
            if ((!mPeerId.equals(userId) && !mAdditionalPeers.containsKey(userId)
                    && (mOwnId.compareTo(userId) < 0))) {
                if (user != null) {
                    Log.d(TAG, "Calling conference: " + user.displayName);
                }

                if (peerConnectionClient.getMediaStream() != null) {
                    String displayName = getString(R.string.unknown);
                    String imgUrl = "";

                    if (user != null) {
                        displayName = user.displayName;
                        imgUrl = getUrl(user.buddyPicture);
                    }

                    AdditionalPeerConnection additionalPeerConnection = new AdditionalPeerConnection(this, this,
                            this, true, userId, WebsocketService.getIceServers(), peerConnectionParameters,
                            rootEglBase, localRender, getRemoteRenderScreen(userId, displayName, imgUrl),
                            peerConnectionClient.getMediaStream(), mConferenceId,
                            peerConnectionClient.getPeerConnectionFactory());
                    mAdditionalPeers.put(userId, additionalPeerConnection);
                    updateVideoView();
                    added = true;
                } else if (mPeerId.length() == 0) {
                    // show the call as incoming
                    String displayName = getString(R.string.unknown);
                    String imgUrl = "";

                    if (user != null) {
                        displayName = user.displayName;
                        imgUrl = getUrl(user.buddyPicture);
                    }

                    mPeerId = userId;
                    mPeerName = displayName;

                    ThumbnailsCacheManager.LoadImage(imgUrl, remoteUserImage, displayName, true, true);
                    initiator = true;
                    signalingParameters = new SignalingParameters(WebsocketService.getIceServers(), initiator, "",
                            "", "", null, null);
                } else {
                    mQueuedPeers.add(user);
                }
            }

        } else if (intent.getAction().equals(WebsocketService.ACTION_REMOTE_ICE_CANDIDATE)) {
            SerializableIceCandidate candidate = (SerializableIceCandidate) intent
                    .getParcelableExtra(WebsocketService.EXTRA_CANDIDATE);
            String id = intent.getStringExtra(WebsocketService.EXTRA_ID);
            String token = intent.getStringExtra(WebsocketService.EXTRA_TOKEN);

            if (token != null && token.length() != 0) {
                if (mTokenPeers.containsKey(candidate.from)) {
                    IceCandidate ic = new IceCandidate(candidate.sdpMid, candidate.sdpMLineIndex, candidate.sdp);
                    mTokenPeers.get(candidate.from).addRemoteIceCandidate(ic);
                }
            } else if (mAdditionalPeers.containsKey(candidate.from)) {
                IceCandidate ic = new IceCandidate(candidate.sdpMid, candidate.sdpMLineIndex, candidate.sdp);
                mAdditionalPeers.get(candidate.from).addRemoteIceCandidate(ic);
            } else if (id.equals(mSdpId)) {
                onRemoteIceCandidate(candidate, id, candidate.from);
            }

        } else if (intent.getAction().equals(WebsocketService.ACTION_REMOTE_DESCRIPTION)) {
            SerializableSessionDescription sdp = (SerializableSessionDescription) intent
                    .getSerializableExtra(WebsocketService.EXTRA_REMOTE_DESCRIPTION);
            String token = intent.getStringExtra(WebsocketService.EXTRA_TOKEN);
            String id = intent.getStringExtra(WebsocketService.EXTRA_ID);
            String conferenceId = intent.getStringExtra(WebsocketService.EXTRA_CONFERENCE_ID);
            String signaling = intent.getStringExtra(EXTRA_SIGNALING);
            String ownJid = intent.getStringExtra(BroadcastTypes.EXTRA_ACCOUNT_JID);
            mSid = intent.getStringExtra(BroadcastTypes.EXTRA_SID);
            User user = (User) intent.getSerializableExtra(WebsocketService.EXTRA_USER);
            if (mConferenceId == null && conferenceId != null && conferenceId.length() != 0) {
                mConferenceId = conferenceId;
            }
            /*else if (conferenceId != null && mConferenceId != null && !mConferenceId.equals(conferenceId)) {
              // already in a conference, reject the invite
              return;
            }*/

            if (ownJid != null) {
                mOwnJid = ownJid;
            }

            if (signaling != null && signaling.equals("xmpp")) {
                mSignaling = signaling;
            }

            if (token != null && token.length() != 0) {
                if (mTokenPeers.containsKey(sdp.from)) {
                    mTokenPeers.get(sdp.from)
                            .setRemoteDescription(new SessionDescription(sdp.type, sdp.description));
                }
            } else if (mPeerId.length() == 0 || mPeerId.equals(sdp.from)) {

                mSdpId = id;
                mPeerId = sdp.from;
                String imgUrl = "";
                if (user != null) {
                    mPeerName = user.displayName;
                    imgUrl = getUrl(user.buddyPicture);
                } else {
                    mPeerName = getString(R.string.unknown);
                }
                ThumbnailsCacheManager.LoadImage(imgUrl, remoteUserImage, mPeerName, true, true);

                if (peerConnectionClient.isConnected()) {
                    onRemoteDescription(sdp, token, id, conferenceId, "", "", "");
                } else {
                    mRemoteSdp = sdp;
                    mToken = token;
                }
            } else {
                if (mAdditionalPeers.containsKey(sdp.from)) {
                    mAdditionalPeers.get(sdp.from)
                            .setRemoteDescription(new SessionDescription(sdp.type, sdp.description));
                } else {
                    if (iceConnected && peerConnectionClient.getMediaStream() != null) {
                        addToCall(sdp, user);
                    } else {
                        mQueuedRemoteConnections.add(new RemoteConnection(sdp, user));
                    }
                }
            }
        }

    }

    String getUrl(String buddyPic) {

        if (buddyPic.length() != 0) {
            String path = buddyPic.substring(4);
            String url = "https://" + getServerAddress() + RoomActivity.BUDDY_IMG_PATH + path;
            return url;
        }
        return "";
    }

    @Override
    protected void onStart() {
        super.onStart();
        // Bind to LocalService
        Intent intent = new Intent(this, WebsocketService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        // Unbind from the service
        if (mWebsocketServiceBound) {
            unbindService(mConnection);
            mWebsocketServiceBound = false;
        }
    }

    private boolean useCamera2() {
        return Camera2Enumerator.isSupported(this) && mUseCamera2;
    }

    private boolean captureToTexture() {
        return mCaptureToTexture;
    }

    private VideoCapturer createCameraCapturer(CameraEnumerator enumerator) {
        final String[] deviceNames = enumerator.getDeviceNames();

        // First, try to find front facing camera
        Logging.d(TAG, "Looking for front facing cameras.");
        for (String deviceName : deviceNames) {
            if (enumerator.isFrontFacing(deviceName)) {
                Logging.d(TAG, "Creating front facing camera capturer.");
                VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);

                if (videoCapturer != null) {
                    return videoCapturer;
                }
            }
        }

        // Front facing camera not found, try something else
        Logging.d(TAG, "Looking for other cameras.");
        for (String deviceName : deviceNames) {
            if (!enumerator.isFrontFacing(deviceName)) {
                Logging.d(TAG, "Creating other camera capturer.");
                VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);

                if (videoCapturer != null) {
                    return videoCapturer;
                }
            }
        }

        return null;
    }

    // Activity interfaces
    @Override
    public void onPause() {
        super.onPause();
        activityRunning = false;
        // Don't stop the video when using screencapture to allow user to show other apps to the remote
        // end.
        if (peerConnectionClient != null && !screencaptureEnabled) {
            peerConnectionClient.stopVideoSource();
        }
        //cpuMonitor.pause();
    }

    @Override
    public void onResume() {
        super.onResume();
        activityRunning = true;
        // Video is not paused for screencapture. See onPause.
        if (peerConnectionClient != null && !screencaptureEnabled && !disconnected) {
            peerConnectionClient.startVideoSource();
        }
        //cpuMonitor.resume();
    }

    @Override
    protected void onDestroy() {
        disconnect();
        unregisterReceiver(mReceiver);

        if (logToast != null) {
            logToast.cancel();
        }
        activityRunning = false;
        if (disconnected || mFinishCalled) {
            rootEglBase.release();
            rootEglBase = null;
        }

        super.onDestroy();
    }

    // CallFragment.OnCallEvents interface implementation.
    @Override
    public void onCallHangUp() {

        mSoundPlayer = new SoundPlayer(this, R.raw.end1);
        mSoundPlayer.Play(false);

        disconnect();
    }

    @Override
    public void onCameraSwitch() {
        if (peerConnectionClient != null) {
            peerConnectionClient.switchCamera();
        }
    }

    @Override
    public void onVideoScalingSwitch(ScalingType scalingType) {
        this.scalingType = scalingType;
        updateVideoView();
    }

    @Override
    public void onCaptureFormatChange(int width, int height, int framerate) {
        if (peerConnectionClient != null) {
            peerConnectionClient.changeCaptureFormat(width, height, framerate);
        }
    }

    @Override
    public boolean onToggleMic() {
        if (peerConnectionClient != null) {
            micEnabled = !micEnabled;
            peerConnectionClient.setAudioEnabled(micEnabled);
            // mute additionals
            for (HashMap.Entry<String, AdditionalPeerConnection> entry : mAdditionalPeers.entrySet()) {
                AdditionalPeerConnection peer = entry.getValue();
                peer.setAudioEnabled(micEnabled);
            }
        }
        return micEnabled;
    }

    @Override
    public boolean onToggleVideo() {
        if (peerConnectionClient != null) {
            videoEnabled = !videoEnabled;
            peerConnectionClient.setVideoEnabled(videoEnabled);
            // mute additionals
            for (HashMap.Entry<String, AdditionalPeerConnection> entry : mAdditionalPeers.entrySet()) {
                AdditionalPeerConnection peer = entry.getValue();
                peer.setVideoEnabled(videoEnabled);
            }
        }
        return videoEnabled;
    }

    @Override
    public boolean onToggleSpeakerPhone() {
        if (audioManager != null) {
            speakerPhoneEnabled = !speakerPhoneEnabled;
            if (speakerPhoneEnabled) {
                audioManager.selectAudioDevice(AudioDevice.SPEAKER_PHONE);
            } else {
                audioManager.selectAudioDevice(AudioDevice.EARPIECE);
            }
        }
        return speakerPhoneEnabled;
    }

    @Override
    public boolean showUserList() {

        userListShown = !userListShown;

        if (userListShown) {
            // Show in call fragment
            FragmentTransaction ft = getFragmentManager().beginTransaction();
            ft.add(R.id.call_list_fragment_container, callListFragment);
            ft.commit();
        } else {

            // Show in call fragment
            FragmentTransaction ft = getFragmentManager().beginTransaction();
            ft.remove(callListFragment);
            ft.commit();
        }
        return userListShown;
    }

    public void showChatMessages(User user) {

        if (!chatFragment.isAdded()) {
            // Show in call fragment
            android.support.v4.app.FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
            ft.add(R.id.chat_fragment_container, chatFragment);
            ft.commit();
        }

        chatFragment.setAvatarUrl(mService.getAvatarUrl());
        Bundle args = chatFragment.getArguments();
        args.putSerializable(WebsocketService.EXTRA_USER, user);
        chatFragment.setArguments(args);
        chatFragment.setUser(user);
        chatFragment.viewChat(user.Id);

    }

    @Override
    public void showChatMessages(String roomName, String fromId, User user) {

        if (!chatFragment.isAdded()) {
            // Show in call fragment
            android.support.v4.app.FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
            ft.add(R.id.chat_fragment_container, chatFragment);
            ft.commit();
        }

    }

    // Helper functions.
    private void toggleCallControlFragmentVisibility() {

        updateVideoView();

        if (!iceConnected || !callFragment.isAdded()) {
            return;
        }
        // Show/hide call control fragment
        callControlFragmentVisible = !callControlFragmentVisible;
        FragmentTransaction ft = getFragmentManager().beginTransaction();
        if (callControlFragmentVisible) {
            ft.show(callFragment);
            ft.show(hudFragment);
        } else {
            ft.hide(callFragment);
            ft.hide(hudFragment);
        }
        ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
        ft.commit();
    }

    private void updateVideoView() {

        if (disconnected || mFinishCalled) {
            return;
        }

        int remoteHeight = REMOTE_HEIGHT;
        boolean layoutVideos = true;

        if (screenshareRenderLayout.getVisibility() == View.VISIBLE) {
            if (maximizeScreenshare) {
                layoutVideos = false;
                if (remoteViewsInUseList.size() == 1) {
                    remoteViewsInUseList.get(0).setPosition(REMOTE_X, REMOTE_Y, 20, 20);
                } else if (remoteViewsInUseList.size() == 2) {
                    remoteViewsInUseList.get(0).setPosition(REMOTE_X, REMOTE_Y, 20, 20);
                    remoteViewsInUseList.get(1).setPosition(80, 0, 20, 20);
                } else if (remoteViewsInUseList.size() == 3) {
                    remoteViewsInUseList.get(0).setPosition(REMOTE_X, REMOTE_Y, 20, 20);
                    remoteViewsInUseList.get(1).setPosition(80, REMOTE_Y, 20, 20);
                    remoteViewsInUseList.get(2).setPosition(REMOTE_X, 80, 20, 20);
                } else if (remoteViewsInUseList.size() == 4) {
                    remoteViewsInUseList.get(0).setPosition(REMOTE_X, REMOTE_Y, 20, 20);
                    remoteViewsInUseList.get(1).setPosition(80, REMOTE_Y, 20, 20);
                    remoteViewsInUseList.get(2).setPosition(REMOTE_X, 80, 20, 20);
                    remoteViewsInUseList.get(3).setPosition(80, 80, 20, 20);
                }

                screenshareRenderLayout.setPosition(REMOTE_X, REMOTE_Y, REMOTE_WIDTH, REMOTE_HEIGHT);
                if (screenshareFrontIndex == 0) {
                    screenshareFrontIndex = ((ViewGroup) screenshareRenderLayout.getParent())
                            .indexOfChild(screenshareRenderLayout);
                }
                sendToBack(screenshareRenderLayout);
            } else {
                layoutVideos = true;
                bringToFront(screenshareRenderLayout);
                screenshareRenderLayout.setPosition(REMOTE_X, LOCAL_Y_CONNECTED, LOCAL_WIDTH_CONNECTED,
                        LOCAL_HEIGHT_CONNECTED);
            }

            for (RemoteConnectionViews remoteConnectionViews : remoteViewsList) {
                remoteConnectionViews.getFrameLayout().setVisibility(View.INVISIBLE);
            }

            screenshareRender.setScalingType(ScalingType.SCALE_ASPECT_FIT);
            screenshareRender.requestLayout();
        }

        if (layoutVideos) {
            if (remoteViewsInUseList.size() == 2) {
                remoteHeight = REMOTE_HEIGHT2;
                remoteViewsInUseList.get(0).setPosition(REMOTE_X, REMOTE_Y, REMOTE_WIDTH, remoteHeight);
                remoteViewsInUseList.get(1).setPosition(REMOTE_X, remoteHeight, REMOTE_WIDTH, remoteHeight);
                remoteViewsInUseList.get(1).getFrameLayout().setVisibility(View.VISIBLE);
                remoteViewsInUseList.get(1).getSurfaceViewRenderer().setVisibility(View.VISIBLE);

                for (RemoteConnectionViews remoteConnectionViews : remoteViewsList) {
                    remoteConnectionViews.getFrameLayout().setVisibility(View.INVISIBLE);
                }
                Log.i(TAG, "Showing 2 video windows");
            } else if (remoteViewsInUseList.size() == 3) {
                remoteHeight = REMOTE_HEIGHT2;
                remoteViewsInUseList.get(0).setPosition(REMOTE_X, REMOTE_Y, REMOTE_WIDTH, remoteHeight);
                remoteViewsInUseList.get(1).setPosition(REMOTE_X, remoteHeight, REMOTE_WIDTH2, remoteHeight);
                remoteViewsInUseList.get(2).setPosition(REMOTE_X2, remoteHeight, REMOTE_WIDTH2, remoteHeight);

                for (RemoteConnectionViews remoteConnectionViews : remoteViewsInUseList) {
                    remoteConnectionViews.getFrameLayout().setVisibility(View.VISIBLE);
                    remoteConnectionViews.getSurfaceViewRenderer().setVisibility(View.VISIBLE);
                }

                for (RemoteConnectionViews remoteConnectionViews : remoteViewsList) {
                    remoteConnectionViews.getFrameLayout().setVisibility(View.INVISIBLE);
                }
                Log.i(TAG, "Showing 3 video windows");
            } else if (remoteViewsInUseList.size() == 4) {
                remoteHeight = REMOTE_HEIGHT2;
                remoteViewsInUseList.get(0).setPosition(REMOTE_X, REMOTE_Y, REMOTE_WIDTH2, remoteHeight);
                remoteViewsInUseList.get(1).setPosition(REMOTE_X2, REMOTE_Y, REMOTE_WIDTH2, remoteHeight);
                remoteViewsInUseList.get(2).setPosition(REMOTE_X, remoteHeight, REMOTE_WIDTH2, remoteHeight);
                remoteViewsInUseList.get(3).setPosition(REMOTE_X2, remoteHeight, REMOTE_WIDTH2, remoteHeight);

                for (RemoteConnectionViews remoteConnectionViews : remoteViewsInUseList) {
                    remoteConnectionViews.getFrameLayout().setVisibility(View.VISIBLE);
                    remoteConnectionViews.getSurfaceViewRenderer().setVisibility(View.VISIBLE);
                }
                Log.i(TAG, "Showing 4 video windows");
            } else {
                remoteViewsInUseList.get(0).setPosition(REMOTE_X, REMOTE_Y, REMOTE_WIDTH, REMOTE_HEIGHT);
                for (RemoteConnectionViews remoteConnectionViews : remoteViewsList) {
                    remoteConnectionViews.getFrameLayout().setVisibility(View.INVISIBLE);
                    remoteConnectionViews.getSurfaceViewRenderer().setVisibility(View.INVISIBLE);
                }
                Log.i(TAG, "Showing 1 video windows");
            }
        }

        for (RemoteConnectionViews remoteConnectionViews : remoteViewsInUseList) {
            remoteConnectionViews.getSurfaceViewRenderer().setScalingType(scalingType);
            remoteConnectionViews.getSurfaceViewRenderer().setMirror(false);
        }

        if (mVideoCallEnabled) {
            if (iceConnected) {
                //localRenderLayout.setVisibility(View.GONE);
                localRenderLayout.setPosition(LOCAL_X_CONNECTED, LOCAL_Y_CONNECTED, LOCAL_WIDTH_CONNECTED,
                        LOCAL_HEIGHT_CONNECTED);
                localRender.setScalingType(ScalingType.SCALE_ASPECT_FIT);
                Log.i(TAG, "Showing small local video window");
            } else {
                localRenderLayout.setVisibility(View.VISIBLE);
                localRenderLayout.setPosition(LOCAL_X_CONNECTING, LOCAL_Y_CONNECTING, LOCAL_WIDTH_CONNECTING,
                        LOCAL_HEIGHT_CONNECTING);
                localRender.setScalingType(scalingType);
                Log.i(TAG, "Showing large local video window");
            }
            localRender.setMirror(true);

            localRender.requestLayout();
        } else {
            localRenderLayout.setVisibility(View.GONE);
        }

        for (RemoteConnectionViews remoteConnectionViews : remoteViewsInUseList) {
            remoteConnectionViews.getSurfaceViewRenderer().requestLayout();
        }

    }

    void sendToBack(View child) {
        final ViewGroup parent = (ViewGroup) child.getParent();
        if (null != parent) {
            parent.removeView(child);
            parent.addView(child, 0);
        }
    }

    void bringToFront(View child) {
        final ViewGroup parent = (ViewGroup) child.getParent();
        if (null != parent) {
            parent.removeView(child);
            parent.addView(child, screenshareFrontIndex);
        }
    }

    private void startCall() {

        callStartedTimeMs = System.currentTimeMillis();

        // Start room connection.
        //logAndToast(getString(R.string.connecting_to, roomConnectionParameters.roomUrl));
        //mService.connectToRoom(roomConnectionParameters);

        // Create and audio manager that will take care of audio routing,
        // audio modes, audio device enumeration etc.
        audioManager = AppRTCAudioManager.create(getApplicationContext());
        // Store existing audio settings and change audio mode to
        // MODE_IN_COMMUNICATION for best possible VoIP performance.
        Log.d(TAG, "Starting the audio manager...");
        audioManager.start(new AudioManagerEvents() {
            // This method will be called each time the number of available audio
            // devices has changed.
            @Override
            public void onAudioDeviceChanged(AudioDevice audioDevice, Set<AudioDevice> availableAudioDevices) {
                onAudioManagerDevicesChanged(audioDevice, availableAudioDevices);
            }
        });
    }

    // Should be called from UI thread
    private void callConnected() {
        connectedUserIds.add(mPeerId);
        connectedUserIds.add(mOwnId);

        LinearLayout connecting_layout = (LinearLayout) findViewById(R.id.connecting_progress_layout);
        if (connecting_layout != null) {
            connecting_layout.setVisibility(View.GONE);
        }

        final long delta = System.currentTimeMillis() - callStartedTimeMs;
        Log.i(TAG, "Call connected: delay=" + delta + "ms");
        if (peerConnectionClient == null || isError) {
            Log.w(TAG, "Call is connected in closed or error state");
            return;
        }
        // Update video view.
        updateVideoView();

        // incoming calls pending
        for (RemoteConnection remoteConnection : mQueuedRemoteConnections) {
            addToCall(remoteConnection.sdp, remoteConnection.user);
        }
        mQueuedRemoteConnections.clear();

        for (User user : mQueuedPeers) {
            addToCall(user, "", true, null);
        }
        mQueuedPeers.clear();

        // Enable statistics callback.
        //peerConnectionClient.enableStatsEvents(true, STAT_CALLBACK_PERIOD);
    }

    // This method is called when the audio manager reports audio device change,
    // e.g. from wired headset to speakerphone.
    private void onAudioManagerDevicesChanged(final AudioDevice device, final Set<AudioDevice> availableDevices) {
        Log.d(TAG, "onAudioManagerDevicesChanged: " + availableDevices + ", " + "selected: " + device);
        // TODO(henrika): add callback handler.
    }

    // Disconnect from remote resources, dispose of local resources, and exit.
    private void disconnect() {

        if (!disconnected) {
            // Gets an instance of the NotificationManager service
            NotificationManager mNotifyMgr = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
            mNotifyMgr.cancel(1001);

            disconnected = true; // only call disconnect once per activity
            activityRunning = false;
            if (mService != null) {
                mService.sendBye(mPeerId);
            }

            for (HashMap.Entry<String, AdditionalPeerConnection> entry : mTokenPeers.entrySet()) {
                AdditionalPeerConnection additionalPeerConnection = entry.getValue();
                additionalPeerConnection.close();

                if (mService != null) {
                    mService.sendBye(entry.getKey());
                }
            }

            for (HashMap.Entry<String, AdditionalPeerConnection> entry : mAdditionalPeers.entrySet()) {
                AdditionalPeerConnection additionalPeerConnection = entry.getValue();
                additionalPeerConnection.close();

                if (mService != null) {
                    mService.sendBye(entry.getKey());
                }
            }

            if (peerConnectionClient != null) {
                peerConnectionClient.close();
                peerConnectionClient = null;
            }

            remoteViewsList.clear();
            remoteViewsInUseList.clear();

            if (localRender != null) {
                localRender.release();
                localRender = null;
            }
            if (screenshareRender != null) {
                screenshareRender.release();
                screenshareRender = null;
            }
            if (videoFileRenderer != null) {
                videoFileRenderer.release();
                videoFileRenderer = null;
            }
            if (remoteRenderScreen != null) {
                remoteRenderScreen.release();
                remoteRenderScreen = null;
            }
            if (remoteRenderScreen2 != null) {
                remoteRenderScreen2.release();
                remoteRenderScreen2 = null;
            }
            if (remoteRenderScreen3 != null) {
                remoteRenderScreen3.release();
                remoteRenderScreen3 = null;
            }
            if (remoteRenderScreen4 != null) {
                remoteRenderScreen4.release();
                remoteRenderScreen4 = null;
            }
            if (audioManager != null) {
                audioManager.stop();
                audioManager = null;
            }
            if (iceConnected && !isError) {
                setResult(RESULT_OK);
            } else {
                setResult(RESULT_CANCELED);
            }
        }
    }

    private void disconnectWithErrorMessage(final String errorMessage) {
        if (commandLineRun || !activityRunning) {
            Log.e(TAG, "Critical error: " + errorMessage);
            disconnect();
        } else {
            new AlertDialog.Builder(this).setTitle(getText(R.string.channel_error_title)).setMessage(errorMessage)
                    .setCancelable(false).setNeutralButton(R.string.ok, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int id) {
                            dialog.cancel();
                            //disconnect();
                        }
                    }).create().show();
        }
    }

    // Log |msg| and Toast about it.
    private void logAndToast(String msg) {
        Log.d(TAG, msg);
        if (logToast != null) {
            logToast.cancel();
        }
        logToast = Toast.makeText(this, msg, Toast.LENGTH_SHORT);
        logToast.show();
    }

    private void showHoldMessage(final boolean on) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                remoteViewsInUseList.get(0).setHoldStatus(on);
                if (on) {
                    callListFragment.updateUserState(User.CallState.HOLD, mPeerId);

                } else {
                    callListFragment.updateUserState(User.CallState.CONNECTED, mPeerId);
                }
            }
        });
    }

    private void reportError(final String description) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                /*if (!isError) {
                  isError = true;
                  disconnectWithErrorMessage(description);
                }*/
                logAndToast(description);
            }
        });
    }

    protected VideoCapturer createVideoCapturer() {
        VideoCapturer videoCapturer = null;
        String videoFileAsCamera = getIntent().getStringExtra(EXTRA_VIDEO_FILE_AS_CAMERA);
        if (videoFileAsCamera != null) {
            try {
                videoCapturer = new FileVideoCapturer(videoFileAsCamera);
            } catch (IOException e) {
                reportError("Failed to open video file for emulated camera");
                return null;
            }
        } else if (screencaptureEnabled) {
            if (mediaProjectionPermissionResultCode != Activity.RESULT_OK) {
                reportError("User didn't give permission to capture the screen.");
                return null;
            }
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                return new ScreenCapturerAndroid(mediaProjectionPermissionResultData,
                        new MediaProjection.Callback() {
                            @Override
                            public void onStop() {
                                reportError("User revoked permission to capture the screen.");
                            }
                        });
            }
        } else if (useCamera2()) {
            if (!captureToTexture()) {
                mCaptureToTexture = true;
            }

            Logging.d(TAG, "Creating capturer using camera2 API.");
            videoCapturer = createCameraCapturer(new Camera2Enumerator(this));
        } else {
            Logging.d(TAG, "Creating capturer using camera1 API.");
            videoCapturer = createCameraCapturer(new Camera1Enumerator(captureToTexture()));
        }
        if (videoCapturer == null) {
            reportError("Failed to open camera");
            return null;
        }
        return videoCapturer;
    }

    private void setupCall(SignalingParameters params) {
        final long delta = System.currentTimeMillis() - callStartedTimeMs;

        signalingParameters = params;
        // logAndToast("Creating peer connection, delay=" + delta + "ms");
        VideoCapturer videoCapturer = null;
        if (peerConnectionParameters.videoCallEnabled) {
            videoCapturer = createVideoCapturer();
        }

        if (peerConnectionClient != null && rootEglBase != null) {
            peerConnectionClient.createPeerConnection(rootEglBase.getEglBaseContext(), localRender, remoteRenderers,
                    videoCapturer, signalingParameters);

            remoteVideoLabel.setText(mPeerName);

            if (signalingParameters.initiator) {
                // logAndToast("Creating OFFER...");
                // Create offer. Offer SDP will be sent to answering client in
                // PeerConnectionEvents.onLocalDescription event.
                peerConnectionClient.createOffer();
            } else {
                if (params.offerSdp != null) {
                    peerConnectionClient.setRemoteDescription(params.offerSdp, params.token);
                    //  logAndToast("Creating ANSWER...");
                    // Create answer. Answer SDP will be sent to offering client in
                    // PeerConnectionEvents.onLocalDescription event.
                    peerConnectionClient.createAnswer();
                }
                if (params.iceCandidates != null) {
                    // Add remote ICE candidates from room.
                    for (IceCandidate iceCandidate : params.iceCandidates) {
                        peerConnectionClient.addRemoteIceCandidate(iceCandidate);
                    }
                }
            }
        }
    }

    // -----Implementation of AppRTCClient.AppRTCSignalingEvents ---------------
    // All callbacks are invoked from websocket signaling looper thread and
    // are routed to UI thread.
    private void onConnectedToRoomInternal(final String roomName) {

    }

    @Override
    public void onConnectedToRoom(final String roomName) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                onConnectedToRoomInternal(roomName);
            }
        });
    }

    @Override
    public void onRemoteDescription(final SerializableSessionDescription sdp, final String token, String id,
            String conferenceId, final String fromId, final String roomName, String type) {
        final long delta = System.currentTimeMillis() - callStartedTimeMs;
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                if (peerConnectionClient == null) {
                    Log.e(TAG, "Received remote SDP for non-initilized peer connection.");
                    return;
                }
                //logAndToast("Received remote " + sdp.type + ", delay=" + delta + "ms");
                SessionDescription sd = new SessionDescription(sdp.type, sdp.description);
                peerConnectionClient.setRemoteDescription(sd, token);
                if (!initiator) {
                    //logAndToast("Creating ANSWER...");
                    // Create answer. Answer SDP will be sent to offering client in
                    // PeerConnectionEvents.onLocalDescription event.
                    peerConnectionClient.createAnswer();
                } else if (sd.type == SessionDescription.Type.OFFER) {
                    // tried to call
                    SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
                    fmt.setTimeZone(TimeZone.getTimeZone("GMT"));
                    String time = fmt.format(new Date());
                    String missedCall = String.format(getString(R.string.missed_call), mPeerName);

                    mService.onChatMessage(missedCall, time, "", mOwnId, mPeerId, roomName);
                    sendBye(fromId, "busy");
                    onCallHangUp();
                } else {

                    // Show in call fragment
                    FragmentTransaction ft = getFragmentManager().beginTransaction();
                    if (!mVideoCallEnabled) {
                        ft.add(R.id.call_list_fragment_container, callListFragment);
                    }
                    ft.replace(R.id.call_fragment_container, callFragment);
                    ft.commit();

                    if (!mVideoCallEnabled) {
                        remoteUserImage.setVisibility(View.VISIBLE);
                    }
                }
            }
        });
    }

    @Override
    public void onRemoteIceCandidate(final SerializableIceCandidate candidate, String id, String from) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                if (peerConnectionClient == null) {
                    Log.e(TAG, "Received ICE candidate for a non-initialized peer connection.");
                    return;
                }

                IceCandidate ic = new IceCandidate(candidate.sdpMid, candidate.sdpMLineIndex, candidate.sdp);
                peerConnectionClient.addRemoteIceCandidate(ic);
            }
        });
    }

    @Override
    public void onRemoteIceCandidatesRemoved(final SerializableIceCandidate[] candidates) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                if (peerConnectionClient == null) {
                    Log.e(TAG, "Received ICE candidate removals for a non-initialized peer connection.");
                    return;
                }
                IceCandidate[] ic = new IceCandidate[candidates.length];
                for (int candidate = 0; candidate < candidates.length; candidate++) {
                    ic[candidate] = new IceCandidate(candidates[candidate].sdpMid,
                            candidates[candidate].sdpMLineIndex, candidates[candidate].sdp);
                }
                peerConnectionClient.removeRemoteIceCandidates(ic);
            }
        });
    }

    @Override
    public void onChannelOpen() {

    }

    @Override
    public void onChannelClose() {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                //logAndToast("Remote end hung up; dropping PeerConnection");
                disconnect();
            }
        });
    }

    @Override
    public void onChannelError(final String description) {
        reportError(description);
    }

    @Override
    public void clearRoomUsers(String room) {

    }

    @Override
    public void onUserEnteredRoom(User user, String room) {

    }

    @Override
    public void onUserLeftRoom(User user, String room) {

    }

    @Override
    public void onBackPressed() {
        if (chatFragment.isAdded()) {
            // Show in call fragment
            android.support.v4.app.FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
            ft.remove(chatFragment);
            ft.commit();

        } else {
            super.onBackPressed();
        }
    }

    @Override
    public void onBye(final String reason, String fromId, String roomName) {

        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                logAndToast(reason);
                disconnect();
            }
        });
    }

    @Override
    public void sendBye(String mPeerId) {

    }

    @Override
    public void sendBye(String mPeerId, String reason) {

    }

    @Override
    public void onPatchResponse(String response) {

    }

    @Override
    public void onPostResponse(String response) {

    }

    @Override
    public void onConfigResponse(String response) {

    }

    @Override
    public void onChatMessage(String message, String time, String status, String to, String fromId,
            String roomName) {

    }

    @Override
    public void onFileMessage(String time, String id, String chunks, String name, String size, String filetype,
            String mIdFrom, String mRoomName) {

    }

    @Override
    public void onAddTurnServer(String url, String username, String password) {

    }

    @Override
    public void onAddStunServer(String url, String username, String password) {

    }

    @Override
    public void onSelf() {

    }

    @Override
    public void onIdChanged(String id, String sid) {

    }

    @Override
    public void onTurnTtl(int ttl) {

    }

    @Override
    public void onConferenceUser(String roomName, String conferenceId, String id) {

    }

    @Override
    public void onError(String code, String message, String roomName) {

    }

    @Override
    public void onScreenShare(final String userId, final String id, String roomName) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                ArrayList<User> users = getUsers();

                if (users != null) {
                    for (User user : users) {
                        if (user.Id.equals(userId)) {
                            String label = "";
                            label = String.format(getString(R.string.screenshare_offer), user.displayName);

                            final User screenshareUser = user;
                            Snackbar snackbar = Snackbar
                                    .make(findViewById(R.id.snackbar_container), label, Snackbar.LENGTH_INDEFINITE)
                                    .setAction(getString(R.string.accept), new View.OnClickListener() {
                                        @Override
                                        public void onClick(View view) {
                                            addToCall(screenshareUser, id, false, null);
                                        }
                                    });

                            snackbar.show();

                            break;
                        }
                    }
                }
            }
        });

    }

    public void onScreenShare(final String userId, final String id, final PeerConnectionClient signalingClient) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                ArrayList<User> users = getUsers();

                if (users != null) {
                    for (User user : users) {
                        if (user.Id.equals(userId)) {
                            String label = "";
                            label = String.format(getString(R.string.screenshare_offer), user.displayName);

                            final User screenshareUser = user;
                            Snackbar snackbar = Snackbar
                                    .make(findViewById(R.id.snackbar_container), label, Snackbar.LENGTH_INDEFINITE)
                                    .setAction(getString(R.string.accept), new View.OnClickListener() {
                                        @Override
                                        public void onClick(View view) {
                                            addToCall(screenshareUser, id, false, null);
                                        }
                                    });

                            snackbar.show();

                            break;
                        }
                    }
                }
            }
        });

    }

    // -----Implementation of PeerConnectionClient.PeerConnectionEvents.---------
    // Send local peer connection SDP and ICE candidates to remote party.
    // All callbacks are invoked from peer connection client looper thread and
    // are routed to UI thread.
    @Override
    public void onLocalDescription(final SessionDescription sdp) {
        mLocalSdp = sdp;
        final long delta = System.currentTimeMillis() - callStartedTimeMs;
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                if (mService != null) {
                    //logAndToast("Sending " + sdp.type + ", delay=" + delta + "ms");
                    if (initiator) {
                        // mService.sendOfferSdp(sdp);
                    } else {
                        if (mAnswerPressed && !mSentAnswer) {
                            AnswerIncomingCall(mPeerId);
                        }
                    }
                }

                if (mWaitingToStartCall) {
                    mWaitingToStartCall = false;
                    StartCall(mPeerId);
                }

                if (initiateCallFragment != null) {
                    initiateCallFragment.enableCallButtons();
                }

                if (peerConnectionParameters.videoMaxBitrate > 0) {
                    Log.d(TAG, "Set video maximum bitrate: " + peerConnectionParameters.videoMaxBitrate);
                    peerConnectionClient.setVideoMaxBitrate(peerConnectionParameters.videoMaxBitrate);
                }
            }
        });
    }

    @Override
    public void onIceCandidate(final SerializableIceCandidate candidate) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mIceCandidates.put(candidate.sdp, candidate);
                if (!mWaitingToStartCall && mService != null) {
                    mService.sendLocalIceCandidate(candidate, "", "", mPeerId);
                }
                if (initiateCallFragment != null) {
                    initiateCallFragment.enableCallButtons();
                }

                if (mWaitingToStartCall) {
                    mWaitingToStartCall = false;
                    StartCall(mPeerId);
                }
            }
        });
    }

    @Override
    public void onIceCandidatesRemoved(final SerializableIceCandidate[] candidates) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                if (mService != null) {
                    mService.sendLocalIceCandidateRemovals(candidates, mPeerId);
                }
            }
        });
    }

    @Override
    public void onVideoEnabled() {
        remoteUserImage.setVisibility(View.INVISIBLE);
    }

    @Override
    public void onVideoDisabled() {
        remoteUserImage.setVisibility(View.VISIBLE);
    }

    @Override
    public void onIceConnected() {
        final long delta = System.currentTimeMillis() - callStartedTimeMs;
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                //logAndToast("ICE connected, delay=" + delta + "ms");
                iceConnected = true;
                callConnected();
                callListFragment.updateUserState(User.CallState.CONNECTED, mPeerId);
            }
        });

    }

    private void addToCall(SerializableSessionDescription sdp, User user) {
        String id = sdp.from;
        String displayName = getString(R.string.unknown);
        String imgUrl = "";

        if (user != null) {
            displayName = user.displayName;
            imgUrl = getUrl(user.buddyPicture);
        }

        AdditionalPeerConnection additionalPeerConnection = new AdditionalPeerConnection(this, this, this, false,
                sdp.from, WebsocketService.getIceServers(), peerConnectionParameters, rootEglBase, localRender,
                getRemoteRenderScreen(id, displayName, imgUrl), peerConnectionClient.getMediaStream(),
                mConferenceId, peerConnectionClient.getPeerConnectionFactory());

        additionalPeerConnection.setRemoteDescription(new SessionDescription(sdp.type, sdp.description));
        mAdditionalPeers.put(sdp.from, additionalPeerConnection);
        updateVideoView();
    }

    private void addToCall(User user, String token, boolean sendMedia, PeerConnectionClient signalingClient) {

        AdditionalPeerConnection additionalPeerConnection = new AdditionalPeerConnection(this, this, this, true,
                user.Id, WebsocketService.getIceServers(), peerConnectionParameters, rootEglBase, localRender,
                screenshareRemoteView, token, mConferenceId, peerConnectionClient.getPeerConnectionFactory());
        additionalPeerConnection.setToken(token, signalingClient);
        if (token.length() != 0) {
            mTokenPeers.put(user.Id, additionalPeerConnection);
            screenshareRenderLayout.setVisibility(View.VISIBLE);
            screenshareRender.setVisibility(View.VISIBLE);
        } else {
            mAdditionalPeers.put(user.Id, additionalPeerConnection);
        }
        updateVideoView();
    }

    @Override
    public void onIceDisconnected() {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                logAndToast("ICE disconnected");
                iceConnected = false;
                //disconnect();
            }
        });
    }

    @Override
    public void onPeerConnectionClosed() {
        if (!mFinishCalled) {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    finish();
                }
            });
            mFinishCalled = true;
        }
    }

    @Override
    public void onPeerConnectionStatsReady(final StatsReport[] reports) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                if (!isError && iceConnected) {
                    hudFragment.updateEncoderStatistics(reports);
                }
            }
        });
    }

    @Override
    public void onPeerConnectionError(final String description) {
        reportError(description);
    }

    @Override
    public void onPeerConnectionFactoryCreated() {

        runOnUiThread(new Runnable() {

            @Override
            public void run() {
                if (mRemoteSdp != null) {
                    SessionDescription sdp = new SessionDescription(mRemoteSdp.type, mRemoteSdp.description);
                    signalingParameters.offerSdp = sdp;
                }
                if (mToken != null) {
                    signalingParameters.token = mToken;
                }
                setupCall(signalingParameters);
            }
        });

    }

    @Override
    public void onPeerConnectionCreated() {

    }

    @Override
    public void onRemoteSdpSet() {

    }

    void AnswerIncomingCall(String to) {
        mAnswerPressed = true;

        if (mLocalSdp != null) {
            if (mService != null && mSignaling.equals("spreed")) {
                mService.sendAnswerSdp(mLocalSdp, to);
                for (HashMap.Entry<String, SerializableIceCandidate> entry : mIceCandidates.entrySet()) {
                    SerializableIceCandidate candidate = entry.getValue();
                    mService.sendLocalIceCandidate(candidate, mToken, mSdpId, mPeerId);
                }
                mIceCandidates.clear();
                mSentAnswer = true;
            } else if (mSignaling.equals("xmpp")) {
                SerializableSessionDescription sdp = new SerializableSessionDescription(mLocalSdp.type,
                        mLocalSdp.description, "");
                Intent broadcastIntent = new Intent();
                broadcastIntent.setAction(ACTION_SEND_SESSION_DESCRIPTION);
                broadcastIntent.putExtra(BroadcastTypes.EXTRA_REMOTE_DESCRIPTION, sdp);
                broadcastIntent.putExtra(BroadcastTypes.EXTRA_JID, mPeerId);
                broadcastIntent.putExtra(BroadcastTypes.EXTRA_ACCOUNT_JID, mOwnJid);
                broadcastIntent.putExtra(BroadcastTypes.EXTRA_SID, mSid);
                broadcastIntent.putExtra(EXTRA_SIGNALING, "xmpp");
                ArrayList<SerializableIceCandidate> candidates = new ArrayList<SerializableIceCandidate>();
                for (HashMap.Entry<String, SerializableIceCandidate> entry : mIceCandidates.entrySet()) {
                    SerializableIceCandidate candidate = entry.getValue();
                    candidates.add(candidate);
                }
                broadcastIntent.putExtra(EXTRA_CANDIDATES, candidates);
                sendBroadcast(broadcastIntent);
                mIceCandidates.clear();
                mSentAnswer = true;
            }
        }
    }

    void StartCall(String to) {
        if (mSignaling.equals("spreed")) {
            if (mService == null) {
                return;
            }

            if (mConferenceId != null) {
                // send the conference invites
                mService.sendConferenceOfferSdp(mLocalSdp, to, mConferenceId);
            } else {
                mService.sendOfferSdp(mLocalSdp, to);
            }

            for (HashMap.Entry<String, SerializableIceCandidate> entry : mIceCandidates.entrySet()) {
                SerializableIceCandidate candidate = entry.getValue();
                mService.sendLocalIceCandidate(candidate, mToken, mSdpId, mPeerId);
            }
            mIceCandidates.clear();
        } else if (mSignaling.equals("xmpp")) {
            SerializableSessionDescription sdp = new SerializableSessionDescription(mLocalSdp.type,
                    mLocalSdp.description, "");
            Intent broadcastIntent = new Intent();
            broadcastIntent.setAction(ACTION_SEND_SESSION_DESCRIPTION);
            broadcastIntent.putExtra(BroadcastTypes.EXTRA_REMOTE_DESCRIPTION, sdp);
            broadcastIntent.putExtra(BroadcastTypes.EXTRA_JID, mPeerId);
            broadcastIntent.putExtra(BroadcastTypes.EXTRA_ACCOUNT_JID, mOwnJid);
            broadcastIntent.putExtra(BroadcastTypes.EXTRA_SID, mSid);
            broadcastIntent.putExtra(EXTRA_SIGNALING, "xmpp");
            ArrayList<SerializableIceCandidate> candidates = new ArrayList<SerializableIceCandidate>();
            for (HashMap.Entry<String, SerializableIceCandidate> entry : mIceCandidates.entrySet()) {
                SerializableIceCandidate candidate = entry.getValue();
                candidates.add(candidate);
            }
            broadcastIntent.putExtra(EXTRA_CANDIDATES, candidates);
            sendBroadcast(broadcastIntent);
            mIceCandidates.clear();
            mSentAnswer = true;
        }
    }

    public String getPeerId() {
        return mPeerId;
    }

    public void onRejectCall() {
        mService.sendBye(mPeerId);
        if (!mFinishCalled) {
            mFinishCalled = true;
            disconnect();
            finish();
        }
    }

    @Override
    public void onAnswerCall() {
        AnswerIncomingCall(mPeerId);
        FragmentTransaction ft = getFragmentManager().beginTransaction();
        if (!mVideoCallEnabled) {
            ft.add(R.id.call_list_fragment_container, callListFragment);
        }
        ft.replace(R.id.call_fragment_container, callFragment);
        ft.commit();

        if (!mVideoCallEnabled) {
            remoteUserImage.setVisibility(View.VISIBLE);
        }
    }

    @Override
    public void onStartCall() {
        mWaitingToStartCall = true;
    }

    @Override
    public void onStartConferenceCall(User user) {
        FragmentTransaction ft = getFragmentManager().beginTransaction();
        ft.replace(R.id.call_fragment_container, callFragment);
        ft.commit();

        if (!mVideoCallEnabled) {
            remoteUserImage.setVisibility(View.VISIBLE);
        }

        callUser(user);
        updateVideoView();

        if (mLocalSdp == null) {
            mWaitingToStartCall = true;
        } else {
            StartCall(mPeerId);
        }
    }

    /**
     * Get a value from the shared preference or from the intent, if it does not
     * exist the default is used.
     */
    private boolean sharedPrefGetBoolean(int attributeId, String intentName, int defaultId) {
        boolean defaultValue = Boolean.valueOf(getString(defaultId));

        Intent intent = getIntent();
        if (intent != null && intent.hasExtra(intentName)) {
            return intent.getBooleanExtra(intentName, defaultValue);
        }

        String attributeName = getString(attributeId);
        return sharedPref.getBoolean(attributeName, defaultValue);

    }

    /**
     * Get a value from the shared preference or from the intent, if it does not
     * exist the default is used.
     */
    private String sharedPrefGetString(int attributeId, String intentName, int defaultId) {
        String defaultValue = getString(defaultId);
        String attributeName = getString(attributeId);
        return sharedPref.getString(attributeName, defaultValue);

    }

    /**
     * Get a value from the shared preference or from the intent, if it does not
     * exist the default is used.
     */
    private int sharedPrefGetInteger(int attributeId, String intentName, int defaultId) {
        String defaultString = getString(defaultId);
        int defaultValue = Integer.parseInt(defaultString);

        String attributeName = getString(attributeId);
        String value = sharedPref.getString(attributeName, defaultString);
        try {
            return Integer.parseInt(value);
        } catch (NumberFormatException e) {
            Log.e(TAG, "Wrong setting for: " + attributeName + ":" + value);
            return defaultValue;
        }

    }

    private void readPrefs() {

        speakerPhoneEnabled = !sharedPrefGetString(R.string.pref_speakerphone_key, "",
                R.string.pref_speakerphone_default).equals("false");

        // for testing
        mForceTurn = sharedPrefGetBoolean(R.string.pref_force_relay_key, "", R.string.pref_force_relay_default);

        // Video call enabled flag.
        mVideoCallEnabled = sharedPrefGetBoolean(R.string.pref_videocall_key, CallActivity.EXTRA_VIDEO_CALL,
                R.string.pref_videocall_default);

        // Use screencapture option.
        screencaptureEnabled = sharedPrefGetBoolean(R.string.pref_screencapture_key,
                CallActivity.EXTRA_SCREENCAPTURE, R.string.pref_screencapture_default);

        // Use Camera2 option.
        mUseCamera2 = sharedPrefGetBoolean(R.string.pref_camera2_key, CallActivity.EXTRA_CAMERA2,
                R.string.pref_camera2_default);

        // Get default codecs.
        mVideoCodec = sharedPrefGetString(R.string.pref_videocodec_key, CallActivity.EXTRA_VIDEOCODEC,
                R.string.pref_videocodec_default);
        mAudioCodec = sharedPrefGetString(R.string.pref_audiocodec_key, CallActivity.EXTRA_AUDIOCODEC,
                R.string.pref_audiocodec_default);

        // Check HW codec flag.
        mHwCodecEnabled = sharedPrefGetBoolean(R.string.pref_hwcodec_key, CallActivity.EXTRA_HWCODEC_ENABLED,
                R.string.pref_hwcodec_default);

        // Check Capture to texture.
        mCaptureToTexture = sharedPrefGetBoolean(R.string.pref_capturetotexture_key,
                CallActivity.EXTRA_CAPTURETOTEXTURE_ENABLED, R.string.pref_capturetotexture_default);

        // Check FlexFEC.
        mFlexfecEnabled = sharedPrefGetBoolean(R.string.pref_flexfec_key, CallActivity.EXTRA_FLEXFEC_ENABLED,
                R.string.pref_flexfec_default);

        // Check Disable Audio Processing flag.
        mNoAudioProcessing = sharedPrefGetBoolean(R.string.pref_noaudioprocessing_key,
                CallActivity.EXTRA_NOAUDIOPROCESSING_ENABLED, R.string.pref_noaudioprocessing_default);

        // Check Disable Audio Processing flag.
        mAecDump = sharedPrefGetBoolean(R.string.pref_aecdump_key, CallActivity.EXTRA_AECDUMP_ENABLED,
                R.string.pref_aecdump_default);

        // Check OpenSL ES enabled flag.
        mUseOpenSLES = sharedPrefGetBoolean(R.string.pref_opensles_key, CallActivity.EXTRA_OPENSLES_ENABLED,
                R.string.pref_opensles_default);

        // Check Disable built-in AEC flag.
        mDisableBuiltInAEC = sharedPrefGetBoolean(R.string.pref_disable_built_in_aec_key,
                CallActivity.EXTRA_DISABLE_BUILT_IN_AEC, R.string.pref_disable_built_in_aec_default);

        // Check Disable built-in AGC flag.
        mDisableBuiltInAGC = sharedPrefGetBoolean(R.string.pref_disable_built_in_agc_key,
                CallActivity.EXTRA_DISABLE_BUILT_IN_AGC, R.string.pref_disable_built_in_agc_default);

        // Check Disable built-in NS flag.
        mDisableBuiltInNS = sharedPrefGetBoolean(R.string.pref_disable_built_in_ns_key,
                CallActivity.EXTRA_DISABLE_BUILT_IN_NS, R.string.pref_disable_built_in_ns_default);

        // Check Enable level control.
        mEnableLevelControl = sharedPrefGetBoolean(R.string.pref_enable_level_control_key,
                CallActivity.EXTRA_ENABLE_LEVEL_CONTROL, R.string.pref_enable_level_control_key);

        // Get video resolution from settings.
        if (mVideoWidth == 0 && mVideoHeight == 0) {
            String resolution = sharedPref.getString(keyprefResolution,
                    getString(R.string.pref_resolution_default));

            if (resolution.equals("Default")) {

                mVideoWidth = 320;
                mVideoHeight = 240;
            } else {
                String[] dimensions = resolution.split("[ x]+");
                if (dimensions.length == 2) {
                    try {
                        mVideoWidth = Integer.parseInt(dimensions[0]);
                        mVideoHeight = Integer.parseInt(dimensions[1]);
                    } catch (NumberFormatException e) {
                        mVideoWidth = 0;
                        mVideoHeight = 0;
                        Log.e(TAG, "Wrong video resolution setting: " + resolution);
                    }
                }
            }
        }

        // Get camera fps from settings.
        if (mCameraFps == 0) {
            String fps = sharedPref.getString(keyprefFps, getString(R.string.pref_fps_default));
            String[] fpsValues = fps.split("[ x]+");
            if (fpsValues.length == 2) {
                try {
                    mCameraFps = Integer.parseInt(fpsValues[0]);
                } catch (NumberFormatException e) {
                    mCameraFps = 0;
                    Log.e(TAG, "Wrong camera fps setting: " + fps);
                }
            }
        }

        // Check capture quality slider flag.
        mCaptureQualitySlider = sharedPrefGetBoolean(R.string.pref_capturequalityslider_key,
                CallActivity.EXTRA_VIDEO_CAPTUREQUALITYSLIDER_ENABLED, R.string.pref_capturequalityslider_default);

        // Get video and audio start bitrate.

        if (mVideoStartBitrate == 0) {
            String bitrateTypeDefault = getString(R.string.pref_maxvideobitrate_default);
            String bitrateType = sharedPref.getString(keyprefVideoBitrateType, bitrateTypeDefault);
            if (!bitrateType.equals(bitrateTypeDefault)) {
                String bitrateValue = sharedPref.getString(keyprefVideoBitrateValue,
                        getString(R.string.pref_maxvideobitratevalue_default));
                mVideoStartBitrate = Integer.parseInt(bitrateValue);
            }
        }

        if (mAudioStartBitrate == 0) {
            String bitrateTypeDefault = getString(R.string.pref_startaudiobitrate_default);
            String bitrateType = sharedPref.getString(keyprefAudioBitrateType, bitrateTypeDefault);
            if (!bitrateType.equals(bitrateTypeDefault)) {
                String bitrateValue = sharedPref.getString(keyprefAudioBitrateValue,
                        getString(R.string.pref_startaudiobitratevalue_default));
                mAudioStartBitrate = Integer.parseInt(bitrateValue);
            }
        }

        // Check statistics display option.
        mDisplayHud = sharedPrefGetBoolean(R.string.pref_displayhud_key, CallActivity.EXTRA_DISPLAY_HUD,
                R.string.pref_displayhud_default);

        mTracing = sharedPrefGetBoolean(R.string.pref_tracing_key, CallActivity.EXTRA_TRACING,
                R.string.pref_tracing_default);

        // Get datachannel options
        mDataChannelEnabled = sharedPrefGetBoolean(R.string.pref_enable_datachannel_key,
                CallActivity.EXTRA_DATA_CHANNEL_ENABLED, R.string.pref_enable_datachannel_default);
        mOrdered = sharedPrefGetBoolean(R.string.pref_ordered_key, CallActivity.EXTRA_ORDERED,
                R.string.pref_ordered_default);
        mNegotiated = sharedPrefGetBoolean(R.string.pref_negotiated_key, CallActivity.EXTRA_NEGOTIATED,
                R.string.pref_negotiated_default);
        mMaxRetrMs = sharedPrefGetInteger(R.string.pref_max_retransmit_time_ms_key,
                CallActivity.EXTRA_MAX_RETRANSMITS_MS, R.string.pref_max_retransmit_time_ms_default);
        mMaxRetr = sharedPrefGetInteger(R.string.pref_max_retransmits_key, CallActivity.EXTRA_MAX_RETRANSMITS,
                R.string.pref_max_retransmits_default);
        mId = sharedPrefGetInteger(R.string.pref_data_id_key, CallActivity.EXTRA_ID, R.string.pref_data_id_default);
        mProtocol = sharedPrefGetString(R.string.pref_data_protocol_key, CallActivity.EXTRA_PROTOCOL,
                R.string.pref_data_protocol_default);

    }

    @Override
    public void sendOfferSdp(SessionDescription localSdp, final String remoteId, String conferenceId,
            String token) {
        if (mService != null) {
            if (token != null && token.length() != 0) {
                mService.sendTokenOfferSdp(localSdp, token, "1", remoteId);
            } else if (conferenceId != null && conferenceId.length() != 0) {
                mService.sendConferenceOfferSdp(localSdp, remoteId, conferenceId);
            } else {
                mService.sendOfferSdp(localSdp, remoteId);
            }
        }

        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                callListFragment.updateUserState(User.CallState.CALLING, remoteId);
            }
        });
    }

    @Override
    public void sendAnswerSdp(SessionDescription localSdp, String remoteId) {
        if (mService != null) {
            mService.sendAnswerSdp(localSdp, remoteId);
        }
        callListFragment.updateUserState(User.CallState.CALLING, remoteId);
    }

    @Override
    public void sendLocalIceCandidate(SerializableIceCandidate candidate, String remoteId, String token,
            String id) {
        if (mService != null) {
            mService.sendLocalIceCandidate(candidate, token != null ? token : "", id != null ? id : "", remoteId);
        }
    }

    @Override
    public void onError(final String description, final String remoteId) {
        connectedUserIds.remove(remoteId);

        runOnUiThread(new Runnable() {

            @Override
            public void run() {
                callListFragment.updateUserState(User.CallState.NONE, remoteId);
                logAndToast(description);
                updateVideoView();
            }
        });
    }

    @Override
    public void onConnected(final String remoteId, final String token) {
        if (token == null || token.length() == 0) {
            connectedUserIds.add(remoteId);

            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    callListFragment.updateUserState(User.CallState.CONNECTED, remoteId);
                    AddRunningIntent();
                }
            });

            if (mConferenceId != null) {
                // send the conference invites
                mService.sendConference(mConferenceId, connectedUserIds);
            }
        }
    }

    @Override
    public void onConnectionClosed(final String remoteId, final String token) {
        if (token == null || token.length() == 0) {
            // normal peer call
            connectedUserIds.remove(remoteId);
        }
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                if (token == null || token.length() == 0) {
                    callListFragment.updateUserState(User.CallState.NONE, remoteId);
                }

                if (mTokenPeers.containsKey(remoteId)) {
                    mTokenPeers.get(remoteId).close();
                    mTokenPeers.remove(remoteId);
                    screenshareRenderLayout.setVisibility(View.GONE);
                    if (screenshareRender != null) {
                        screenshareRender.setVisibility(View.GONE);
                    }
                    updateVideoView();
                }
            }
        });
        runOnUiThread(new Runnable() {

            @Override
            public void run() {
                updateVideoView();
            }
        });
    }

    @Override
    public void showHoldMessage(final boolean on, final String remoteId) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                for (RemoteConnectionViews remoteView : remoteViewsInUseList) {
                    if (remoteView.getId().equals(remoteId)) {
                        remoteView.setHoldStatus(on);
                        break;
                    }
                }
                if (on) {
                    callListFragment.updateUserState(User.CallState.HOLD, remoteId);

                } else {
                    callListFragment.updateUserState(User.CallState.CONNECTED, remoteId);
                }
            }
        });
    }

    public ArrayList<User> getUsers() {
        ArrayList<User> users = null;
        if (mService != null) {
            String roomName = mService.getCurrentRoomName();
            if (roomName != null) {
                users = mService.getUsersInRoom(roomName);
            }
        }

        return users;
    }

    public String getServerAddress() {
        return mServer;
    }

    public boolean callHasId(String id) {
        if (mPeerId.equals(id) || mAdditionalPeers.containsKey(id)) {
            return true;
        }

        return false;
    }

    private void putCallOnHold(boolean on) {
        if (peerConnectionClient != null) {
            try {
                JSONObject json = new JSONObject();
                JSONObject jsonHold = new JSONObject();
                json.put("state", on);

                jsonHold.put("Hold", json);
                jsonHold.put("Type", "Hold");

                peerConnectionClient.sendDataChannelMessage(jsonHold.toString());
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
    }
}