gt:             Log.i(TAG, "Stats: " + statsJson.toString(3));
context: /*
   * TeleStax, Open Source Cloud Communications
   * Copyright 2011-2015, Telestax Inc and individual contributors
   * by the @authors tag.
   *
   * This program is free software: you can redistribute it and/or modify
   * under the terms of the GNU Affero General Public License as
   * published by the Free Software Foundation; either version 3 of
   * the License, or (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU Affero General Public License for more details.
   *
   * You should have received a copy of the GNU Affero General Public License
   * along with this program.  If not, see <http://www.gnu.org/licenses/>
   *
   * For questions related to commercial use licensing, please contact sales@telestax.com.
   *
   */
  
  package org.restcomm.android.olympus;
  
  import android.Manifest;
  import android.app.AlertDialog;
  import android.app.FragmentTransaction;
  import android.content.ComponentName;
  import android.content.Context;
  import android.content.DialogInterface;
  import android.content.Intent;
  import android.content.ServiceConnection;
  import android.content.SharedPreferences;
  import android.content.pm.PackageManager;
  import android.graphics.Color;
  import android.media.AudioManager;
  import android.os.Bundle;
  import android.os.Handler;
  import android.os.IBinder;
  import android.preference.PreferenceManager;
  import android.support.v4.app.ActivityCompat;
  import android.support.v7.app.AppCompatActivity;
  import android.util.Log;
  import android.view.Menu;
  import android.view.MenuItem;
  import android.view.View;
  import android.view.Window;
  import android.view.WindowManager;
  import android.widget.ImageButton;
  import android.widget.TextView;
  
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.HashMap;
  import java.util.ListIterator;
  import java.util.Locale;
  
  import org.restcomm.android.sdk.RCConnection;
  import org.restcomm.android.sdk.RCConnectionListener;
  import org.restcomm.android.sdk.RCDevice;
  import org.restcomm.android.sdk.util.PercentFrameLayout;
  import org.restcomm.android.sdk.util.RCException;
  
  import static org.restcomm.android.sdk.RCConnection.ConnectionMediaType.AUDIO_VIDEO;
  
  
  public class CallActivity extends AppCompatActivity implements RCConnectionListener, View.OnClickListener,
          KeypadFragment.OnFragmentInteractionListener, ServiceConnection {
      private RCConnection connection, pendingConnection;
      SharedPreferences prefs;
      private static final String TAG = "CallActivity";
      private HashMap<String, Object> connectParams;  // = new HashMap<String, Object>();
      private HashMap<String, Object> acceptParams; // = new HashMap<String, Object>();
      private RCDevice device;
      boolean serviceBound = false;
      private boolean pendingError = false;
      private boolean activityVisible = false;
      private boolean muteAudio = false;
      private boolean muteVideo = false;
      private boolean isVideo = false;
      // handler for the timer
      private Handler timerHandler = new Handler();
      int secondsElapsed = 0;
      private final int PERMISSION_REQUEST_DANGEROUS = 1;
      private AlertDialog alertDialog;
      private long timeConnected = 0;
      public static final String LIVE_CALL_PAUSE_TIME = "live-call-pause-time";
      private boolean callOutgoing = true;
  
      ImageButton btnMuteAudio, btnMuteVideo;
      ImageButton btnHangup;
      ImageButton btnAnswer, btnAnswerAudio;
      ImageButton btnKeypad;
      KeypadFragment keypadFragment;
      TextView lblCall, lblStatus, lblTimer;
  
      //public static String ACTION_OUTGOING_CALL = "org.restcomm.android.olympus.ACTION_OUTGOING_CALL";
  
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          // Set window styles for fullscreen-window size. Needs to be done before
          // adding content.
          requestWindowFeature(Window.FEATURE_NO_TITLE);
          getWindow().addFlags(
                  WindowManager.LayoutParams.FLAG_FULLSCREEN
                          | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
                          | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
                          | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
                          | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
          getWindow().getDecorView().setSystemUiVisibility(
                  View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                          | View.SYSTEM_UI_FLAG_FULLSCREEN
                          | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
          setContentView(R.layout.activity_call);
  
          // Initialize UI
          btnHangup = (ImageButton) findViewById(R.id.button_hangup);
          btnHangup.setOnClickListener(this);
          btnAnswer = (ImageButton) findViewById(R.id.button_answer);
          btnAnswer.setOnClickListener(this);
          btnAnswerAudio = (ImageButton) findViewById(R.id.button_answer_audio);
          btnAnswerAudio.setOnClickListener(this);
          btnMuteAudio = (ImageButton) findViewById(R.id.button_mute_audio);
          btnMuteAudio.setOnClickListener(this);
          btnMuteVideo = (ImageButton) findViewById(R.id.button_mute_video);
          btnMuteVideo.setOnClickListener(this);
          btnKeypad = (ImageButton) findViewById(R.id.button_keypad);
          btnKeypad.setOnClickListener(this);
          lblCall = (TextView) findViewById(R.id.label_call);
          lblStatus = (TextView) findViewById(R.id.label_status);
          lblTimer = (TextView) findViewById(R.id.label_timer);
  
          alertDialog = new AlertDialog.Builder(CallActivity.this, R.style.SimpleAlertStyle).create();
  
          PreferenceManager.setDefaultValues(this, "preferences.xml", MODE_PRIVATE, R.xml.preferences, false);
          prefs = PreferenceManager.getDefaultSharedPreferences(this);
  
          // Get Intent parameters.
          final Intent intent = getIntent();
          if (intent.getAction().equals(RCDevice.ACTION_OUTGOING_CALL)) {
              btnAnswer.setVisibility(View.INVISIBLE);
              btnAnswerAudio.setVisibility(View.INVISIBLE);
          } else {
              btnAnswer.setVisibility(View.VISIBLE);
              btnAnswerAudio.setVisibility(View.VISIBLE);
          }
  
          keypadFragment = new KeypadFragment();
  
          lblTimer.setVisibility(View.INVISIBLE);
          // these might need to be moved to Resume()
          btnMuteAudio.setVisibility(View.INVISIBLE);
          btnMuteVideo.setVisibility(View.INVISIBLE);
          btnKeypad.setVisibility(View.INVISIBLE);
  
          activityVisible = true;
  
          // open keypad
          FragmentTransaction ft = getFragmentManager().beginTransaction();
          ft.add(R.id.keypad_fragment_container, keypadFragment);
          ft.hide(keypadFragment);
          ft.commit();
      }
  
      @Override
      protected void onPause() {
          super.onPause();
          Log.i(TAG, "%% onPause");
          activityVisible = false;
  
          if (connection != null && connection.getState() == RCConnection.ConnectionState.CONNECTED) {
              connection.detachVideo();
          }
  
      }
  
      @Override
      protected void onStart()
      {
         super.onStart();
         Log.i(TAG, "%% onStart");
  
         // User requested to disconnect via foreground service notification. At this point the service has already
         // disconnected the call, so let's close the call activity
         if (getIntent().getAction().equals(RCDevice.ACTION_CALL_DISCONNECT)) {
             Intent intent = new Intent(this, MainActivity.class);
             intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
             intent.setAction(MainActivity.ACTION_DISCONNECTED_BACKGROUND);
             startActivity(intent);
         }
         else {
  
             activityVisible = true;
  
             bindService(new Intent(this, RCDevice.class), this, Context.BIND_AUTO_CREATE);
         }
      }
  
      @Override
      protected void onStop() {
          super.onStop();
          Log.i(TAG, "%% onStop");
  
          // Unbind from the service
          if (serviceBound) {
              //device.detach();
              unbindService(this);
              serviceBound = false;
          }
      }
  
      @Override
      protected void onResume() {
          super.onResume();
  
          // The activity has become visible (it is now "resumed").
          Log.i(TAG, "%% onResume");
          if (connection != null) {
              if (connection.getState() == RCConnection.ConnectionState.CONNECTED) {
                  // update the intent action if there's an ongoing call that we need to resume so that we provide a hint
                  // to handleCall() to do proper handling
                  getIntent().setAction(RCDevice.ACTION_RESUME_CALL);
  
                  // Now that we can mute/umnute via notification, we need to update the UI accordingly if there was a change
                  // while we were not in the foreground
                  muteAudio = connection.isAudioMuted();
                  if (!muteAudio) {
                      btnMuteAudio.setImageResource(R.drawable.audio_unmuted);
                  } else {
                      btnMuteAudio.setImageResource(R.drawable.audio_muted);
                  }
              }
              else if (connection.getState() == RCConnection.ConnectionState.DISCONNECTED) {
                  // When a user returns to CallActivity, after the call was hung up by the peer, while App
                  // was in the background, we need to just close the Activity
                  finish();
              }
          }
      }
  
      @Override
      protected void onDestroy()
      {
          super.onDestroy();
          // The activity is about to be destroyed.
          Log.i(TAG, "%% onDestroy");
  
          if (timerHandler != null) {
              timerHandler.removeCallbacksAndMessages(null);
          }
  
          if (pendingConnection != null) {
              // incoming ringing
              pendingConnection.reject();
              pendingConnection = null;
          } else {
              if (connection != null) {
                  // incoming established or outgoing any state (pending, connecting, connected)
                  if (connection.getState() == RCConnection.ConnectionState.CONNECTED) {
                      // If user leaves activity while on call we need to stop local video
                      ///connection.disconnect();
  
                      // TODO: Issue #380: once we figure out the issue with the backgrounding we need to uncomment this
                      //connection.detachVideo();
                  }
                  else {
                      connection = null;
                      pendingConnection = null;
                  }
              }
          }
      }
  
      @Override
      public void onNewIntent(Intent intent)
      {
          Log.i(TAG, "%% onNewIntent");
          super.onNewIntent(intent);
          setIntent(intent);
      }
  
  
      // Callbacks for service binding, passed to bindService()
      @Override
      public void onServiceConnected(ComponentName className, IBinder service)
      {
          Log.i(TAG, "%% onServiceConnected");
          // We've bound to LocalService, cast the IBinder and get LocalService instance
          RCDevice.RCDeviceBinder binder = (RCDevice.RCDeviceBinder) service;
          device = binder.getService();
  
          // We have the device reference, let's handle the call
          handleCall(getIntent());
  
          serviceBound = true;
      }
  
      @Override
      public void onServiceDisconnected(ComponentName arg0)
      {
          Log.i(TAG, "%% onServiceDisconnected");
          serviceBound = false;
      }
  
      private void handleCall(Intent intent)
      {
          if (intent.getAction().equals(RCDevice.ACTION_RESUME_CALL)) {
              String text;
              connection = device.getLiveConnection();
              if (connection != null) {
                  connection.setConnectionListener(this);
  
                  if (connection.isIncoming()) {
                      // Incoming
                      if (connection.getRemoteMediaType() == AUDIO_VIDEO) {
                          text = "Video Call from ";
                      } else {
                          text = "Audio Call from ";
                      }
                  } else {
                      // Outgoing
                      if (connection.getLocalMediaType() == AUDIO_VIDEO) {
                          text = "Video Calling ";
                      } else {
                          text = "Audio Calling ";
                      }
                  }
  
                  lblCall.setText(text + connection.getPeer().replaceAll(".*?sip:", "").replaceAll("@.*$", ""));
                  lblStatus.setText("Connected");
                  connection.reattachVideo((PercentFrameLayout) findViewById(R.id.local_video_layout),
                          (PercentFrameLayout) findViewById(R.id.remote_video_layout));
  
                  // Get the time when we paused call so that now that we are resuming we can show correct time
                  long difference = (System.currentTimeMillis() - prefs.getLong(LIVE_CALL_PAUSE_TIME, 0));
                  int duration = (int)(difference/1000);
                  startTimer(duration);
  
                  // resume UI state
                  muteAudio = connection.isAudioMuted();
                  muteVideo = connection.isVideoMuted();
                  if (connection.isAudioMuted()){
                      btnMuteAudio.setImageResource(R.drawable.audio_muted);
                  }
                  if (connection.getLocalMediaType() != AUDIO_VIDEO) {
                      btnMuteVideo.setEnabled(false);
                      btnMuteVideo.setColorFilter(Color.parseColor(getString(R.string.string_color_filter_video_disabled)));
                  }
                  else {
                      if (connection.isVideoMuted()){
                          btnMuteVideo.setImageResource(R.drawable.video_muted);
                      }
                  }
  
                  // Hide answering buttons and show mute & keypad
                  btnAnswer.setVisibility(View.INVISIBLE);
                  btnAnswerAudio.setVisibility(View.INVISIBLE);
                  btnMuteAudio.setVisibility(View.VISIBLE);
                  btnMuteVideo.setVisibility(View.VISIBLE);
                  btnKeypad.setVisibility(View.VISIBLE);
                  lblTimer.setVisibility(View.VISIBLE);
              }
              else {
                  showOkAlert("Resume ongoing call", "No call to resume");
              }
  
              return;
          }
  
          if (connection != null) {
              return;
          }
  
          isVideo = intent.getBooleanExtra(RCDevice.EXTRA_VIDEO_ENABLED, false);
          if (intent.getAction().equals(RCDevice.ACTION_OUTGOING_CALL)) {
              String text;
              if (isVideo) {
                  text = "Video Calling ";
              }
              else {
                  text = "Audio Calling ";
              }
  
              lblCall.setText(text + intent.getStringExtra(RCDevice.EXTRA_DID).replaceAll(".*?sip:", "").replaceAll("@.*$", ""));
              lblStatus.setText("Initiating Call...");
  
              connectParams = new HashMap<String, Object>();
              connectParams.put(RCConnection.ParameterKeys.CONNECTION_PEER, intent.getStringExtra(RCDevice.EXTRA_DID));
              connectParams.put(RCConnection.ParameterKeys.CONNECTION_VIDEO_ENABLED, intent.getBooleanExtra(RCDevice.EXTRA_VIDEO_ENABLED, false));
              connectParams.put(RCConnection.ParameterKeys.CONNECTION_LOCAL_VIDEO, (PercentFrameLayout)findViewById(R.id.local_video_layout));
              connectParams.put(RCConnection.ParameterKeys.CONNECTION_REMOTE_VIDEO, (PercentFrameLayout)findViewById(R.id.remote_video_layout));
              // by default we use VP8 for video as it tends to be more adopted, but you can override that and specify VP9 or H264 as follows:
              connectParams.put(RCConnection.ParameterKeys.CONNECTION_PREFERRED_AUDIO_CODEC,
                    audioCodecString2Enum(prefs.getString(RCConnection.ParameterKeys.CONNECTION_PREFERRED_AUDIO_CODEC, ""))
              );
              connectParams.put(RCConnection.ParameterKeys.CONNECTION_PREFERRED_VIDEO_CODEC,
                    videoCodecString2Enum(prefs.getString(RCConnection.ParameterKeys.CONNECTION_PREFERRED_VIDEO_CODEC, ""))
              );
              connectParams.put(RCConnection.ParameterKeys.CONNECTION_PREFERRED_VIDEO_RESOLUTION,
                    resolutionString2Enum(prefs.getString(RCConnection.ParameterKeys.CONNECTION_PREFERRED_VIDEO_RESOLUTION, ""))
              );
              connectParams.put(RCConnection.ParameterKeys.CONNECTION_PREFERRED_VIDEO_FRAME_RATE,
                    frameRateString2Enum(prefs.getString(RCConnection.ParameterKeys.CONNECTION_PREFERRED_VIDEO_FRAME_RATE, ""))
              );
              // Needed until we implement Trickle ICE
              connectParams.put(RCConnection.ParameterKeys.DEBUG_CONNECTION_CANDIDATE_TIMEOUT,
                      Integer.parseInt(prefs.getString(RCConnection.ParameterKeys.DEBUG_CONNECTION_CANDIDATE_TIMEOUT, "0"))
              );
  
              // Here's how to set manually
              //connectParams.put(RCConnection.ParameterKeys.CONNECTION_PREFERRED_VIDEO_CODEC, RCConnection.VideoCodec.VIDEO_CODEC_VP8);
              //connectParams.put(RCConnection.ParameterKeys.CONNECTION_PREFERRED_VIDEO_RESOLUTION, RCConnection.VideoResolution.RESOLUTION_HD_1280x720);
              //connectParams.put(RCConnection.ParameterKeys.CONNECTION_PREFERRED_VIDEO_FRAME_RATE, RCConnection.VideoFrameRate.FPS_DEFAULT);
  
              // *** if you want to add custom SIP headers, please uncomment this
              //HashMap<String, String> sipHeaders = new HashMap<>();
              //sipHeaders.put("X-SIP-Header1", "Value1");
              //connectParams.put(RCConnection.ParameterKeys.CONNECTION_CUSTOM_SIP_HEADERS, sipHeaders);
  
              // save peer to preferences, we might need it for potential bug report (check BugReportActivity)
              SharedPreferences.Editor editor = prefs.edit();
              editor.putString(BugReportActivity.MOST_RECENT_CALL_PEER, intent.getStringExtra(RCDevice.EXTRA_DID));
              editor.apply();
  
              handlePermissions(isVideo);
          }
          if (intent.getAction().equals(RCDevice.ACTION_INCOMING_CALL) ||
                intent.getAction().equals(RCDevice.ACTION_INCOMING_CALL_ANSWER_AUDIO) || intent.getAction().equals(RCDevice.ACTION_INCOMING_CALL_ANSWER_VIDEO)) {
              String text;
              if (isVideo) {
                  text = "Video Call from ";
              }
              else {
                  text = "Audio Call from ";
              }
  
              lblCall.setText(text + intent.getStringExtra(RCDevice.EXTRA_DID).replaceAll(".*?sip:", "").replaceAll("@.*$", ""));
              lblStatus.setText("Call Received...");
  
              //callOutgoing = false;
              pendingConnection = device.getPendingConnection();
              // There is chance that pendingConnection is null if call activity is reopened after call has failed
              // but used hasn't pressed ok to the dialog so that the call activity is destroyed. Let's guard for this
              if (pendingConnection != null) {
                  pendingConnection.setConnectionListener(this);
  
                  // the number from which we got the call
                  String incomingCallDid = intent.getStringExtra(RCDevice.EXTRA_DID);
                  HashMap<String, String> customHeaders = (HashMap<String, String>) intent.getSerializableExtra(RCDevice.EXTRA_CUSTOM_HEADERS);
                  if (customHeaders != null) {
                      Log.i(TAG, "Got custom headers in incoming call: " + customHeaders.toString());
                  }
  
                  // save peer to preferences, we might need it for potential bug report (check BugReportActivity)
                  SharedPreferences.Editor editor = prefs.edit();
                  editor.putString(BugReportActivity.MOST_RECENT_CALL_PEER, incomingCallDid);
                  editor.apply();
  
  
                  if (intent.getAction().equals(RCDevice.ACTION_INCOMING_CALL_ANSWER_AUDIO) || intent.getAction().equals(RCDevice.ACTION_INCOMING_CALL_ANSWER_VIDEO)) {
                      // The Intent has been sent from the Notification subsystem. It can be either of type 'decline', 'video answer and 'audio answer'
                      boolean answerVideo = intent.getAction().equals(RCDevice.ACTION_INCOMING_CALL_ANSWER_VIDEO);
                      btnAnswer.setVisibility(View.INVISIBLE);
                      btnAnswerAudio.setVisibility(View.INVISIBLE);
  
                      acceptParams = new HashMap<String, Object>();
                      acceptParams.put(RCConnection.ParameterKeys.CONNECTION_VIDEO_ENABLED, answerVideo);
                      acceptParams.put(RCConnection.ParameterKeys.CONNECTION_LOCAL_VIDEO, (PercentFrameLayout)findViewById(R.id.local_video_layout));
                      acceptParams.put(RCConnection.ParameterKeys.CONNECTION_REMOTE_VIDEO, (PercentFrameLayout)findViewById(R.id.remote_video_layout));
                      acceptParams.put(RCConnection.ParameterKeys.CONNECTION_PREFERRED_AUDIO_CODEC,
                              audioCodecString2Enum(prefs.getString(RCConnection.ParameterKeys.CONNECTION_PREFERRED_AUDIO_CODEC, ""))
                      );
                      // Needed until we implement Trickle ICE
                      acceptParams.put(RCConnection.ParameterKeys.DEBUG_CONNECTION_CANDIDATE_TIMEOUT,
                              Integer.parseInt(prefs.getString(RCConnection.ParameterKeys.DEBUG_CONNECTION_CANDIDATE_TIMEOUT, "0"))
                      );
  
  
                      if (intent.getAction().equals(RCDevice.ACTION_INCOMING_CALL_ANSWER_VIDEO)) {
                          acceptParams.put(RCConnection.ParameterKeys.CONNECTION_PREFERRED_VIDEO_CODEC,
                                  videoCodecString2Enum(prefs.getString(RCConnection.ParameterKeys.CONNECTION_PREFERRED_VIDEO_CODEC, ""))
                          );
                          acceptParams.put(RCConnection.ParameterKeys.CONNECTION_PREFERRED_VIDEO_RESOLUTION,
                                  resolutionString2Enum(prefs.getString(RCConnection.ParameterKeys.CONNECTION_PREFERRED_VIDEO_RESOLUTION, ""))
                          );
                          acceptParams.put(RCConnection.ParameterKeys.CONNECTION_PREFERRED_VIDEO_FRAME_RATE,
                                  frameRateString2Enum(prefs.getString(RCConnection.ParameterKeys.CONNECTION_PREFERRED_VIDEO_FRAME_RATE, ""))
                          );
                      }
  
  
                      // Check permissions asynchronously and then accept the call
                      handlePermissions(answerVideo);
                  }
              }
              else {
                  Log.w(TAG, "Warning: pendingConnection is null, probably reusing past intent");
              }
          }
      }
  
      // UI Events
      public void onClick(View view) {
          if (view.getId() == R.id.button_hangup) {
              if (pendingConnection != null) {
                  // incoming ringing
                  lblStatus.setText("Rejecting Call...");
                  pendingConnection.reject();
                  pendingConnection = null;
              } else {
                  if (connection != null) {
                      // incoming established or outgoing any state (pending, connecting, connected)
                      lblStatus.setText("Disconnecting Call...");
                      connection.disconnect();
                      connection = null;
                      pendingConnection = null;
                  } else {
                      Log.e(TAG, "Error: not connected/connecting/pending");
                  }
              }
              finish();
          } else if (view.getId() == R.id.button_answer) {
              if (pendingConnection != null) {
                  lblStatus.setText("Answering Call...");
                  btnAnswer.setVisibility(View.INVISIBLE);
                  btnAnswerAudio.setVisibility(View.INVISIBLE);
  
                  acceptParams = new HashMap<String, Object>();
                  acceptParams.put(RCConnection.ParameterKeys.CONNECTION_VIDEO_ENABLED, true);
                  acceptParams.put(RCConnection.ParameterKeys.CONNECTION_LOCAL_VIDEO, (PercentFrameLayout)findViewById(R.id.local_video_layout));
                  acceptParams.put(RCConnection.ParameterKeys.CONNECTION_REMOTE_VIDEO, (PercentFrameLayout)findViewById(R.id.remote_video_layout));
                  acceptParams.put(RCConnection.ParameterKeys.CONNECTION_PREFERRED_AUDIO_CODEC,
                        audioCodecString2Enum(prefs.getString(RCConnection.ParameterKeys.CONNECTION_PREFERRED_AUDIO_CODEC, ""))
                  );
                  acceptParams.put(RCConnection.ParameterKeys.CONNECTION_PREFERRED_VIDEO_CODEC,
                        videoCodecString2Enum(prefs.getString(RCConnection.ParameterKeys.CONNECTION_PREFERRED_VIDEO_CODEC, ""))
                  );
                  acceptParams.put(RCConnection.ParameterKeys.CONNECTION_PREFERRED_VIDEO_RESOLUTION,
                        resolutionString2Enum(prefs.getString(RCConnection.ParameterKeys.CONNECTION_PREFERRED_VIDEO_RESOLUTION, ""))
                  );
                  acceptParams.put(RCConnection.ParameterKeys.CONNECTION_PREFERRED_VIDEO_FRAME_RATE,
                        frameRateString2Enum(prefs.getString(RCConnection.ParameterKeys.CONNECTION_PREFERRED_VIDEO_FRAME_RATE, ""))
                  );
                  acceptParams.put(RCConnection.ParameterKeys.DEBUG_CONNECTION_CANDIDATE_TIMEOUT,
                          Integer.parseInt(prefs.getString(RCConnection.ParameterKeys.DEBUG_CONNECTION_CANDIDATE_TIMEOUT, "0"))
                  );
  
  
                  // Check permissions asynchronously and then accept the call
                  handlePermissions(true);
              }
          } else if (view.getId() == R.id.button_answer_audio) {
              if (pendingConnection != null) {
                  lblStatus.setText("Answering Call...");
                  btnAnswer.setVisibility(View.INVISIBLE);
                  btnAnswerAudio.setVisibility(View.INVISIBLE);
  
                  acceptParams = new HashMap<String, Object>();
                  acceptParams.put(RCConnection.ParameterKeys.CONNECTION_VIDEO_ENABLED, false);
                  acceptParams.put(RCConnection.ParameterKeys.CONNECTION_LOCAL_VIDEO, (PercentFrameLayout)findViewById(R.id.local_video_layout));
                  acceptParams.put(RCConnection.ParameterKeys.CONNECTION_REMOTE_VIDEO, (PercentFrameLayout)findViewById(R.id.remote_video_layout));
                  acceptParams.put(RCConnection.ParameterKeys.CONNECTION_PREFERRED_AUDIO_CODEC,
                        audioCodecString2Enum(prefs.getString(RCConnection.ParameterKeys.CONNECTION_PREFERRED_AUDIO_CODEC, ""))
                  );
  
                  acceptParams.put(RCConnection.ParameterKeys.DEBUG_CONNECTION_CANDIDATE_TIMEOUT,
                          Integer.parseInt(prefs.getString(RCConnection.ParameterKeys.DEBUG_CONNECTION_CANDIDATE_TIMEOUT, "0"))
                  );
  
                  // Check permissions asynchronously and then accept the call
                  handlePermissions(false);
              }
          } else if (view.getId() == R.id.button_keypad) {
              keypadFragment.setConnection(connection);
  
              View rootView = getWindow().getDecorView().findViewById(android.R.id.content);
  
              // show keypad
              FragmentTransaction ft = getFragmentManager().beginTransaction();
              ft.show(keypadFragment);
              ft.commit();
          } else if (view.getId() == R.id.button_mute_audio) {
              if (connection != null) {
                  if (!muteAudio) {
                      btnMuteAudio.setImageResource(R.drawable.audio_muted);
                  } else {
                      btnMuteAudio.setImageResource(R.drawable.audio_unmuted);
                  }
                  muteAudio = !muteAudio;
                  connection.setAudioMuted(muteAudio);
              }
          } else if (view.getId() == R.id.button_mute_video) {
              if (connection != null) {
                  muteVideo = !muteVideo;
                  if (muteVideo) {
                      btnMuteVideo.setImageResource(R.drawable.video_muted);
                      //connection.detachVideo();
                  } else {
                      btnMuteVideo.setImageResource(R.drawable.video_unmuted);
                      //connection.reattachVideo((PercentFrameLayout)findViewById(R.id.local_video_layout), (PercentFrameLayout)findViewById(R.id.remote_video_layout));
                  }
  
                  connection.setVideoMuted(muteVideo);
              }
          }
  
      }
  
      @Override
      public boolean onCreateOptionsMenu(Menu menu) {
          // Inflate the menu; this adds items to the action bar if it is present.
          getMenuInflater().inflate(R.menu.menu_call, menu);
          return true;
      }
  
      @Override
      public boolean onOptionsItemSelected(MenuItem item) {
          // Handle action bar item clicks here. The action bar will
          // automatically handle clicks on the Home/Up button, so long
          // as you specify a parent activity in AndroidManifest.xml.
          int id = item.getItemId();
  
          if (id == R.id.action_settings) {
              return true;
          }
  
          return super.onOptionsItemSelected(item);
      }
  
      // RCConnection Listeners
      public void onConnecting(RCConnection connection) {
          Log.i(TAG, "RCConnection connecting");
          lblStatus.setText("Started Connecting...");
      }
  
      public void onConnected(RCConnection connection, HashMap<String, String> customHeaders) {
          Log.i(TAG, "RCConnection connected, customHeaders: " + customHeaders);
          lblStatus.setText("Connected");
  
          btnMuteAudio.setVisibility(View.VISIBLE);
          if (!isVideo) {
              btnMuteVideo.setEnabled(false);
              btnMuteVideo.setColorFilter(Color.parseColor(getString(R.string.string_color_filter_video_disabled)));
          }
          btnMuteVideo.setVisibility(View.VISIBLE);
          btnKeypad.setVisibility(View.VISIBLE);
  
          lblTimer.setVisibility(View.VISIBLE);
  
          // Save time we got connected in case the call is paused and we need to resume it
          SharedPreferences.Editor editor = prefs.edit();
          editor.putLong(LIVE_CALL_PAUSE_TIME, System.currentTimeMillis());
          editor.apply();
  
          startTimer(0);
  
          // reset to no mute at beggining of new call
          muteAudio = false;
          muteVideo = false;
  
          setVolumeControlStream(AudioManager.STREAM_VOICE_CALL);
      }
  
      public void onDisconnected(RCConnection connection) {
          Log.i(TAG, "RCConnection disconnected");
          lblStatus.setText("Disconnected");
  
          // When onDisconnect() is called, WebRTC PeerConnection stats are also gathered and
          // can be retrieved using connection.getStats()
          /*
          try {
              JSONObject statsJson = new JSONObject(connection.getStats());
  