package com.tutpro.baresip.plus

import android.Manifest.permission.*
import android.annotation.SuppressLint
import android.app.Activity
import android.app.AlertDialog
import android.app.KeyguardManager
import android.app.NotificationManager
import android.app.Presentation
import android.content.*
import android.content.Intent.ACTION_CALL
import android.content.Intent.ACTION_DIAL
import android.content.Intent.ACTION_VIEW
import android.content.pm.PackageManager
import android.content.res.Configuration
import android.graphics.Rect
import android.hardware.display.DisplayManager
import android.media.AudioManager
import android.media.MediaActionSound
import android.net.Uri
import android.os.*
import android.os.StrictMode.VmPolicy
import android.provider.DocumentsContract
import android.provider.MediaStore
import android.provider.MediaStore.Audio.Radio
import android.text.InputType
import android.text.TextWatcher
import android.util.TypedValue
import android.view.*
import android.view.inputmethod.InputMethodManager
import android.widget.*
import androidx.activity.OnBackPressedCallback
import androidx.activity.enableEdgeToEdge
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.appcompat.widget.AppCompatButton
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.graphics.Insets
import androidx.core.net.toUri
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.isVisible
import androidx.lifecycle.Observer
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar
import com.tutpro.baresip.plus.Utils.showSnackBar
import com.tutpro.baresip.plus.databinding.ActivityMainBinding
import java.io.File
import java.text.SimpleDateFormat
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.util.*
import kotlin.collections.HashMap
import kotlin.system.exitProcess


class SecondScreenPresentation(context: Context, display: Display) : Presentation(context, display) {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        //setContentView(textView)
    }

    public fun setDisplayView(view : View) {
        setContentView(view)
    }
}

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding
    private lateinit var defaultLayout: RelativeLayout
    private lateinit var videoLayout: RelativeLayout
    //private lateinit var videoView: VideoView
    private lateinit var callTitle: TextView
    private lateinit var callTimer: Chronometer
    private lateinit var callUri: AutoCompleteTextView
    private lateinit var securityButton: ImageButton
    private lateinit var videoSecurityButton: ImageButton
    private lateinit var diverter: LinearLayout
    private lateinit var diverterUri: TextView
    private lateinit var callButton: ImageButton
    private lateinit var callVideoButton: ImageButton
    private lateinit var hangupButton: ImageButton
    private lateinit var answerButton: ImageButton
    private lateinit var answerVideoButton: ImageButton
    private lateinit var rejectButton: ImageButton
    private lateinit var callControl: RelativeLayout
    private lateinit var holdButton: ImageButton
    private lateinit var transferButton: ImageButton
    private lateinit var videoButton: ImageButton
    private lateinit var voicemailButton: ImageButton
    private lateinit var voicemailButtonSpace: Space
    private lateinit var contactsButton: ImageButton
    private lateinit var messagesButton: ImageButton
    private lateinit var callsButton: ImageButton
    private lateinit var dialpadButton: ImageButton
    private lateinit var dtmf: EditText
    private var dtmfWatcher: TextWatcher? = null
    private lateinit var infoButton: ImageButton
    private lateinit var onHoldNotice: TextView
    private lateinit var videoOnHoldNotice: TextView
    private lateinit var uaAdapter: UaSpinnerAdapter
    private lateinit var aorSpinner: Spinner
    private lateinit var imm: InputMethodManager
    private lateinit var nm: NotificationManager
    private lateinit var am: AudioManager
    private lateinit var kgm: KeyguardManager
    private lateinit var screenEventReceiver: BroadcastReceiver
    private lateinit var serviceEventObserver: Observer<Event<Long>>
    private var recIcon: MenuItem? = null
    private var micIcon: MenuItem? = null
    private var speakerIcon: MenuItem? = null
    private lateinit var speakerButton: ImageButton
    private lateinit var swipeRefresh: SwipeRefreshLayout
    private lateinit var requestPermissionLauncher: ActivityResultLauncher<String>
    private lateinit var requestPermissionsLauncher: ActivityResultLauncher<Array<String>>
    private lateinit var accountsRequest: ActivityResultLauncher<Intent>
    private lateinit var chatRequests: ActivityResultLauncher<Intent>
    private lateinit var configRequest: ActivityResultLauncher<Intent>
    private lateinit var backupRequest: ActivityResultLauncher<Intent>
    private lateinit var restoreRequest: ActivityResultLauncher<Intent>
    private lateinit var logcatRequest: ActivityResultLauncher<Intent>
    private lateinit var contactsRequest: ActivityResultLauncher<Intent>
    private lateinit var callsRequest: ActivityResultLauncher<Intent>
    private lateinit var comDevChangedListener: AudioManager.OnCommunicationDeviceChangedListener
    private lateinit var permissions: Array<String>

    private lateinit var dialogImgBtn1: AppCompatButton
    private lateinit var dialogImgBtn2: AppCompatButton

    private lateinit var callStartButton: AppCompatButton
    private lateinit var callHistoryButton: AppCompatButton
    private lateinit var settingButton: AppCompatButton

    private var callHandler: Handler = Handler(Looper.getMainLooper())
    private var callRunnable: Runnable? = null
    private var downloadsInputUri: Uri? = null
    private var downloadsOutputUri: Uri? = null
    private var audioModeChangedListener: AudioManager.OnModeChangedListener? = null

    private var presentation: Presentation? = null

    private lateinit var baresipService: Intent

    private var restart = false
    private var atStartup = false
    private var alerting = false

    private var resumeUri = ""
    private var resumeUap = 0L
    private var resumeCall: Call? = null
    private var resumeAction = ""

    private val onBackPressedCallback = object : OnBackPressedCallback(true) {
        override fun handleOnBackPressed() {
            moveTaskToBack(true)
        }
    }

    private fun setDpadNavigation(
        view: View,
        upView: View? = null,
        downView: View? = null
    ) {
        view.setOnKeyListener { _, keyCode, event ->
            if (event.action == KeyEvent.ACTION_DOWN) {
                when (keyCode) {
                    KeyEvent.KEYCODE_DPAD_UP -> {
                        upView?.requestFocus()
                        true
                    }
                    KeyEvent.KEYCODE_DPAD_DOWN -> {
                        downView?.requestFocus()
                        true
                    }
                    else -> false
                }
            } else {
                false
            }
        }
    }

    lateinit var navUpList : HashMap<View, View>
    lateinit var navDownList : HashMap<View, View>

    override fun dispatchKeyEvent(event: KeyEvent): Boolean {
        if(!isKeyboardVisible) {
            val currentFocusView = currentFocus
            if(event.action == KeyEvent.ACTION_UP) {
                println("pokaRITO pokachip z. " + event + "   focusView : " + currentFocusView)
                if(event.keyCode == KeyEvent.KEYCODE_DPAD_UP) {
                    if(navUpList.contains(currentFocusView)) {
                        val view = navUpList.get(currentFocusView)!!
                        if(view.isVisible) {
                            view.requestFocus()
                            return false
                        } else {
                            if(currentFocusView == binding.btnApply) {
                                binding.radioDhcp.requestFocus()
                                return false
                            }
                        }
                    } else {
                        if(currentFocusView == binding.aorSpinner) {
                            callStartButton.requestFocus()
                            return false
                        }
                    }
                }
                else if(event.keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
                    if(navDownList.contains(currentFocusView)) {
                        val view = navDownList.get(currentFocusView)!!
                        if(view.isVisible) {
                            view.requestFocus()
                            return false
                        }
                    }

                } else if(event.keyCode == KeyEvent.KEYCODE_ENTER) {
                    if(currentFocusView == binding.editIp ||
                        currentFocusView == binding.editGateway ||
                        currentFocusView == binding.editNetmask ||
                        currentFocusView == binding.editDns) {
                        if(imm.isActive) {
                            imm.showSoftInput(currentFocusView, InputMethodManager.SHOW_IMPLICIT)
                            imm.restartInput(currentFocusView)
                            return true
                        }
                    }
                }
            } else if(event.action == KeyEvent.ACTION_DOWN) {
                if(event.keyCode == KeyEvent.KEYCODE_DPAD_DOWN || event.keyCode == KeyEvent.KEYCODE_DPAD_UP) {
                    var found = false
                    if(navUpList.contains(currentFocusView) || navDownList.contains(currentFocusView)) {
                        found = true
                    }

                    if(found) {
                        return true
                    }
                } else if(event.keyCode == KeyEvent.KEYCODE_ENTER) {
                    if(currentFocusView == binding.editIp ||
                        currentFocusView == binding.editGateway ||
                        currentFocusView == binding.editNetmask ||
                        currentFocusView == binding.editDns) {
                        return true
                    }
                }
            }
        } else {
//            if(event.keyCode == KeyEvent.KEYCODE_BACK) {
//                val currentFocusView = currentFocus
//                if(currentFocusView != null) {
//                    imm.hideSoftInputFromWindow(currentFocusView.windowToken, 0)
//                }
//            }
        }


//        if (event.action == KeyEvent.ACTION_UP && event.keyCode == KeyEvent.KEYCODE_BACK) {
//            val currentFocusView = currentFocus
//            println("pokaRITO pokachip 0")
//            if (currentFocusView is EditText) {
//                println("pokaRITO pokachip 1")
//                // 키보드가 내려간 후 300ms 뒤에 포커스를 재설정
//                Handler(Looper.getMainLooper()).postDelayed({
//                    currentFocusView.requestFocus()
//                }, 300)
//            }
//        }
        return super.dispatchKeyEvent(event)
    }
    private var isKeyboardVisible = false

    private fun setupKeyboardVisibilityListener() {
        val rootView = findViewById<View>(android.R.id.content)
        rootView.viewTreeObserver.addOnGlobalLayoutListener {
            val rect = Rect()
            rootView.getWindowVisibleDisplayFrame(rect)
            val screenHeight = rootView.rootView.height

            val keypadHeight = screenHeight - rect.bottom

            val visible = keypadHeight > screenHeight * 0.15  // 키보드 올라옴 판단 기준

            if (isKeyboardVisible && !visible) {
                // 🔻 키보드가 막 내려간 순간!
                onKeyboardHidden()
            }

            isKeyboardVisible = visible
        }
    }

    private fun onKeyboardHidden() {
        println( "키보드가 내려갔습니다!")

//        val currentFocusView = currentFocus
//        if(currentFocusView != null) {
//            println( "hideSoftInputFromWindow : " + currentFocusView)
//            imm.hideSoftInputFromWindow(currentFocusView.windowToken, 0)
//            imm.restartInput(currentFocusView)
//            currentFocusView.clearFocus()
//            currentFocusView.invalidate()
//            currentFocusView.requestFocus()
//        }
//        Handler(Looper.getMainLooper()).postDelayed({
//            findViewById<EditText>(R.id.editIp).requestFocus()
//            findViewById<EditText>(R.id.editDns).requestFocus()
//        }, 100)  // 지연은 상황 따라 조정
    }

    private fun updateFieldsVisibility() {
        val isStatic = binding.radioStatic.isChecked
        val visibility = if (isStatic) View.VISIBLE else View.GONE
        binding.editIp.visibility = visibility
        binding.editNetmask.visibility = visibility
        binding.editGateway.visibility = visibility
        binding.editDns.visibility = visibility
    }

    fun showCustomToast(context: Context, message: String) {
        val inflater = LayoutInflater.from(context)
        val layout: View = inflater.inflate(R.layout.custom_toast, null)

        val textView: TextView = layout.findViewById(R.id.toast_text)
        textView.text = message

        val toast = Toast(context)
        toast.duration = Toast.LENGTH_LONG
        toast.view = layout
        toast.show()
    }

    @SuppressLint("ClickableViewAccessibility")
    override fun onCreate(savedInstanceState: Bundle?) {

        // theme.applyStyle(R.style.OptOutEdgeToEdgeEnforcement,false)

        super.onCreate(savedInstanceState)

//        if (BuildConfig.DEBUG) {
//
//        }
//
//        StrictMode.setVmPolicy(
//            VmPolicy.Builder()
//                .detectLeakedClosableObjects() // ✨ close() 호출 누락 감지
//                .penaltyLog() // Logcat에 로그 출력
//                .penaltyDeath() // 앱 크래시 (원인 추적 용이)
//                .build()
//        )

        enableEdgeToEdge()

        AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)

        onBackPressedDispatcher.addCallback(this, onBackPressedCallback)

        binding = ActivityMainBinding.inflate(layoutInflater)

        val extraAction = intent.getStringExtra("action")
        Log.d(TAG, "Main onCreate ${intent.action}/${intent.data}/$extraAction")

        window.addFlags(WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES)

        // 시스템 바 숨김 (Full Screen Mode) - by ritoseo
        window.decorView.systemUiVisibility = (
                View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY or
                        View.SYSTEM_UI_FLAG_FULLSCREEN or
                        View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                )

        setContentView(binding.root)

        ViewCompat.setOnApplyWindowInsetsListener(window.decorView) { v: View, insets: WindowInsetsCompat ->
            val systemBars: Insets = insets.getInsets(WindowInsetsCompat.Type.systemBars())
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
            WindowInsetsCompat.CONSUMED
        }

        setupKeyboardVisibilityListener()

        // Must be done after view has been created
        this.setShowWhenLocked(true)
        this.setTurnScreenOn( true)
        Utils.requestDismissKeyguard(this)

        setSupportActionBar(binding.toolbar)
        supportActionBar?.title = "OSVC-C220"

        defaultLayout = binding.defaultLayout
        videoLayout = binding.videoLayout
        videoView = VideoView(applicationContext)
        aorSpinner = binding.aorSpinner
        callTitle = binding.callTitle
        callTimer = binding.callTimer
        callUri = binding.callUri
        securityButton = binding.securityButton
        diverter = binding.diverter
        diverterUri = binding.diverterUri
        callButton = binding.callButton
        callVideoButton = binding.callVideoButton
        hangupButton = binding.hangupButton
        answerButton = binding.answerButton
        answerVideoButton = binding.answerVideoButton
        rejectButton = binding.rejectButton
        callControl = binding.callControl
        holdButton = binding.holdButton
        transferButton = binding.transferButton
        dtmf = binding.dtmf
        infoButton = binding.info
        onHoldNotice = binding.onHoldNotice
        voicemailButton = binding.voicemailButton
        voicemailButtonSpace = binding.voicemailButtonSpace
        videoButton = binding.videoButton
        contactsButton = binding.contactsButton
        messagesButton = binding.messagesButton
        callsButton = binding.callsButton
        dialpadButton = binding.dialpadButton
        swipeRefresh = binding.swipeRefresh

        dialogImgBtn1 = binding.dialogButtonImg1
        dialogImgBtn1.setOnClickListener {

        }
        dialogImgBtn2 = binding.dialogButtonImg2
        callStartButton = binding.callStartBtn
        callStartButton.setOnClickListener {
            val status = Utils.propertyGet("sys.ritosip.sip.status")
            if(status == "registered") {
                callVideoButton.performClick()
            } else {
                showCustomToast(this, "SIP서버에 연결 되어 있지 않습니다.")
            }
        }
        //callHistoryButton = binding.callHistoryBtn
        settingButton = binding.settingBtn
        settingButton.setOnClickListener {
//            val i = Intent(this, NetworkSettingActivity::class.java)
//            i.flags = Intent.FLAG_ACTIVITY_NEW_TASK or
//                    Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP
//            startActivity(i)

//            binding.baseButtonLayout.visibility = View.INVISIBLE
//            binding.mainActivityLayout.visibility = View.INVISIBLE
//            binding.defaultLayout.visibility = View.INVISIBLE
            binding.networkSettingLayout.visibility = View.VISIBLE
            binding.radioDhcp.requestFocus()
        }


        BaresipService.supportedCameras = Utils.supportedCameras(applicationContext).isNotEmpty()

        imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
        nm = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
        am = getSystemService(AUDIO_SERVICE) as AudioManager
        kgm = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager

        binding.editIp.setOnClickListener {
            imm.showSoftInput(it, InputMethodManager.SHOW_IMPLICIT)
        }
        binding.editNetmask.setOnClickListener {
            imm.showSoftInput(it, InputMethodManager.SHOW_IMPLICIT)
        }
        binding.editGateway.setOnClickListener {
            imm.showSoftInput(it, InputMethodManager.SHOW_IMPLICIT)
        }
        binding.editDns.setOnClickListener {
            imm.showSoftInput(it, InputMethodManager.SHOW_IMPLICIT)
        }
        navUpList = HashMap<View, View>()
        navDownList = HashMap<View, View>()

        navUpList.put(binding.editIp, binding.radioStatic)
        navUpList.put(binding.editNetmask, binding.editIp)
        navUpList.put(binding.editGateway, binding.editNetmask)
        navUpList.put(binding.editDns, binding.editGateway)
        navUpList.put(binding.btnApply, binding.editDns)
        //navUpList.put(binding.btnApply, binding.radioDhcp)

        navDownList.put(binding.radioStatic, binding.editIp)
        navDownList.put(binding.editIp, binding.editNetmask)
        navDownList.put(binding.editNetmask, binding.editGateway)
        navDownList.put(binding.editGateway, binding.editDns)
        navDownList.put(binding.editDns, binding.btnApply)
        navDownList.put(binding.radioDhcp, binding.btnApply)

//        setDpadNavigation(binding.editIp, downView = binding.editNetmask)
//        setDpadNavigation(binding.editNetmask, upView = binding.editIp, downView = binding.editGateway)
//        setDpadNavigation(binding.editGateway, upView = binding.editNetmask, downView = binding.editDns)
//        setDpadNavigation(binding.editDns, upView = binding.editGateway, downView = binding.btnApply)
//        setDpadNavigation(binding.radioStatic, downView = binding.editIp)

        binding.btnApply.setOnClickListener { _ ->
            if(binding.radioDhcp.isChecked) {
                val params: HashMap<String, String> = HashMap<String, String>()
                params.put("IPV4TYPE", "DHCP")
                params.put("DEVTYPE", "ETHERNET")
                Utils.sendRequestToFactory(applicationContext, "Ipv4", params)
//                Toast.makeText(
//                    applicationContext,
//                    "DHCP 적용되었습니다.",
//                    Toast.LENGTH_SHORT
//                ).show()
                showCustomToast(applicationContext, "DHCP 적용되었습니다.")
                binding.networkSettingLayout.visibility = View.INVISIBLE
            } else {
                val params: HashMap<String, String> = HashMap<String, String>()
                params.put("IPV4TYPE", "STATIC")
                params.put("DEVTYPE", "ETHERNET")
                params.put("ADDRESS", binding.editIp.text.toString())
                params.put("NETMASK", binding.editNetmask.text.toString())
                params.put("GATEWAY", binding.editGateway.text.toString())
                params.put("DNS", binding.editDns.text.toString())
                Utils.sendRequestToFactory(applicationContext, "Ipv4", params)
//                Toast.makeText(
//                    applicationContext,
//                    "STATIC 적용되었습니다.",
//                    Toast.LENGTH_SHORT
//                ).show()
                showCustomToast(applicationContext, "STATIC 적용되었습니다.")
                binding.networkSettingLayout.visibility = View.INVISIBLE
            }
        }

        binding.btnCancel.setOnClickListener { _ ->
            binding.networkSettingLayout.visibility = View.INVISIBLE
        }

        updateFieldsVisibility()

        val radioGroup = findViewById<RadioGroup>(R.id.radioGroup)
        radioGroup.setOnCheckedChangeListener { _, checkedId ->
            val selected = findViewById<RadioButton>(checkedId)
            selected.requestFocus()
            updateFieldsVisibility()
        }


        serviceEventObserver = Observer {
            val event = it.getContentIfNotHandled()
            Log.d(TAG, "Observed event $event")
            if (event != null && BaresipService.serviceEvents.isNotEmpty()) {
                val first = BaresipService.serviceEvents.removeAt(0)
                handleServiceEvent(first.event, first.params)
            }
        }

        BaresipService.serviceEvent.observeForever(serviceEventObserver)

        screenEventReceiver = object : BroadcastReceiver() {
            override fun onReceive(contxt: Context, intent: Intent) {
                if (kgm.isKeyguardLocked) {
                    Log.d(TAG, "Screen on when locked")
                    this@MainActivity.setShowWhenLocked(Call.inCall())
                }
            }
        }

        if(Utils.propertyGet("sys.rito.debug") == "1") {
            binding.toolbar.visibility = View.VISIBLE
            findViewById<LinearLayout>(R.id.header_bar).visibility = View.GONE
        }

        this.registerReceiver(screenEventReceiver, IntentFilter().apply {
            addAction(Intent.ACTION_SCREEN_ON)
        })

        if (Build.VERSION.SDK_INT >= 31) {
            comDevChangedListener = AudioManager.OnCommunicationDeviceChangedListener { device ->
                if (device != null) {
                    Log.d(TAG, "Com device changed to type ${device.type} in mode ${am.mode}")
                    setSpeakerButtonAndIcon()
                }
            }
            am.addOnCommunicationDeviceChangedListener(mainExecutor, comDevChangedListener)
        }

        uaAdapter = UaSpinnerAdapter(applicationContext, BaresipService.uas)
        aorSpinner.adapter = uaAdapter
        aorSpinner.setSelection(-1)
        aorSpinner.tag = ""
        aorSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
            // Have to allow NULL view, since sometimes when onItemSelected is called, view is NULL.
            // Haven't found any explanation why this can happen.
            override fun onItemSelected(parent: AdapterView<*>, view: View?, position: Int, id: Long) {
                Log.d(TAG, "aorSpinner selecting $position")
                if (position < BaresipService.uas.size) {
                    val ua = BaresipService.uas[position]
                    val acc = ua.account
                    aorSpinner.tag = acc.aor
                    showCall(ua)
                    updateIcons(acc)
                }
            }

            override fun onNothingSelected(parent: AdapterView<*>) {
                Log.d(TAG, "Nothing selected")
            }
        }

        val registrationObserver = Observer<Long> { uaAdapter.notifyDataSetChanged() }
        BaresipService.registrationUpdate.observe(this, registrationObserver)

        accountsRequest = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
            uaAdapter.notifyDataSetChanged()
            spinToAor(activityAor)
            if (aorSpinner.tag != "")
                updateIcons(Account.ofAor(aorSpinner.tag.toString())!!)
            if (BaresipService.isServiceRunning) {
                baresipService.action = "Update Notification"
                startService(baresipService)
            }
        }

        accountRequest = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
            spinToAor(activityAor)
            val ua = UserAgent.ofAor(activityAor)!!
            updateIcons(ua.account)
            if (it.resultCode == Activity.RESULT_OK)
                if (BaresipService.aorPasswords[activityAor] == NO_AUTH_PASS)
                    askPassword(getString(R.string.authentication_password), ua)
        }

        aorSpinner.setOnTouchListener { view, event ->
            if (event.action == MotionEvent.ACTION_DOWN) {
                if (aorSpinner.selectedItemPosition == -1) {
                    val i = Intent(this@MainActivity, AccountsActivity::class.java)
                    val b = Bundle()
                    b.putString("aor", "")
                    i.putExtras(b)
                    accountsRequest.launch(i)
                    true
                } else {
                    if ((event.x - view.left) < 100) {
                        val i = Intent(this@MainActivity, AccountActivity::class.java)
                        val b = Bundle()
                        b.putString("aor", aorSpinner.tag.toString())
                        i.putExtras(b)
                        accountRequest!!.launch(i)
                        true
                    } else {
                        BaresipService.uas[aorSpinner.selectedItemPosition].account.resumeUri =
                                callUri.text.toString()
                        false
                    }
                }
            } else {
                // view.performClick()
                false
            }
        }

        aorSpinner.setOnLongClickListener {
            if (aorSpinner.selectedItemPosition != -1) {
                val ua = UserAgent.ofAor(aorSpinner.tag.toString())
                if (ua != null) {
                    val acc = ua.account
                    if (Api.account_regint(acc.accp) > 0) {
                        Api.account_set_regint(acc.accp, 0)
                        Api.ua_unregister(ua.uap)
                    } else {
                        Api.account_set_regint(acc.accp, acc.configuredRegInt)
                        Api.ua_register(ua.uap)
                    }
                    acc.regint = Api.account_regint(acc.accp)
                    AccountsActivity.saveAccounts()
                }
            }
            true
        }

        callUri.setAdapter(ArrayAdapter(this, android.R.layout.select_dialog_item,
                Contact.contactNames()))
        callUri.threshold = 2
        callUri.setOnFocusChangeListener { view, b ->
            if (b) {
                imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT)
            }
        }
        callUri.setOnClickListener { view ->
            imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT)
        }

        securityButton.setOnClickListener {
            when (securityButton.tag) {
                R.drawable.unlocked -> {
                    Utils.alertView(this, getString(R.string.alert),
                            getString(R.string.call_not_secure))
                }
                R.drawable.locked_yellow -> {
                    Utils.alertView(this, getString(R.string.alert),
                            getString(R.string.peer_not_verified))
                }
                R.drawable.locked_green -> {
                    with(MaterialAlertDialogBuilder(this, R.style.AlertDialogTheme)) {
                        setTitle(R.string.info)
                        setMessage(getString(R.string.call_is_secure))
                        setPositiveButton(getString(R.string.unverify)) { dialog, _ ->
                            val ua = BaresipService.uas[aorSpinner.selectedItemPosition]
                            val call = ua.currentCall()
                            if (call != null) {
                                if (Api.cmd_exec("zrtp_unverify " + call.zid) != 0) {
                                    Log.e(TAG, "Command 'zrtp_unverify ${call.zid}' failed")
                                } else {
                                    securityButton.setImageResource(R.drawable.locked_yellow)
                                    securityButton.tag = R.drawable.locked_yellow
                                }
                            }
                            dialog.dismiss()
                        }
                        setNeutralButton(getString(R.string.cancel)) { dialog, _ ->
                            dialog.dismiss()
                        }
                        show()
                    }
                }
            }
        }

        callButton.setOnClickListener {
            if (aorSpinner.selectedItemPosition >= 0) {
                if (Utils.checkPermissions(this, arrayOf(RECORD_AUDIO)))
                    makeCall("voice")
                else
                    Toast.makeText(applicationContext, getString(R.string.no_calls),
                            Toast.LENGTH_SHORT).show()
            }
        }

        callVideoButton.setOnClickListener {
            if (aorSpinner.selectedItemPosition >= 0) {
                if (Utils.checkPermissions(this, arrayOf(RECORD_AUDIO, CAMERA)))
                    makeCall("video")
                else
                    Toast.makeText(
                        applicationContext, getString(R.string.no_video_calls),
                        Toast.LENGTH_SHORT
                    ).show()
            }
        }

        hangupButton.setOnClickListener {
            val ua = BaresipService.uas[aorSpinner.selectedItemPosition]
            if (Build.VERSION.SDK_INT < 31) {
                if (callRunnable != null) {
                    callHandler.removeCallbacks(callRunnable!!)
                    callRunnable = null
                    BaresipService.abandonAudioFocus(applicationContext)
                    showCall(ua)
                    return@setOnClickListener
                }
            } else {
                if (audioModeChangedListener != null) {
                    am.removeOnModeChangedListener(audioModeChangedListener!!)
                    audioModeChangedListener = null
                    BaresipService.abandonAudioFocus(applicationContext)
                    showCall(ua)
                    return@setOnClickListener
                }
            }
            val aor = ua.account.aor
            val uaCalls = ua.calls()
            if (uaCalls.size > 0) {
                val call = uaCalls[uaCalls.size - 1]
                val callp = call.callp
                Log.d(TAG, "AoR $aor hanging up call $callp with ${callUri.text}")
                hangupButton.isEnabled = false
                Api.ua_hangup(ua.uap, callp, 0, "")
            }
        }

        answerButton.setOnClickListener {
            val ua = BaresipService.uas[aorSpinner.selectedItemPosition]
            val call = ua.currentCall() ?: return@setOnClickListener
            Log.d(TAG, "AoR ${ua.account.aor} answering call from ${callUri.text}")
            answerButton.isEnabled = false
            answerVideoButton.isEnabled = false
            rejectButton.isEnabled = false
            call.setMediaDirection(Api.SDP_SENDRECV, Api.SDP_INACTIVE)
            call.disableVideoStream(true)
            val intent = Intent(this@MainActivity, BaresipService::class.java)
            intent.action = "Call Answer"
            intent.putExtra("uap", ua.uap)
            intent.putExtra("callp", call.callp)
            startService(intent)
        }

        answerVideoButton.setOnClickListener {
            val ua = BaresipService.uas[aorSpinner.selectedItemPosition]
            val aor = ua.account.aor
            val call = ua.calls("in")[0]
            Log.d(TAG, "AoR $aor answering video call ${call.callp} from ${callUri.text}")
            answerButton.isEnabled = false
            answerVideoButton.isEnabled = false
            rejectButton.isEnabled = false
            val videoDir = if (Utils.isCameraAvailable(this))
                Api.SDP_SENDRECV
            else
                Api.SDP_RECVONLY
            call.setMediaDirection(Api.SDP_SENDRECV, videoDir)
            val intent = Intent(this@MainActivity, BaresipService::class.java)
            intent.action = "Call Answer"
            intent.putExtra("uap", ua.uap)
            intent.putExtra("callp", call.callp)
            startService(intent)
        }

        rejectButton.setOnClickListener {
            val ua = BaresipService.uas[aorSpinner.selectedItemPosition]
            val aor = ua.account.aor
            val call = ua.currentCall()!!
            val callp = call.callp
            Log.d(TAG, "AoR $aor rejecting call $callp from ${callUri.text}")
            answerButton.isEnabled = false
            answerVideoButton.isEnabled = false
            rejectButton.isEnabled = false
            call.rejected = true
            Api.ua_hangup(ua.uap, callp, 486, "Busy Here")
        }

        holdButton.setOnClickListener {
            val ua = BaresipService.uas[aorSpinner.selectedItemPosition]
            val aor = ua.account.aor
            val call = ua.currentCall()!!
            if (call.onhold) {
                Log.d(TAG, "AoR $aor resuming call ${call.callp} with ${callUri.text}")
                call.resume()
                call.onhold = false
                holdButton.setImageResource(R.drawable.call_hold)
            } else {
                Log.d(TAG, "AoR $aor holding call ${call.callp} with ${callUri.text}")
                call.hold()
                call.onhold = true
                holdButton.setImageResource(R.drawable.resume)
            }
        }

        transferButton.setOnClickListener {
            val ua = BaresipService.uas[aorSpinner.selectedItemPosition]
            val call = ua.currentCall()
            if (call != null ) {
                if (call.onHoldCall != null) {
                    if (!call.executeTransfer())
                        Utils.alertView(this@MainActivity, getString(R.string.notice),
                                String.format(getString(R.string.transfer_failed)))
                } else {
                    makeTransfer(ua)
                }
            }
        }

        infoButton.setOnClickListener {
            val ua = BaresipService.uas[aorSpinner.selectedItemPosition]
            val call = ua.currentCall()
            if (call != null) {
                val stats = call.stats("audio")
                if (stats != "") {
                    val parts = stats.split(",") as ArrayList
                    if (parts[2] == "0/0") {
                        parts[2] = "?/?"
                        parts[3] = "?/?"
                        parts[4] = "?/?"
                    }
                    val codecs = call.audioCodecs()
                    val duration = call.duration()
                    val txCodec = codecs.split(',')[0].split("/")
                    val rxCodec = codecs.split(',')[1].split("/")
                    Utils.alertView(this, getString(R.string.call_info),
                            "${String.format(getString(R.string.duration), duration)}\n" +
                                    "${getString(R.string.codecs)}: ${txCodec[0]} ch ${txCodec[2]}/" +
                                    "${rxCodec[0]} ch ${rxCodec[2]}\n" +
                                    "${String.format(getString(R.string.rate), parts[0])}\n" +
                                    "${String.format(getString(R.string.average_rate), parts[1])}\n" +
                                    "${getString(R.string.packets)}: ${parts[2]}\n" +
                                    "${getString(R.string.lost)}: ${parts[3]}\n" +
                                    String.format(getString(R.string.jitter), parts[4]))
                } else {
                    Utils.alertView(this, getString(R.string.call_info),
                            getString(R.string.call_info_not_available))
                }
            }
        }

        dtmf.setOnFocusChangeListener { view, hasFocus ->
            if (hasFocus) {
                imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT)
            }
        }
        dtmf.setOnClickListener { view ->
            imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT)
        }

        voicemailButton.setOnClickListener {
            if (aorSpinner.selectedItemPosition >= 0) {
                val ua = BaresipService.uas[aorSpinner.selectedItemPosition]
                val acc = ua.account
                if (acc.vmUri != "") {
                    val dialogClickListener = DialogInterface.OnClickListener { _, which ->
                        when (which) {
                            DialogInterface.BUTTON_POSITIVE -> {
                                val i = Intent(this, MainActivity::class.java)
                                i.flags = Intent.FLAG_ACTIVITY_NEW_TASK or
                                        Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP
                                i.putExtra("action", "call")
                                i.putExtra("uap", ua.uap)
                                i.putExtra("peer", acc.vmUri)
                                startActivity(i)
                            }
                            DialogInterface.BUTTON_NEGATIVE -> {
                            }
                        }
                    }
                    with(MaterialAlertDialogBuilder(this, R.style.AlertDialogTheme)) {
                        setTitle(R.string.voicemail_messages)
                        setMessage(acc.vmMessages(this@MainActivity))
                        setPositiveButton(getString(R.string.listen), dialogClickListener)
                        setNeutralButton(getString(R.string.cancel), dialogClickListener)
                        show()
                    }
                }
            }
        }

        contactsRequest = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
            callUri.setAdapter(ArrayAdapter(this, android.R.layout.select_dialog_item,
                    Contact.contactNames()))
            }

        contactsButton.setOnClickListener {
            val i = Intent(this@MainActivity, ContactsActivity::class.java)
            val b = Bundle()
            if (aorSpinner.selectedItemPosition >= 0)
                b.putString("aor", aorSpinner.tag.toString())
            else
                b.putString("aor", "")
            i.putExtras(b)
            contactsRequest.launch(i)
        }

        chatRequests = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
            spinToAor(activityAor)
            updateIcons(Account.ofAor(activityAor)!!)
        }

        messagesButton.setOnClickListener {
            if (aorSpinner.selectedItemPosition >= 0) {
                val i = Intent(this@MainActivity, ChatsActivity::class.java)
                val b = Bundle()
                b.putString("aor", aorSpinner.tag.toString())
                b.putString("peer", resumeUri)
                i.putExtras(b)
                chatRequests.launch(i)
            }
        }

        callsRequest = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
            spinToAor(activityAor)
            callsButton.setImageResource(R.drawable.calls)
        }

        callsButton.setOnClickListener {
            if (aorSpinner.selectedItemPosition >= 0) {
                val i = Intent(this@MainActivity, CallsActivity::class.java)
                val b = Bundle()
                b.putString("aor", aorSpinner.tag.toString())
                i.putExtras(b)
                callsRequest.launch(i)
            }
        }

        dialpadButton.tag = "off"
        dialpadButton.setOnClickListener {
            if (dialpadButton.tag == "off") {
                callUri.inputType = InputType.TYPE_CLASS_PHONE
                dialpadButton.setImageResource(R.drawable.dialpad_on)
                dialpadButton.tag = "on"
                //Log.d(TAG, "Screen ${Utils.getScreenOrientation(applicationContext)}")
                //val path = BaresipService.downloadsPath + "/video.mp4"
                //Utils.ffmpegExecute("-video_size hd720 -f android_camera -camera_index 1 -i anything -r 10 -t 5 -y $path")
            } else {
                callUri.inputType = InputType.TYPE_CLASS_TEXT +
                        InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
                dialpadButton.setImageResource(R.drawable.dialpad_off)
                dialpadButton.tag = "off"
            }
        }

        videoButton.setOnClickListener {
            videoButton.isClickable = false
            videoButton.setImageResource(R.drawable.video_pending)
            Handler(Looper.getMainLooper()).postDelayed({
                val call = Call.call("connected")
                if (call != null) {
                    val dir = call.videoRequest
                    if (dir != 0) {
                        call.videoRequest = 0
                        call.setVideoDirection(dir)
                    } else {
                        if (Utils.isCameraAvailable(this))
                            call.setVideoDirection(Api.SDP_SENDRECV)
                        else
                            call.setVideoDirection(Api.SDP_RECVONLY)
                    }
                    imm.hideSoftInputFromWindow(dtmf.windowToken, 0)
                }
            }, 250)
        }

        configRequest = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
            if ((it.data != null) && it.data!!.hasExtra("restart")) {
                with(MaterialAlertDialogBuilder(this, R.style.AlertDialogTheme)) {
                    setTitle(R.string.restart_request)
                    setMessage(getString(R.string.config_restart))
                    setPositiveButton(getText(R.string.restart)) { dialog, _ ->
                        dialog.dismiss()
                        quitRestart(true)
                    }
                    setNeutralButton(getText(R.string.cancel)) { dialog, _ ->
                        dialog.dismiss()
                    }
                    show()
                }
            }
            val displayTheme = Preferences(applicationContext).displayTheme
            if (displayTheme != AppCompatDelegate.getDefaultNightMode()) {
                AppCompatDelegate.setDefaultNightMode(displayTheme)
                delegate.applyDayNight()
            }
        }

        backupRequest = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
            if (it.resultCode == Activity.RESULT_OK)
                it.data?.data?.also { uri ->
                    downloadsOutputUri = uri
                    askPassword(getString(R.string.encrypt_password))
                }
        }

        restoreRequest = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
            if (it.resultCode == Activity.RESULT_OK)
                it.data?.data?.also { uri ->
                    downloadsInputUri = uri
                    askPassword(getString(R.string.decrypt_password))
                }
        }

        logcatRequest = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
            if (it.resultCode == Activity.RESULT_OK)
                it.data?.data?.also { uri ->
                    try {
                        val out = contentResolver.openOutputStream(uri)
                        val process = Runtime.getRuntime().exec("logcat -d --pid=${Process.myPid()}")
                        val bufferedReader = process.inputStream.bufferedReader()
                        bufferedReader.forEachLine { line ->
                            out!!.write(line.toByteArray())
                            out.write('\n'.code.toByte().toInt())
                        }
                        out!!.close()
                    } catch (e: Exception) {
                        Log.e(TAG, "Failed to write logcat to file")
                    }
                }
        }

        swipeRefresh.setOnTouchListener(object : OnSwipeTouchListener(this@MainActivity) {

            override fun onSwipeLeft() {
                super.onSwipeLeft()
                if (BaresipService.uas.size > 0) {
                    val curPos = aorSpinner.selectedItemPosition
                    val newPos = if (curPos == -1)
                        0
                    else
                        (curPos + 1) % BaresipService.uas.size
                    if (curPos != newPos) {
                        aorSpinner.setSelection(newPos)
                        showCall(BaresipService.uas[newPos])
                    }
                }
            }

            override fun onSwipeRight() {
                super.onSwipeRight()
                if (BaresipService.uas.size > 0) {
                    val curPos = aorSpinner.selectedItemPosition
                    val newPos = when (curPos) {
                        -1 -> 0
                        0 -> BaresipService.uas.size - 1
                        else -> curPos - 1
                    }
                    if (curPos != newPos) {
                        aorSpinner.setSelection(newPos)
                        showCall(BaresipService.uas[newPos])
                    }
                }
            }
        })

        swipeRefresh.setOnRefreshListener {
            if (BaresipService.uas.size > 0) {
                if (aorSpinner.selectedItemPosition == -1)
                    aorSpinner.setSelection(0)
                val ua = BaresipService.uas[aorSpinner.selectedItemPosition]
                if (ua.account.regint > 0)
                    Api.ua_register(ua.uap)
            }
            swipeRefresh.isRefreshing = false
        }

        baresipService = Intent(this@MainActivity, BaresipService::class.java)

        atStartup = intent.hasExtra("onStartup")

        when (intent?.action) {
            ACTION_DIAL, ACTION_CALL, ACTION_VIEW ->
                if (BaresipService.isServiceRunning)
                    callAction(intent.data, if (intent?.action == ACTION_CALL) "call" else "dial")
                else
                    BaresipService.callActionUri = intent.data.toString()
                            .replace("tel:%2B", "tel:+")
        }

        permissions = if (BaresipService.supportedCameras) {
            if (Build.VERSION.SDK_INT >= 33)
                arrayOf(POST_NOTIFICATIONS, RECORD_AUDIO, CAMERA, BLUETOOTH_CONNECT)
            else if (Build.VERSION.SDK_INT >= 31)
                arrayOf(RECORD_AUDIO, CAMERA, BLUETOOTH_CONNECT)
            else
                arrayOf(RECORD_AUDIO, CAMERA)
        } else {
            if (Build.VERSION.SDK_INT >= 33)
                arrayOf(POST_NOTIFICATIONS, RECORD_AUDIO, BLUETOOTH_CONNECT)
            else if (Build.VERSION.SDK_INT >= 31)
                arrayOf(RECORD_AUDIO, BLUETOOTH_CONNECT)
            else
                arrayOf(RECORD_AUDIO)
        }

        requestPermissionLauncher =
            registerForActivityResult(ActivityResultContracts.RequestPermission()) {}

        requestPermissionsLauncher =
            registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) {
                val denied = mutableListOf<String>()
                val shouldShow = mutableListOf<String>()
                it.forEach { permission ->
                    if (!permission.value) {
                        denied.add(permission.key)
                        if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                                permission.key))
                            shouldShow.add(permission.key)
                    }
                }
                if (denied.contains(POST_NOTIFICATIONS) &&
                    !shouldShow.contains(POST_NOTIFICATIONS)) {
                    with(MaterialAlertDialogBuilder(this, R.style.AlertDialogTheme)) {
                        setTitle(getString(R.string.notice))
                        setMessage(getString(R.string.no_notifications))
                        setPositiveButton(getString(R.string.ok)) { _, _ ->
                            quitRestart(false)
                        }
                        show()
                    }
                } else {
                    if (shouldShow.isNotEmpty())
                        Utils.alertView(this, getString(R.string.permissions_rationale),
                            if (CAMERA in permissions)
                                getString(R.string.audio_and_video_permissions)
                            else
                                getString(R.string.audio_permissions)
                        ) { requestPermissionsLauncher.launch(permissions) }
                    else
                        startBaresip()
                }
            }

        addVideoLayoutViews()

        if (!BaresipService.isServiceRunning) {
            if (File(filesDir.absolutePath + "/accounts").exists()) {
                val accounts = String(
                    Utils.getFileContents(filesDir.absolutePath + "/accounts")!!,
                    Charsets.UTF_8
                ).lines().toMutableList()
                askPasswords(accounts)
            } else {
                // Baresip is started for the first time
                requestPermissionsLauncher.launch(permissions)
            }
        }

    } // OnCreate

    override fun onStart() {
        super.onStart()
        Log.d(TAG, "Main onStart")
        val action = intent.getStringExtra("action")
        if (action != null) {
            // MainActivity was not visible when call, message, or transfer request came in
            intent.removeExtra("action")
            handleIntent(intent, action)
        }
    }

    override fun onResume() {
        super.onResume()
        Log.d(TAG, "Main onResume with action '$resumeAction'")
        nm.cancelAll()
        BaresipService.isMainVisible = true
        when (resumeAction) {
            "call show" -> {
                handleServiceEvent ("call incoming",
                    arrayListOf(resumeCall!!.ua.uap, resumeCall!!.callp))
            }
            "call answer" -> {
                answerButton.performClick()
                showCall(resumeCall!!.ua)
            }
            "call missed" -> {
                callsButton.performClick()
            }
            "call reject" ->
                rejectButton.performClick()
            "call" -> {
                callUri.setText(BaresipService.uas[aorSpinner.selectedItemPosition].account.resumeUri)
                callButton.performClick()
            }
            "dial" -> {
                callUri.setText(BaresipService.uas[aorSpinner.selectedItemPosition].account.resumeUri)
            }
            "call transfer", "transfer show", "transfer accept" ->
                handleServiceEvent("$resumeAction,$resumeUri",
                    arrayListOf(resumeCall!!.ua.uap, resumeCall!!.callp))
            "message", "message show", "message reply" ->
                handleServiceEvent(resumeAction, arrayListOf(resumeUap, resumeUri))
            else -> {
                val incomingCall = Call.call("incoming")
                if (incomingCall != null) {
                    spinToAor(incomingCall.ua.account.aor)
                } else {
                    restoreActivities()
                    if (BaresipService.uas.size > 0) {
                        if (aorSpinner.selectedItemPosition == -1) {
                            if (Call.inCall())
                                spinToAor(Call.calls()[0].ua.account.aor)
                            else {
                                aorSpinner.setSelection(0)
                                aorSpinner.tag = BaresipService.uas[0].account.aor
                            }
                        }
                    }
                }
                uaAdapter.notifyDataSetChanged()
                if (BaresipService.uas.size > 0) {
                    val ua = BaresipService.uas[aorSpinner.selectedItemPosition]
                    showCall(ua)
                    updateIcons(ua.account)
                }
            }
        }
        resumeAction = ""

        val tv = findViewById<TextView>(R.id.textViewHeaderIp)
        tv.setText(BaresipService.deviceIpAddress)
    }

    override fun onPause() {
        super.onPause()
        Log.d(TAG, "Main onPause")
        Utils.addActivity("main")
        BaresipService.isMainVisible = false
        callTimer.stop()
        saveCallUri()
    }

    override fun onStop() {
        super.onStop()
        Log.d(TAG, "Main onStop")
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.d(TAG, "Main onDestroy")
        this.unregisterReceiver(screenEventReceiver)
        if (Build.VERSION.SDK_INT >= 31)
            am.removeOnCommunicationDeviceChangedListener(comDevChangedListener)
        BaresipService.serviceEvent.removeObserver(serviceEventObserver)
        BaresipService.serviceEvents.clear()
        BaresipService.activities.clear()
    }

    var prevLayoutShow = false
    private fun switchVideoLayout(show : Boolean) {
        if(show == prevLayoutShow) {
            return
        }
        prevLayoutShow = show

        if(BaresipService.connectedDisplayCount < 2)
            return

        println("switchVideoLayout : ${show}")
        if(show) {
//            var prm: FrameLayout.LayoutParams =
//                FrameLayout.LayoutParams(
//                    1920,
//                    1080
//                )
//            prm.leftMargin = 0
//            prm.topMargin = 0
//            videoView.surfaceSelfView.layoutParams = prm
//            try {
//                val parentView = videoView.surfaceSelfView.parent
//                (parentView as ViewGroup).removeView(videoView.surfaceSelfView)
//            } catch(e : java.lang.Exception) {
//            }
//            presentation?.setContentView(videoView.surfaceSelfView)
            videoView.surfaceView.bringToFront()
            videoView.surfaceSelfView.bringToFront()
        } else {
//            try {
//                val parentView = videoView.surfaceSelfView.parent
//                (parentView as ViewGroup).removeView(videoView.surfaceSelfView)
//            } catch(e : java.lang.Exception) {
//            }
//
//            var prm2: FrameLayout.LayoutParams =
//                FrameLayout.LayoutParams(
//                    1920,
//                    1080
//                )
//            prm2.leftMargin = 1919
//            prm2.topMargin = 1079
//            videoView.surfaceSelfView.layoutParams = prm2
//            videoLayout.addView(videoView.surfaceSelfView)
//            presentation?.setContentView(videoView.standbyView)
            videoView.standbyView.bringToFront()
        }
    }

    private fun isCallExist() : Boolean {
        if(BaresipService.uas.size == 0)
            return false

        val ua = BaresipService.uas[0]
        val call = ua.currentCall()
        if (call != null) {
            return true
        }

        return false
    }

    private fun updateDisplayLayout() {
        if(BaresipService.connectedDisplayCount > 1) {
            var prm1: RelativeLayout.LayoutParams =
                RelativeLayout.LayoutParams(
                    1920,
                    1080
                )
            prm1.leftMargin = 0
            prm1.topMargin = 0

            videoView.surfaceView.layoutParams = prm1


            var prm2: RelativeLayout.LayoutParams =
                RelativeLayout.LayoutParams(
                    1920,
                    1080
                )
            prm2.leftMargin = 0
            prm2.topMargin = 0

            videoView.surfaceSelfView.layoutParams = prm2
            return
        }

        if(BaresipService.displaySplitMode == BaresipService.DISPLAY_SPLIT_MODE_원거리_최대_근거리_우하단) {
            var prm1: RelativeLayout.LayoutParams =
                RelativeLayout.LayoutParams(
                    1920,
                    1080
                )
            prm1.leftMargin = 0
            prm1.topMargin = 0

            videoView.surfaceView.layoutParams = prm1


            var prm2: RelativeLayout.LayoutParams =
                RelativeLayout.LayoutParams(
                    480,
                    270
                )
            prm2.leftMargin = 1440
            prm2.topMargin = 810

            videoView.surfaceSelfView.layoutParams = prm2
        } else if(BaresipService.displaySplitMode == BaresipService.DISPLAY_SPLIT_MODE_원거리_최대_근거리_우상단) {
            var prm1: RelativeLayout.LayoutParams =
                RelativeLayout.LayoutParams(
                    1920,
                    1080
                )
            prm1.leftMargin = 0
            prm1.topMargin = 0

            videoView.surfaceView.layoutParams = prm1


            var prm2: RelativeLayout.LayoutParams =
                RelativeLayout.LayoutParams(
                    480,
                    270
                )
            prm2.leftMargin = 1440
            prm2.topMargin = 0

            videoView.surfaceSelfView.layoutParams = prm2
        } else if(BaresipService.displaySplitMode == BaresipService.DISPLAY_SPLIT_MODE_원거리_최대_근거리_좌상단) {
            var prm1: RelativeLayout.LayoutParams =
                RelativeLayout.LayoutParams(
                    1920,
                    1080
                )
            prm1.leftMargin = 0
            prm1.topMargin = 0

            videoView.surfaceView.layoutParams = prm1


            var prm2: RelativeLayout.LayoutParams =
                RelativeLayout.LayoutParams(
                    480,
                    270
                )
            prm2.leftMargin = 0
            prm2.topMargin = 0

            videoView.surfaceSelfView.layoutParams = prm2
        } else if(BaresipService.displaySplitMode == BaresipService.DISPLAY_SPLIT_MODE_원거리_최대_근거리_좌하단) {
            var prm1: RelativeLayout.LayoutParams =
                RelativeLayout.LayoutParams(
                    1920,
                    1080
                )
            prm1.leftMargin = 0
            prm1.topMargin = 0

            videoView.surfaceView.layoutParams = prm1


            var prm2: RelativeLayout.LayoutParams =
                RelativeLayout.LayoutParams(
                    480,
                    270
                )
            prm2.leftMargin = 0
            prm2.topMargin = 810

            videoView.surfaceSelfView.layoutParams = prm2
        } else if(BaresipService.displaySplitMode == BaresipService.DISPLAY_SPLIT_MODE_원거리_대상단_근거리_소하단) {
            var prm1: RelativeLayout.LayoutParams =
                RelativeLayout.LayoutParams(
                    1440,
                    810
                )
            prm1.leftMargin = 240
            prm1.topMargin = 0

            videoView.surfaceView.layoutParams = prm1


            var prm2: RelativeLayout.LayoutParams =
                RelativeLayout.LayoutParams(
                    480,
                    270
                )
            prm2.leftMargin = 720
            prm2.topMargin = 810

            videoView.surfaceSelfView.layoutParams = prm2
        } else if(BaresipService.displaySplitMode == BaresipService.DISPLAY_SPLIT_MODE_원거리_대좌단_근거리_소우단) {
            var prm1: RelativeLayout.LayoutParams =
                RelativeLayout.LayoutParams(
                    1440,
                    810
                )
            prm1.leftMargin = 0
            prm1.topMargin = 135

            videoView.surfaceView.layoutParams = prm1


            var prm2: RelativeLayout.LayoutParams =
                RelativeLayout.LayoutParams(
                    480,
                    270
                )
            prm2.leftMargin = 1440
            prm2.topMargin = 135

            videoView.surfaceSelfView.layoutParams = prm2
        } else if(BaresipService.displaySplitMode == BaresipService.DISPLAY_SPLIT_MODE_원거리_대하단_근거리_소상단) {
            var prm1: RelativeLayout.LayoutParams =
                RelativeLayout.LayoutParams(
                    1440,
                    810
                )
            prm1.leftMargin = 240
            prm1.topMargin = 270

            videoView.surfaceView.layoutParams = prm1


            var prm2: RelativeLayout.LayoutParams =
                RelativeLayout.LayoutParams(
                    480,
                    270
                )
            prm2.leftMargin = 720
            prm2.topMargin = 0

            videoView.surfaceSelfView.layoutParams = prm2
        } else if(BaresipService.displaySplitMode == BaresipService.DISPLAY_SPLIT_MODE_원거리_반좌단_근거리_반우단) {
            var prm1: RelativeLayout.LayoutParams =
                RelativeLayout.LayoutParams(
                    960,
                    540
                )
            prm1.leftMargin = 0
            prm1.topMargin = 270

            videoView.surfaceView.layoutParams = prm1


            var prm2: RelativeLayout.LayoutParams =
                RelativeLayout.LayoutParams(
                    960,
                    540
                )
            prm2.leftMargin = 960
            prm2.topMargin = 270

            videoView.surfaceSelfView.layoutParams = prm2
        } else if(BaresipService.displaySplitMode == BaresipService.DISPLAY_SPLIT_MODE_원거리_최대_근거리_없음) {
            var prm1: RelativeLayout.LayoutParams =
                RelativeLayout.LayoutParams(
                    1920,
                    1080
                )
            prm1.leftMargin = 0
            prm1.topMargin = 0

            videoView.surfaceView.layoutParams = prm1


            var prm2: RelativeLayout.LayoutParams =
                RelativeLayout.LayoutParams(
                    1,
                    1
                )
            prm2.leftMargin = 0
            prm2.topMargin = 0

            videoView.surfaceSelfView.layoutParams = prm2
        } else if(BaresipService.displaySplitMode == BaresipService.DISPLAY_SPLIT_MODE_원거리_없음_근거리_최대) {
            var prm1: RelativeLayout.LayoutParams =
                RelativeLayout.LayoutParams(
                    1,
                    1
                )
            prm1.leftMargin = 0
            prm1.topMargin = 0

            videoView.surfaceView.layoutParams = prm1


            var prm2: RelativeLayout.LayoutParams =
                RelativeLayout.LayoutParams(
                    1920,
                    1080
                )
            prm2.leftMargin = 0
            prm2.topMargin = 0

            videoView.surfaceSelfView.layoutParams = prm2
        }

    }

    private fun updateInfo() {
        val view = findViewById<TextView>(R.id.textViewDeviceName)
        view.setText(BaresipService.deviceName)

        val now = LocalDateTime.now()

        val formatter = DateTimeFormatter.ofPattern("yyyy년 M월 d일(E요일) a h시 m분", Locale.KOREAN)
        val viewDate = findViewById<TextView>(R.id.textViewDatetime)
        val formattedDate = now.format(formatter)
        viewDate.setText(formattedDate)
    }

    private fun updateDisplay() {
        println("Update Display : ${BaresipService.connectedDisplayCount}, prevDisplayCount : ${BaresipService.prevConnectedDisplayCount}")
        if(BaresipService.connectedDisplayCount < 2) {
            if(BaresipService.prevConnectedDisplayCount != BaresipService.connectedDisplayCount) {
                try {
                    presentation?.dismiss()
                } catch(e : java.lang.Exception) {
                }

                try {
                    val parentView = videoView.surfaceView.parent
                    (parentView as ViewGroup).removeView(videoView.surfaceView)
                } catch(e : java.lang.Exception) {
                }

                try {
                    val parentView = videoView.surfaceSelfView.parent
                    (parentView as ViewGroup).removeView(videoView.surfaceSelfView)
                } catch(e : java.lang.Exception) {
                }

                try {
                    val parentView = videoView.standbyView.parent
                    (parentView as ViewGroup).removeView(videoView.standbyView)
                } catch(e : java.lang.Exception) {
                }


                var prm: FrameLayout.LayoutParams =
                    FrameLayout.LayoutParams(
                        1920,
                        1080
                    )
                prm.leftMargin = 0
                prm.topMargin = 0
                videoView.surfaceView.layoutParams = prm
                videoLayout.addView(videoView.surfaceView)
            }

            try {
                val parentView = videoView.surfaceSelfView.parent
                (parentView as ViewGroup).removeView(videoView.surfaceSelfView)
            } catch(e : java.lang.Exception) {
            }

            var prm2: RelativeLayout.LayoutParams =
                RelativeLayout.LayoutParams(
                    1,
                    1
                )
            prm2.leftMargin = 0
            prm2.topMargin = 0

            videoView.surfaceSelfView.layoutParams = prm2
            videoLayout.addView(videoView.surfaceSelfView)

            updateDisplayLayout()
        } else {
            try {
                presentation?.dismiss()
            } catch(e : java.lang.Exception) {
            }

            try {
                val parentView = videoView.surfaceView.parent
                (parentView as ViewGroup).removeView(videoView.surfaceView)
            } catch(e : java.lang.Exception) {
            }

            try {
                val parentView = videoView.surfaceSelfView.parent
                (parentView as ViewGroup).removeView(videoView.surfaceSelfView)
            } catch(e : java.lang.Exception) {
            }

            try {
                val parentView = videoView.standbyView.parent
                (parentView as ViewGroup).removeView(videoView.standbyView)
            } catch(e : java.lang.Exception) {
            }

            var useSecondScreenAsFar = false
            if(BaresipService.farViewDisplayId == 1) {
                useSecondScreenAsFar = false
            } else if(BaresipService.farViewDisplayId == 2) {
                useSecondScreenAsFar = true
            }

            val displayManager = getSystemService(DISPLAY_SERVICE) as DisplayManager
            val displays = displayManager.displays
            val secondDisplay = displays[1]  // 두 번째 디스플레이 가져오기
            presentation = SecondScreenPresentation(this, secondDisplay)

            var prm1: RelativeLayout.LayoutParams =
                RelativeLayout.LayoutParams(
                    1920,
                    1080
                )
            prm1.leftMargin = 0
            prm1.topMargin = 0
            videoView.surfaceView.layoutParams = prm1

            if(useSecondScreenAsFar) {
                //presentation?.setContentView(videoView.surfaceView)
                presentation?.setContentView(R.layout.presentation_layout)
                val presentationView = presentation?.window?.decorView?.findViewById<RelativeLayout>(R.id.presentationLayout)
                presentationView?.addView(videoView.surfaceView)
                presentationView?.addView(videoView.standbyView)
            } else {
                var prm2: RelativeLayout.LayoutParams =
                    RelativeLayout.LayoutParams(
                        1920,
                        1080
                    )
                prm2.leftMargin = 0
                prm2.topMargin = 0
                videoView.surfaceSelfView.layoutParams = prm2
                presentation?.setContentView(R.layout.presentation_layout)
                val presentationView = presentation?.window?.decorView?.findViewById<RelativeLayout>(R.id.presentationLayout)
                try {
                    presentationView?.addView(videoView.surfaceSelfView)
                } catch(e : java.lang.Exception) {
                }
                try {
                    presentationView?.addView(videoView.standbyView)
                } catch(e : java.lang.Exception) {
                }
            }

            if(useSecondScreenAsFar) {
                var prm2: RelativeLayout.LayoutParams =
                    RelativeLayout.LayoutParams(
                        1920,
                        1080
                    )
                prm2.leftMargin = 0
                prm2.topMargin = 0
                videoView.surfaceSelfView.layoutParams = prm2
                videoLayout.addView(videoView.surfaceSelfView)
            } else {
                videoLayout.addView(videoView.surfaceView)
            }

            if(isCallExist()) {
                videoView.surfaceSelfView.bringToFront()
                videoView.surfaceView.bringToFront()
            } else {
                videoView.standbyView.bringToFront()
            }
            presentation?.show()
        }

        BaresipService.prevConnectedDisplayCount = BaresipService.connectedDisplayCount
    }

    private fun addVideoLayoutViews() {
        var connectedCount = 0
        var useSplit = false
        for(idx in 0..1) {
            if (Utils.checkDisplayConnection(idx) == "connected")
                connectedCount++
        }

        BaresipService.connectedDisplayCount = connectedCount

        if(connectedCount < 2)
            useSplit = true

        //videoLayout.addView(videoView.surfaceView)
        if(useSplit) {
            var prm: FrameLayout.LayoutParams =
                FrameLayout.LayoutParams(
                    1920,
                    1080
                )
            prm.leftMargin = 0
            prm.topMargin = 0
            videoView.surfaceView.layoutParams = prm
            videoLayout.addView(videoView.surfaceView)
            //videoView.surfaceSelfView.bringToFront()

            var prm2: FrameLayout.LayoutParams =
                FrameLayout.LayoutParams(
                    480,
                    270
                )
            prm2.leftMargin = 1440
            prm2.topMargin = 810

            videoView.surfaceSelfView.layoutParams = prm2
            videoLayout.addView(videoView.surfaceSelfView)

            updateDisplayLayout()
        } else {
            var useSecondScreenAsFar = false
            // DisplayManager 가져오기
            val displayManager = getSystemService(DISPLAY_SERVICE) as DisplayManager
            val displays = displayManager.displays

            if(BaresipService.farViewDisplayId == 1) {
                useSecondScreenAsFar = false
            } else if(BaresipService.farViewDisplayId == 2) {
                useSecondScreenAsFar = true
            }

            if (displays.size > 1) { // 보조 디스플레이가 있는 경우
                println("디스플레이 상태 : [0]${Utils.checkDisplayConnection(0)} [1]${Utils.checkDisplayConnection(1)}")
                val secondDisplay = displays[1]  // 두 번째 디스플레이 가져오기
                presentation = SecondScreenPresentation(this, secondDisplay)
                if(useSecondScreenAsFar) {
                    presentation?.setContentView(R.layout.presentation_layout)
                    val presentationView = presentation?.window?.decorView?.findViewById<RelativeLayout>(R.id.presentationLayout)
                    presentationView?.addView(videoView.surfaceView)
                    presentationView?.addView(videoView.standbyView)
                } else {
                    //presentation?.setContentView(videoView.surfaceSelfView)
                    var prm2: FrameLayout.LayoutParams =
                        FrameLayout.LayoutParams(
                            1920,
                            1080
                        )
                    prm2.leftMargin = 0
                    prm2.topMargin = 0
                    videoView.surfaceSelfView.layoutParams = prm2
                    //videoLayout.addView(videoView.surfaceSelfView)
                    presentation?.setContentView(R.layout.presentation_layout)
                    val presentationView = presentation?.window?.decorView?.findViewById<RelativeLayout>(R.id.presentationLayout)
                    presentationView?.addView(videoView.surfaceSelfView)
                    presentationView?.addView(videoView.standbyView)
                    //presentation?.setContentView(videoView.standbyView)
                }
                presentation?.show()
            } else {
                var prm2: RelativeLayout.LayoutParams =
                    RelativeLayout.LayoutParams(
                        1920,
                        1080
                    )
                prm2.leftMargin = 1919
                prm2.topMargin = 1079
                videoView.surfaceSelfView.layoutParams = prm2
                videoLayout.addView(videoView.surfaceSelfView)
                //videoView.surfaceSelfView.visibility = View.INVISIBLE
            }

            if(useSecondScreenAsFar) {
                videoLayout.addView(videoView.surfaceSelfView)
            } else {
                videoLayout.addView(videoView.surfaceView)
            }
        }


        // Video Button
        val vb = ImageButton(this)
        vb.id = View.generateViewId()
        vb.setImageResource(R.drawable.video_off)
        vb.setBackgroundResource(0)
        var prm: RelativeLayout.LayoutParams =
                RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT,
                        RelativeLayout.LayoutParams.WRAP_CONTENT)
        prm.addRule(RelativeLayout.ALIGN_PARENT_LEFT)
        prm.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM)
        prm.marginStart = 15
        prm.bottomMargin = 15
        vb.layoutParams = prm
        vb.setOnClickListener {
            Call.call("connected")?.setVideoDirection(Api.SDP_INACTIVE)
        }
        videoLayout.addView(vb)
        vb.visibility = View.INVISIBLE      // by ritoseo

        // Camera Button
        val cb = ImageButton(this)
        if (!Utils.isCameraAvailable(this))
            cb.visibility = View.INVISIBLE
        cb.visibility = View.INVISIBLE      // by ritoseo
        cb.id = View.generateViewId()
        cb.setImageResource(R.drawable.camera_front)
        cb.setBackgroundResource(0)
        prm = RelativeLayout.LayoutParams(
            RelativeLayout.LayoutParams.WRAP_CONTENT,
            RelativeLayout.LayoutParams.WRAP_CONTENT
        )
        prm.addRule(RelativeLayout.ALIGN_PARENT_LEFT)
        prm.addRule(RelativeLayout.ALIGN_PARENT_TOP)
        prm.marginStart = 15
        prm.topMargin = 15
        cb.layoutParams = prm
        cb.setOnClickListener {
            val call = Call.call("connected")
            if (call != null) {
                if (call.setVideoSource(!BaresipService.cameraFront) != 0)
                    Log.w(TAG, "Failed to set video source")
                else
                    BaresipService.cameraFront = !BaresipService.cameraFront
                if (BaresipService.cameraFront)
                    cb.setImageResource(R.drawable.camera_front)
                else
                    cb.setImageResource(R.drawable.camera_rear)
            }
        }
        videoLayout.addView(cb)

        // Snapshot Button
        if ((Build.VERSION.SDK_INT >= 29) ||
                Utils.checkPermissions(this, arrayOf(WRITE_EXTERNAL_STORAGE))) {
            val sb = ImageButton(this)
            sb.setImageResource(R.drawable.snapshot)
            sb.setBackgroundResource(0)
            prm = RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT,
                    RelativeLayout.LayoutParams.WRAP_CONTENT)
            prm.addRule(RelativeLayout.ALIGN_PARENT_LEFT)
            prm.addRule(RelativeLayout.BELOW, cb.id)
            prm.marginStart = 15
            prm.topMargin= 24
            sb.layoutParams = prm
            sb.setOnClickListener {
                val sdf = SimpleDateFormat("yyyyMMdd_hhmmss", Locale.getDefault())
                val fileName = "IMG_" + sdf.format(Date()) + ".png"
                val filePath = BaresipService.filesPath + "/" + fileName
                if (Api.cmd_exec("snapshot_recv $filePath") != 0)
                    Log.e(TAG, "Command 'snapshot_recv $filePath' failed")
                else
                    MediaActionSound().play(MediaActionSound.SHUTTER_CLICK)
            }
            videoLayout.addView(sb)
            sb.visibility = View.INVISIBLE      // by ritoseo
        }

        // Lock Button
        videoSecurityButton = ImageButton(this)
        videoSecurityButton.visibility = View.INVISIBLE
        videoSecurityButton.setBackgroundResource(0)
        prm = RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT,
                RelativeLayout.LayoutParams.WRAP_CONTENT)
        prm.addRule(RelativeLayout.ALIGN_PARENT_LEFT)
        prm.addRule(RelativeLayout.ABOVE, vb.id)
        prm.marginStart = 15
        prm.bottomMargin= 24
        videoSecurityButton.layoutParams = prm
        videoSecurityButton.setOnClickListener {
            when (securityButton.tag) {
                R.drawable.unlocked -> {
                    Utils.alertView(this, getString(R.string.alert),
                            getString(R.string.call_not_secure))
                }
                R.drawable.locked_yellow -> {
                    Utils.alertView(this, getString(R.string.alert),
                            getString(R.string.peer_not_verified))
                }
                R.drawable.locked_green -> {
                    with(MaterialAlertDialogBuilder(this, R.style.AlertDialogTheme)) {
                        setTitle(R.string.info)
                        setMessage(getString(R.string.call_is_secure))
                        setPositiveButton(getString(R.string.unverify)) { dialog, _ ->
                            val ua = BaresipService.uas[aorSpinner.selectedItemPosition]
                            val call = ua.currentCall()
                            if (call != null) {
                                if (Api.cmd_exec("zrtp_unverify " + call.zid) != 0) {
                                    Log.e(TAG, "Command 'zrtp_unverify ${call.zid}' failed")
                                } else {
                                    securityButton.tag = R.drawable.locked_yellow
                                    videoSecurityButton.setImageResource(R.drawable.locked_video_yellow)
                                }
                            }
                            dialog.dismiss()
                        }
                        setNeutralButton(getString(R.string.cancel)) { dialog, _ ->
                            dialog.dismiss()
                        }
                        show()
                    }
                }
            }
        }
        videoLayout.addView(videoSecurityButton)
        videoSecurityButton.visibility = View.INVISIBLE      // by ritoseo

        // Speaker Button
        speakerButton = ImageButton(this)
        speakerButton.id = View.generateViewId()
        speakerButton.setBackgroundResource(0)
        prm = RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT,
                RelativeLayout.LayoutParams.WRAP_CONTENT)
        prm.addRule(RelativeLayout.ALIGN_PARENT_RIGHT)
        prm.addRule(RelativeLayout.ALIGN_PARENT_TOP)
        prm.marginEnd = 15
        prm.topMargin = 15
        speakerButton.layoutParams = prm
        speakerButton.setOnClickListener {
            Utils.toggleSpeakerPhone(ContextCompat.getMainExecutor(this), am)
            setSpeakerButtonAndIcon()
        }
        setSpeakerButtonAndIcon()
        videoLayout.addView(speakerButton)
        speakerButton.visibility = View.INVISIBLE      // by ritoseo

        // Mic Button
        val mb = ImageButton(this)
        mb.setBackgroundResource(0)
        prm = RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT,
                RelativeLayout.LayoutParams.WRAP_CONTENT)
        prm.addRule(RelativeLayout.ALIGN_PARENT_RIGHT)
        prm.addRule(RelativeLayout.BELOW, speakerButton.id)
        prm.marginEnd = 15
        prm.topMargin= 24
        mb.layoutParams = prm
        if (BaresipService.isMicMuted)
            mb.setImageResource(R.drawable.mic_off_button)
        else
            mb.setImageResource(R.drawable.mic_on_button)
        mb.setOnClickListener {
            BaresipService.isMicMuted = !BaresipService.isMicMuted
            if (BaresipService.isMicMuted) {
                mb.setImageResource(R.drawable.mic_off_button)
                Api.calls_mute(true)
            } else {
                mb.setImageResource(R.drawable.mic_on_button)
                Api.calls_mute(false)
            }
        }
        videoLayout.addView(mb)
        mb.visibility = View.INVISIBLE      // by ritoseo

        // Hangup Button
        val hb = ImageButton(this)
        hb.id = View.generateViewId()
        hb.setImageResource(R.drawable.hangup)
        hb.setBackgroundResource(0)
        prm = RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT,
                RelativeLayout.LayoutParams.WRAP_CONTENT)
        prm.addRule(RelativeLayout.ALIGN_PARENT_RIGHT)
        prm.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM)
        prm.marginEnd = 15
        prm.bottomMargin = 15
        hb.layoutParams = prm
        hb.setOnClickListener {
            if (!Utils.isCameraAvailable(this))
                Call.call("connected")?.setVideoDirection(Api.SDP_INACTIVE)
            hangupButton.performClick()
        }
        videoLayout.addView(hb)

        // Info Button
        val ib = ImageButton(this)
        ib.setImageResource(R.drawable.video_info)
        ib.setBackgroundResource(0)
        prm = RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT,
                RelativeLayout.LayoutParams.WRAP_CONTENT)
        prm.addRule(RelativeLayout.ALIGN_PARENT_RIGHT)
        prm.addRule(RelativeLayout.ABOVE, hb.id)
        prm.marginEnd = 15
        prm.bottomMargin= 24
        ib.layoutParams = prm
        ib.setOnClickListener {
            val ua = BaresipService.uas[aorSpinner.selectedItemPosition]
            val calls = ua.calls()
            if (calls.size > 0) {
                val call = calls[0]
                val stats = call.stats("video")
                if (stats != "") {
                    val parts = stats.split(",")
                    val codecs = call.videoCodecs().split(',')
                    val duration = call.duration()
                    val txCodec = codecs[0]
                    val rxCodec = codecs[1]
                    Utils.alertView(this, getString(R.string.call_info),
                            "${String.format(getString(R.string.duration), duration)}\n" +
                                    "${getString(R.string.codecs)}: $txCodec/$rxCodec\n" +
                                    "${String.format(getString(R.string.rate), parts[0])}\n" +
                                    "${String.format(getString(R.string.average_rate), parts[1])}\n" +
                                    "${String.format(getString(R.string.jitter), parts[4])}\n" +
                                    "${getString(R.string.packets)}: ${parts[2]}\n" +
                                    "${getString(R.string.lost)}: ${parts[3]}")
                } else {
                    Utils.alertView(this, getString(R.string.call_info),
                            getString(R.string.call_info_not_available))
                }
            }
        }
        videoLayout.addView(ib)
        ib.visibility = View.INVISIBLE      // by ritoseo

        // OnHold Notice
        videoOnHoldNotice = TextView(this)
        prm = RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT,
                RelativeLayout.LayoutParams.WRAP_CONTENT)
        prm.addRule(RelativeLayout.CENTER_HORIZONTAL, RelativeLayout.TRUE)
        prm.addRule(RelativeLayout.CENTER_VERTICAL, RelativeLayout.TRUE)
        videoOnHoldNotice.layoutParams = prm
        videoOnHoldNotice.text = getString(R.string.call_is_on_hold)
        videoOnHoldNotice.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18f)
        videoOnHoldNotice.setTextColor(ContextCompat.getColor(this, R.color.colorAccent))
        videoOnHoldNotice.visibility = View.GONE
        videoLayout.addView(videoOnHoldNotice)
        videoOnHoldNotice.visibility = View.INVISIBLE      // by ritoseo

    }

    private fun setSpeakerButtonAndIcon() {
        if (Utils.isSpeakerPhoneOn(am)) {
            speakerButton.setImageResource(R.drawable.speaker_on_button)
            if (speakerIcon != null) speakerIcon!!.setIcon(R.drawable.speaker_on)
        } else {
            speakerButton.setImageResource(R.drawable.speaker_off_button)
            if (speakerIcon != null) speakerIcon!!.setIcon(R.drawable.speaker_off)
        }
    }

    override fun onConfigurationChanged(newConfig: Configuration) {
        super.onConfigurationChanged(newConfig)
        if (dtmf.isEnabled)
            dtmf.requestFocus()
    }

    override fun onNewIntent(intent: Intent) {
        // Called when MainActivity already exists at the top of current task
        super.onNewIntent(intent)

        this.setShowWhenLocked(true)
        this.setTurnScreenOn(true)

        resumeAction = ""
        resumeUri = ""

        Log.d(TAG, "onNewIntent with action/data '${intent.action}/${intent.data}'")

        when (intent.action) {
            ACTION_DIAL, ACTION_CALL, ACTION_VIEW -> {
                callAction(intent.data, if (intent.action == ACTION_CALL) "call" else "dial")
            }
            else -> {
                val action = intent.getStringExtra("action")
                if (action != null) {
                    intent.removeExtra("action")
                    handleIntent(intent, action)
                }
            }
        }
    }

    private fun callAction(uri: Uri?, action: String) {
        if (Call.inCall() || BaresipService.uas.size == 0)
            return
        Log.d(TAG, "Action $action to $uri")
        if (uri != null) {
            when (uri.scheme) {
                "sip" -> {
                    val uriStr = Utils.uriUnescape(uri.toString())
                    var ua = UserAgent.ofDomain(Utils.uriHostPart(uriStr))
                    if (ua == null && BaresipService.uas.size > 0)
                        ua = BaresipService.uas[0]
                    if (ua == null) {
                        Log.w(TAG, "No accounts for '$uriStr'")
                        return
                    }
                    spinToAor(ua.account.aor)
                    resumeAction = action
                    ua.account.resumeUri = uriStr
                }
                "tel" -> {
                    val uriStr = uri.toString().replace("%2B", "+")
                        .replace("%20", "")
                        .filterNot{setOf('-', ' ', '(', ')').contains(it)}
                    var account: Account? = null
                    for (a in Account.accounts())
                        if (a.telProvider != "") {
                            account = a
                            break
                        }
                    if (account == null) {
                        Log.w(TAG, "No telephony providers for '$uriStr'")
                        return
                    }
                    spinToAor(account.aor)
                    resumeAction = action
                    account.resumeUri = uriStr
                }
                else -> {
                    Log.w(TAG, "Unsupported URI scheme ${uri.scheme}")
                    return
                }
            }
        }
    }

    private fun handleIntent(intent: Intent, action: String) {
        Log.d(TAG, "Handling intent '$action'")
        Log.w(TAG, "<RITO> Handling intent '$action'")
        val ev = action.split(",")
        when (ev[0]) {
            "no network" -> {       // 네트워크 안잡힌 경우
                /*
                Utils.alertView(this, getString(R.string.notice),
                    getString(R.string.no_network))
                 */
                return
            }
            /* Added by ritoseo */
            "network available" -> {
                val tv = findViewById<TextView>(R.id.textViewHeaderIp)
                tv.setText(intent.getStringExtra("address")!!)
            }
            "video call" -> {
                callUri.setText(intent.getStringExtra("peer")!!)
                println("resumeUri : ${callUri.text}")
                makeCall("video", false)
            }
            "hangup" -> {
                hangupButton.performClick()
            }
            "update display" -> {
                updateDisplay()
            }
            "update layout" -> {
                updateDisplayLayout()
            }
            "update info" -> {
                updateInfo()
            }
            /********************/
            "call", "dial" -> {
                if (Call.inCall()) {
                    Toast.makeText(applicationContext, getString(R.string.call_already_active),
                            Toast.LENGTH_SHORT).show()
                    return
                }
                val uap = intent.getLongExtra("uap", 0L)
                val ua = UserAgent.ofUap(uap)
                if (ua == null) {
                    Log.w(TAG, "handleIntent 'call' did not find ua $uap")
                    return
                }
                if (ua.account.aor != aorSpinner.tag)
                    spinToAor(ua.account.aor)
                resumeAction = action
                ua.account.resumeUri = intent.getStringExtra("peer")!!
            }
            "call show", "call answer" -> {
                val callp = intent.getLongExtra("callp", 0L)
                val call = Call.ofCallp(callp)
                if (call == null) {
                    Log.w(TAG, "handleIntent '$action' did not find call $callp")
                    return
                }
                val ua = call.ua
                if (ua.account.aor != aorSpinner.tag)
                    spinToAor(ua.account.aor)
                resumeAction = action
                resumeCall = call

                if(ev[0] == "call answer") {
                    Handler(Looper.getMainLooper()).postDelayed({
                        videoButton.performClick()
                    }, 1000)
                }
            }
            "call missed" -> {
                val uap = intent.getLongExtra("uap", 0L)
                val ua = UserAgent.ofUap(uap)
                if (ua == null) {
                    Log.w(TAG, "handleIntent did not find ua $uap")
                    return
                }
                if (ua.account.aor != aorSpinner.tag)
                    spinToAor(ua.account.aor)
                resumeAction = action
            }
            "call transfer", "transfer show", "transfer accept" -> {
                val callp = intent.getLongExtra("callp", 0L)
                val call = Call.ofCallp(callp)
                if (call == null) {
                    Log.w(TAG, "handleIntent '$action' did not find call $callp")
                    moveTaskToBack(true)
                    return
                }
                resumeAction = ev[0]
                resumeCall = call
                resumeUri = if (ev[0] == "call transfer")
                    ev[1]
                else
                    intent.getStringExtra("uri")!!
            }
            "message", "message show", "message reply" -> {
                val uap = intent.getLongExtra("uap", 0L)
                val ua = UserAgent.ofUap(uap)
                if (ua == null) {
                    Log.w(TAG, "handleIntent did not find ua $uap")
                    return
                }
                if (ua.account.aor != aorSpinner.tag)
                    spinToAor(ua.account.aor)
                resumeAction = action
                resumeUap = uap
                resumeUri = intent.getStringExtra("peer")!!
            }
        }
    }

    override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
        val BTN_MISC = 188
        //val BTN_MENU = 58
        val SCANCODE_OPTION = 357      //MENU키 -> OPTION키로 매핑
        //println("onKeyDown : ${keyCode}")
        //println("onKeyDown2 : ${event?.scanCode}")
        val stream = if (am.mode == AudioManager.MODE_RINGTONE)
            AudioManager.STREAM_RING
        else
            AudioManager.STREAM_MUSIC
        when (keyCode) {
            KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.KEYCODE_VOLUME_UP -> {
                am.adjustStreamVolume(stream,
                        if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)
                            AudioManager.ADJUST_LOWER else
                            AudioManager.ADJUST_RAISE,
                        AudioManager.FLAG_SHOW_UI)
                Log.d(TAG, "Adjusted volume $keyCode of stream $stream to ${am.getStreamVolume(stream)}")
                return true
            }
            KeyEvent.KEYCODE_BACK -> {
                //callVideoButton.requestFocus()
                return true
            }
            KeyEvent.KEYCODE_0, KeyEvent.KEYCODE_1, KeyEvent.KEYCODE_2, KeyEvent.KEYCODE_3, KeyEvent.KEYCODE_4, KeyEvent.KEYCODE_5, KeyEvent.KEYCODE_6,
            KeyEvent.KEYCODE_7, KeyEvent.KEYCODE_8, KeyEvent.KEYCODE_9
            -> {
                callUri.setText(callUri.text.toString() + (keyCode - KeyEvent.KEYCODE_0).toString())
            }
            KeyEvent.KEYCODE_DEL -> {
                val editable = callUri.text
                val length = editable.length
                if (length > 0) {
                    editable.delete(length - 1, length)
                }
            }
            BTN_MISC -> {
                return true
            }
        }
        when (event?.scanCode) {
            SCANCODE_OPTION -> {
                val dialog = findViewById<FrameLayout>(R.id.dialogLayout)
                if(dialog.isVisible) {
                    dialog.visibility = View.INVISIBLE
                } else {
                    dialog.visibility = View.VISIBLE
                    findViewById<AppCompatButton>(R.id.dialogButtonImg1).requestFocus()
                }
//                val dialog = findViewById<FrameLayout>(R.id.dialogBase)
//                val customButton = LayoutInflater.from(this).inflate(R.layout.image_button, null)
//                dialog.addView(customButton)
                return true
            }
        }


        return super.onKeyDown(keyCode, event)
    }

    private fun handleServiceEvent(event: String, params: ArrayList<Any>) {

        fun handleNextEvent(logMessage: String? = null) {
            if (logMessage != null)
                Log.w(TAG, logMessage)
            if (BaresipService.serviceEvents.isNotEmpty()) {
                val first = BaresipService.serviceEvents.removeAt(0)
                handleServiceEvent(first.event, first.params)
            }
        }

        if (taskId == -1) {
            handleNextEvent("Omit service event '$event' for task -1")
            return
        }

        if (event == "started") {
            val uriString = params[0] as String
            Log.d(TAG, "Handling service event 'started' with URI '$uriString'")
            if (!this::uaAdapter.isInitialized) {
                // Android has restarted baresip when permission has been denied in app settings
                recreate()
                return
            }
            uaAdapter.notifyDataSetChanged()
            if (uriString != "") {
                callAction(uriString.toUri(), "dial")
            } else {
                if ((aorSpinner.selectedItemPosition == -1) && (BaresipService.uas.size > 0)) {
                    aorSpinner.setSelection(0)
                    aorSpinner.tag = BaresipService.uas[0].account.aor
                }
            }
            if (Preferences(applicationContext).displayTheme != AppCompatDelegate.getDefaultNightMode()) {
                AppCompatDelegate.setDefaultNightMode(Preferences(applicationContext).displayTheme)
                delegate.applyDayNight()
            }

            updateDisplay()
            handleNextEvent()
            return
        }

        if (event == "stopped") {
            Log.d(TAG, "Handling service event 'stopped' with start error '${params[0]}'")
            if (params[0] != "") {
                Utils.alertView(this, getString(R.string.notice), getString(R.string.start_failed))
            } else {
                finishAndRemoveTask()
                if (restart)
                    reStart()
                else
                    exitProcess(0)
            }
            return
        }

        val uap = params[0] as Long
        val ua = UserAgent.ofUap(uap)
        if (ua == null) {
            handleNextEvent("handleServiceEvent '$event' did not find ua $uap")
            return
        }

        val ev = event.split(",")
        Log.d(TAG, "Handling service event '${ev[0]}' for $uap")
        val acc = ua.account
        val aor = ua.account.aor

        when (ev[0]) {
            "call rejected" -> {
                if (aor == aorSpinner.tag) {
                    callsButton.setImageResource(R.drawable.calls_missed)
                }
            }
            "call incoming", "call outgoing" -> {
                val callp = params[1] as Long
                if (BaresipService.isMainVisible) {
                    uaAdapter.notifyDataSetChanged()
                    if (aor != aorSpinner.tag)
                        spinToAor(aor)
                    showCall(ua, Call.ofCallp(callp))
                } else {
                    Log.d(TAG, "Reordering to front")
                    BaresipService.activities.clear()
                    BaresipService.serviceEvents.clear()
                    val i = Intent(applicationContext, MainActivity::class.java)
                    i.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
                    i.putExtra("action", "call show")
                    i.putExtra("callp", callp)
                    startActivity(i)
                    return
                }
            }
            "call answered" -> {
                showCall(ua)
            }
            "call redirect", "video call redirect" -> {
                val redirectUri = ev[1]
                val target = Utils.friendlyUri(this, redirectUri, acc)
                if (acc.autoRedirect) {
                    redirect(ev[0], ua, redirectUri)
                    Toast.makeText(applicationContext,
                        String.format(getString(R.string.redirect_notice), target),
                        Toast.LENGTH_SHORT
                    ).show()
                } else {
                    with(MaterialAlertDialogBuilder(this, R.style.AlertDialogTheme)) {
                        setTitle(R.string.redirect_request)
                        setMessage(String.format(getString(R.string.redirect_request_query), target))
                        setPositiveButton(getString(R.string.yes)) { dialog, _ ->
                            redirect(ev[0], ua, redirectUri)
                            dialog.dismiss()
                        }
                        setNeutralButton(getString(R.string.no)) { dialog, _ ->
                            dialog.dismiss()
                        }
                        show()
                    }
                }
                showCall(ua)
            }
            "call established" -> {
                if (aor == aorSpinner.tag) {
                    dtmf.text = null
                    dtmf.hint = getString(R.string.dtmf)
                    showCall(ua)
                }
            }
            "call update" -> {
                showCall(ua)
            }
            "call video request" -> {
                val callp = params[1] as Long
                val dir = params[2] as Int
                val call = Call.ofCallp(callp)
                if (call == null) {
                    Log.w(TAG, "Video request call $callp not found")
                    return
                }
                if (!isFinishing && !alerting) {
                    with(MaterialAlertDialogBuilder(this, R.style.AlertDialogTheme)) {
                        setTitle(R.string.video_request)
                        val peerUri = Utils.friendlyUri(this@MainActivity, call.peerUri, acc)
                        val msg = when (dir) {
                            1 -> String.format(getString(R.string.allow_video_recv), peerUri)
                            2 -> String.format(getString(R.string.allow_video_send), peerUri)
                            3 -> String.format(getString(R.string.allow_video), peerUri)
                            else -> ""
                        }
                        setMessage(msg)
                        setPositiveButton(getString(R.string.yes)) { dialog, _ ->
                            call.videoRequest = dir
                            videoButton.performClick()
                            dialog.dismiss()
                            alerting = false
                        }
                        setNeutralButton(getString(R.string.no)) { dialog, _ ->
                            dialog.dismiss()
                            alerting = false
                        }
                        alerting = true
                        show()
                    }
                }
            }
            "call verify" -> {
                val callp = params[1] as Long
                val call = Call.ofCallp(callp)
                if (call == null) {
                    handleNextEvent("Call $callp to be verified is not found")
                    return
                }
                with(MaterialAlertDialogBuilder(this, R.style.AlertDialogTheme)) {
                    setTitle(R.string.verify)
                    setMessage(String.format(getString(R.string.verify_sas), ev[1]))
                    setPositiveButton(getString(R.string.yes)) { dialog, _ ->
                        call.security = if (Api.cmd_exec("zrtp_verify ${ev[2]}") != 0) {
                            Log.e(TAG, "Command 'zrtp_verify ${ev[2]}' failed")
                            R.drawable.locked_yellow
                        } else {
                            R.drawable.locked_green
                        }
                        call.zid = ev[2]
                        if (aor == aorSpinner.tag) {
                            securityButton.tag = call.security
                            securityButton.setImageResource(call.security)
                            setVideoSecurityButton(call.security)
                        }
                        dialog.dismiss()
                    }
                    setNeutralButton(getString(R.string.no)) { dialog, _ ->
                        call.security = R.drawable.locked_yellow
                        call.zid = ev[2]
                        if (aor == aorSpinner.tag) {
                            securityButton.tag = R.drawable.locked_yellow
                            securityButton.setImageResource(R.drawable.locked_yellow)
                            setVideoSecurityButton(R.drawable.locked_yellow)
                        }
                        dialog.dismiss()
                    }
                    show()
                }
            }
            "call verified", "call secure" -> {
                val callp = params[1] as Long
                val call = Call.ofCallp(callp)
                if (call == null) {
                    handleNextEvent("Call $callp that is verified is not found")
                    return
                }
                if (aor == aorSpinner.tag) {
                    securityButton.setImageResource(call.security)
                    securityButton.tag = call.security
                    setVideoSecurityButton(call.security)
                }
            }
            "call transfer", "transfer show" -> {
                val callp = params[1] as Long
                if (!BaresipService.isMainVisible) {
                    Log.d(TAG, "Reordering to front")
                    BaresipService.activities.clear()
                    BaresipService.serviceEvents.clear()
                    val i = Intent(applicationContext, MainActivity::class.java)
                    i.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
                    i.putExtra("action", event)
                    i.putExtra("callp", callp)
                    startActivity(i)
                    return
                }
                val call = Call.ofCallp(callp)!!
                val target = Utils.friendlyUri(this, ev[1], acc)
                with(MaterialAlertDialogBuilder(this, R.style.AlertDialogTheme)) {
                    setTitle(R.string.transfer_request)
                    setMessage(String.format(getString(R.string.transfer_request_query),
                            target))
                    setPositiveButton(getString(R.string.yes)) { dialog, _ ->
                        acceptTransfer(ua, call, ev[1])
                        dialog.dismiss()
                    }
                    setNeutralButton(getString(R.string.no)) { dialog, _ ->
                        if (call in Call.calls())
                            call.notifySipfrag(603, "Decline")
                        dialog.dismiss()
                    }
                    show()
                }
            }
            "transfer accept" -> {
                val callp = params[1] as Long
                val call = Call.ofCallp(callp)
                if (call in Call.calls())
                    Api.ua_hangup(uap, callp, 0, "")
                call(ua, ev[1], "voice")
                showCall(ua)
            }
            "transfer failed" -> {
                showCall(ua)
            }
            "call closed" -> {
                uaAdapter.notifyDataSetChanged()
                val call = ua.currentCall()
                if (call != null) {
                    call.resume()
                    startCallTimer(call)
                } else {
                    callTimer.stop()
                }
                if (aor == aorSpinner.tag) {
                    callUri.inputType = InputType.TYPE_CLASS_TEXT +
                            InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
                    dialpadButton.setImageResource(R.drawable.dialpad_off)
                    dialpadButton.tag = "off"
                    ua.account.resumeUri = ""
                    showCall(ua)
                    if (acc.missedCalls)
                        callsButton.setImageResource(R.drawable.calls_missed)
                }
                if (kgm.isDeviceLocked)
                    this.setShowWhenLocked(false)

                switchVideoLayout(false)
                //callVideoButton.requestFocus()

                BaresipService.isMicMuted = false
                BaresipService.isVideoMuted = false
            }
            "message", "message show", "message reply" -> {
                val i = Intent(applicationContext, ChatActivity::class.java)
                i.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
                val b = Bundle()
                b.putString("aor", aor)
                b.putString("peer", params[1] as String)
                b.putBoolean("focus", ev[0] == "message reply")
                i.putExtras(b)
                chatRequests.launch(i)
            }
            "mwi notify" -> {
                val lines = ev[1].split("\n")
                for (line in lines) {
                    if (line.startsWith("Voice-Message:")) {
                        val counts = (line.split(" ")[1]).split("/")
                        acc.vmNew = counts[0].toInt()
                        acc.vmOld = counts[1].toInt()
                        break
                    }
                }
                if (aor == aorSpinner.tag) {
                    if (acc.vmNew > 0)
                        voicemailButton.setImageResource(R.drawable.voicemail_new)
                    else
                        voicemailButton.setImageResource(R.drawable.voicemail)
                }
            }
            else -> Log.e(TAG, "Unknown event '${ev[0]}'")
        }

        handleNextEvent()
    }

    private fun redirect(event: String, ua: UserAgent, redirectUri: String) {
        if (ua.account.aor != aorSpinner.tag)
            spinToAor(ua.account.aor)
        callUri.setText(redirectUri)
        if (event == "call redirect")
            callButton.performClick()
        else
            callVideoButton.performClick()
    }

    private fun reStart() {
        Log.d(TAG, "Trigger restart")
        val pm = applicationContext.packageManager
        val intent = pm.getLaunchIntentForPackage(this.packageName)
        this.startActivity(intent)
        exitProcess(0)
    }

    override fun onPrepareOptionsMenu(menu: Menu): Boolean {
        if(Utils.propertyGet("sys.rito.debug") == "1") {
            if (Build.VERSION.SDK_INT >= 29)
                menu.findItem(R.id.logcat).setVisible(true)
        }
        return super.onPrepareOptionsMenu(menu)
    }

    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        if(Utils.propertyGet("sys.rito.debug") == "1") {
            menuInflater.inflate(R.menu.main_menu, menu)
        }

        // main layout 툴바 상단 아이콘 숨김 - by ritoseo
/*
        menuInflater.inflate(R.menu.rec_icon, menu)
        recIcon = menu.findItem(R.id.recIcon)
        if (BaresipService.isRecOn)
            recIcon!!.setIcon(R.drawable.rec_on)
        else
            recIcon!!.setIcon(R.drawable.rec_off)

        menuInflater.inflate(R.menu.mic_icon, menu)
        micIcon = menu.findItem(R.id.micIcon)
        if (BaresipService.isMicMuted)
            micIcon!!.setIcon(R.drawable.mic_off)
        else
            micIcon!!.setIcon(R.drawable.mic_on)

        menuInflater.inflate(R.menu.speaker_icon, menu)
        speakerIcon = menu.findItem(R.id.speakerIcon)
        if (Utils.isSpeakerPhoneOn(am))
            speakerIcon!!.setIcon(R.drawable.speaker_on)
        else
            speakerIcon!!.setIcon(R.drawable.speaker_off)

 */

        return super.onCreateOptionsMenu(menu)
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {

        when (item.itemId) {

            R.id.recIcon -> {
                if (Call.call("connected") == null) {
                    BaresipService.isRecOn = !BaresipService.isRecOn
                    if (BaresipService.isRecOn) {
                        item.setIcon(R.drawable.rec_on)
                        Api.module_load("sndfile")
                    } else {
                        item.setIcon(R.drawable.rec_off)
                        Api.module_unload("sndfile")
                    }
                } else {
                    Toast.makeText(
                        applicationContext,
                        R.string.rec_in_call,
                        Toast.LENGTH_SHORT
                    ).show()
                }
            }

            R.id.micIcon -> {
                if (Call.call("connected") != null) {
                    BaresipService.isMicMuted = !BaresipService.isMicMuted
                    if (BaresipService.isMicMuted) {
                        item.setIcon(R.drawable.mic_off)
                        Api.calls_mute(true)
                    } else {
                        item.setIcon(R.drawable.mic_on)
                        Api.calls_mute(false)
                    }
                }
            }

            R.id.speakerIcon -> {
                if (Build.VERSION.SDK_INT >= 31)
                    Log.d(TAG, "Toggling speakerphone when dev/mode is " +
                            "${am.communicationDevice!!.type}/${am.mode}")
                Utils.toggleSpeakerPhone(ContextCompat.getMainExecutor(this), am)
                setSpeakerButtonAndIcon()
            }

            R.id.config -> {
                configRequest.launch(Intent(this, ConfigActivity::class.java))
            }

            R.id.accounts -> {
                val i = Intent(this, AccountsActivity::class.java)
                val b = Bundle()
                b.putString("aor", aorSpinner.tag.toString())
                i.putExtras(b)
                accountsRequest.launch(i)
            }

            R.id.backup -> {
                when {
                    Build.VERSION.SDK_INT >= 29 -> pickupFileFromDownloads("backup")
                    ContextCompat.checkSelfPermission(
                        this,
                        WRITE_EXTERNAL_STORAGE
                    ) == PackageManager.PERMISSION_GRANTED -> {
                        Log.d(TAG, "Write External Storage permission granted")
                        val path = Utils.downloadsPath("baresip+.bs")
                        downloadsOutputUri = File(path).toUri()
                        askPassword(getString(R.string.encrypt_password))
                    }
                    ActivityCompat.shouldShowRequestPermissionRationale(
                        this,
                        WRITE_EXTERNAL_STORAGE) -> {
                        defaultLayout.showSnackBar(
                            binding.root,
                            getString(R.string.no_backup),
                            Snackbar.LENGTH_INDEFINITE,
                            getString(R.string.ok)
                        ) {
                            requestPermissionLauncher.launch(WRITE_EXTERNAL_STORAGE)
                        }
                    }
                    else -> {
                        requestPermissionLauncher.launch(WRITE_EXTERNAL_STORAGE)
                    }
                }
            }

            R.id.restore -> {
                when {
                    Build.VERSION.SDK_INT >= 29 ->
                        pickupFileFromDownloads("restore")
                    ContextCompat.checkSelfPermission(
                        this,
                        READ_EXTERNAL_STORAGE
                    ) == PackageManager.PERMISSION_GRANTED -> {
                        Log.d(TAG, "Read External Storage permission granted")
                        val path = Utils.downloadsPath("baresip+.bs")
                        downloadsInputUri = File(path).toUri()
                        askPassword(getString(R.string.decrypt_password))
                    }
                    ActivityCompat.shouldShowRequestPermissionRationale(
                        this,
                        READ_EXTERNAL_STORAGE) -> {
                        defaultLayout.showSnackBar(
                            binding.root,
                            getString(R.string.no_restore),
                            Snackbar.LENGTH_INDEFINITE,
                            getString(R.string.ok)
                        ) {
                            requestPermissionLauncher.launch(READ_EXTERNAL_STORAGE)
                        }
                    }
                    else -> {
                        requestPermissionLauncher.launch(READ_EXTERNAL_STORAGE)
                    }
                }
            }

            R.id.logcat -> {
                if (Build.VERSION.SDK_INT >= 29)
                    pickupFileFromDownloads("logcat")
            }

            R.id.about -> {
                startActivity(Intent(this, AboutActivity::class.java))
            }

            R.id.restart, R.id.quit -> {
                quitRestart(item.itemId == R.id.restart)
            }
        }

        return true
    }

    private fun quitRestart(reStart: Boolean) {
        Log.d(TAG, "quitRestart Restart = $reStart")
        window.setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
                WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
        if (BaresipService.isServiceRunning) {
            restart = reStart
            baresipService.action = "Stop"
            startService(baresipService)
        } else {
            finishAndRemoveTask()
            if (reStart)
                reStart()
            else
                exitProcess(0)
        }
    }

    private fun makeTransfer(ua: UserAgent) {

        val layout = LayoutInflater.from(this)
                .inflate(R.layout.call_transfer_dialog, findViewById(android.R.id.content), false)
        val blind: CheckBox = layout.findViewById(R.id.blind)
        val attended: CheckBox = layout.findViewById(R.id.attended)

        val transferUri: AutoCompleteTextView = layout.findViewById(R.id.transferUri)
        transferUri.setAdapter(ArrayAdapter(this, android.R.layout.select_dialog_item,
                Contact.contactNames()))
        transferUri.threshold = 2
        transferUri.requestFocus()

        val builder = MaterialAlertDialogBuilder(this, R.style.AlertDialogTheme)
        with(builder) {
            setView(layout)
            setPositiveButton(R.string.transfer) { dialog, _ ->
                imm.hideSoftInputFromWindow(transferUri.windowToken, 0)
                dialog.dismiss()
                var uriText = transferUri.text.toString().trim()
                if (uriText.isNotEmpty()) {
                    val uris = Contact.contactUris(uriText)
                    if (uris.size > 1) {
                        val destinationBuilder = MaterialAlertDialogBuilder(
                            this@MainActivity,
                            R.style.AlertDialogTheme
                        )
                        with(destinationBuilder) {
                            setTitle(R.string.choose_destination_uri)
                            setItems(uris.toTypedArray()) { _, which ->
                                uriText = uris[which]
                                transfer(
                                    ua,
                                    if (Utils.isTelNumber(uriText)) "tel:$uriText" else uriText,
                                    attended.isChecked
                                )
                            }
                            setNeutralButton(getString(R.string.cancel)) { _: DialogInterface, _: Int -> }
                            show()
                        }
                    } else {
                        if (uris.size == 1)
                            uriText = uris[0]
                    }
                    transfer(ua, if (Utils.isTelNumber(uriText)) "tel:$uriText" else uriText,
                        attended.isChecked)
                }
            }
            setNeutralButton(android.R.string.cancel) { dialog, _ ->
                imm.hideSoftInputFromWindow(transferUri.windowToken, 0)
                dialog.cancel()
            }
        }
        val alertDialog = builder.create()

        val call = ua.currentCall() ?: return
        val blindOrAttended: RelativeLayout = layout.findViewById(R.id.blindOrAttended)
        if (call.replaces()) {
            blind.setOnClickListener {
                if (blind.isChecked) {
                    attended.isChecked = false
                    alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).text = getString(R.string.transfer)
                }
            }
            attended.setOnClickListener {
                if (attended.isChecked) {
                    blind.isChecked = false
                    alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).text = getString(R.string.call)
                }
            }
            blindOrAttended.visibility = View.VISIBLE
        } else {
            blindOrAttended.visibility = View.GONE
        }

        // This needs to be done after dialog has been created and before it is shown
        alertDialog.window!!.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE)
        alertDialog.show()
    }

    @RequiresApi(29)
    private fun pickupFileFromDownloads(action: String) {
        when (action) {
            "backup" -> {
                backupRequest.launch(Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
                    addCategory(Intent.CATEGORY_OPENABLE)
                    type = "application/octet-stream"
                    putExtra(Intent.EXTRA_TITLE, "baresip+_" +
                            SimpleDateFormat("yyyy_MM_dd_HH_mm_ss", Locale.getDefault()).format(Date()))
                    putExtra(DocumentsContract.EXTRA_INITIAL_URI, MediaStore.Downloads.EXTERNAL_CONTENT_URI)
                })
            }
            "restore" -> {
                restoreRequest.launch(Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
                    addCategory(Intent.CATEGORY_OPENABLE)
                    type = "application/octet-stream"
                    putExtra(DocumentsContract.EXTRA_INITIAL_URI, MediaStore.Downloads.EXTERNAL_CONTENT_URI)
                })
            }
            "logcat" -> {
                logcatRequest.launch(Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
                    addCategory(Intent.CATEGORY_OPENABLE)
                    type = "text/plain"
                    putExtra(Intent.EXTRA_TITLE, "baresip_logcat_" +
                            SimpleDateFormat("yyyy_MM_dd_HH_mm_ss", Locale.getDefault()).format(Date()))
                    putExtra(DocumentsContract.EXTRA_INITIAL_URI, MediaStore.Downloads.EXTERNAL_CONTENT_URI)
                })
            }
        }
    }

    private fun transfer(ua: UserAgent, uriText: String, attended: Boolean) {
        val uri = if (Utils.isTelUri(uriText))
            Utils.telToSip(uriText, ua.account)
        else
            Utils.uriComplete(uriText, ua.account.aor)
        if (!Utils.checkUri(uri)) {
            Utils.alertView(this@MainActivity, getString(R.string.notice),
                String.format(getString(R.string.invalid_sip_or_tel_uri), uri))
        } else {
            val call = ua.currentCall()
            if (call != null) {
                if (attended) {
                    if (call.hold()) {
                        call.referTo = uri
                        call(ua, uri, "voice", call)
                    }
                } else {
                    if (!call.transfer(uri)) {
                        Utils.alertView(this@MainActivity, getString(R.string.notice),
                            String.format(getString(R.string.transfer_failed)))
                    }
                }
                showCall(ua)
            }
        }
    }
    private fun askPassword(title: String, ua: UserAgent? = null) {
        val layout = LayoutInflater.from(this)
                .inflate(R.layout.password_dialog, findViewById(android.R.id.content),
                        false)
        val titleView: TextView = layout.findViewById(R.id.title)
        titleView.text = title
        if (ua != null) {
            val messageView: TextView = layout.findViewById(R.id.message)
            val message = getString(R.string.account) + " " + Utils.plainAor(activityAor)
            messageView.text = message
        }
        val input: EditText = layout.findViewById(R.id.password)
        input.requestFocus()
        with (MaterialAlertDialogBuilder(this, R.style.AlertDialogTheme)) {
            setView(layout)
            setPositiveButton(android.R.string.ok) { dialog, _ ->
                imm.hideSoftInputFromWindow(input.windowToken, 0)
                dialog.dismiss()
                var password = input.text.toString().trim()
                if (!Account.checkAuthPass(password)) {
                    Toast.makeText(
                        applicationContext,
                        String.format(getString(R.string.invalid_authentication_password), password),
                        Toast.LENGTH_SHORT
                    ).show()
                    password = ""
                }
                when (title) {
                    getString(R.string.encrypt_password) ->
                        if (password != "") backup(password)
                    getString(R.string.decrypt_password) ->
                        if (password != "") restore(password)
                    else ->
                        if (password == "") {
                            askPassword(title, ua!!)
                        } else {
                            Api.account_set_auth_pass(ua!!.account.accp, password)
                            ua.account.authPass =  Api.account_auth_pass(ua.account.accp)
                            BaresipService.aorPasswords[ua.account.aor] = ua.account.authPass
                            if (ua.account.regint == 0)
                                Api.ua_unregister(ua.uap)
                            else
                                Api.ua_register(ua.uap)
                        }
                }
            }
            setNeutralButton(android.R.string.cancel) { dialog, _ ->
                imm.hideSoftInputFromWindow(input.windowToken, 0)
                dialog.cancel()
            }
            val dialog = this.create()
            dialog.setCancelable(false)
            dialog.setCanceledOnTouchOutside(false)
            dialog.window!!.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE)
            dialog.show()
        }
    }

    private fun askPasswords(accounts: MutableList<String>) {
        if (accounts.isNotEmpty()) {
            val account = accounts.removeAt(0)
            val params = account.substringAfter(">")
            if ((Utils.paramValue(params, "auth_user") != "") &&
                    (Utils.paramValue(params, "auth_pass") == "")) {
                val aor = account.substringAfter("<").substringBefore(">")
                val layout = LayoutInflater.from(this)
                        .inflate(R.layout.password_dialog, findViewById(android.R.id.content),
                                false)
                val titleView: TextView = layout.findViewById(R.id.title)
                titleView.text = getString(R.string.authentication_password)
                val messageView: TextView = layout.findViewById(R.id.message)
                val message = getString(R.string.account) + " " + Utils.plainAor(aor)
                messageView.text = message
                val input: EditText = layout.findViewById(R.id.password)
                input.requestFocus()
                with (MaterialAlertDialogBuilder(this, R.style.AlertDialogTheme)) {
                    setView(layout)
                    setPositiveButton(android.R.string.ok) { dialog, _ ->
                        imm.hideSoftInputFromWindow(input.windowToken, 0)
                        dialog.dismiss()
                        val password = input.text.toString().trim()
                        if (!Account.checkAuthPass(password)) {
                            Toast.makeText(
                                applicationContext,
                                String.format(getString(R.string.invalid_authentication_password),
                                    password),
                                Toast.LENGTH_SHORT
                            ).show()
                            accounts.add(0, account)
                        } else {
                            BaresipService.aorPasswords[aor] = password
                        }
                        askPasswords(accounts)
                    }
                    setNeutralButton(android.R.string.cancel) { dialog, _ ->
                        imm.hideSoftInputFromWindow(input.windowToken, 0)
                        dialog.cancel()
                        BaresipService.aorPasswords[aor] = NO_AUTH_PASS
                        askPasswords(accounts)
                    }
                    val dialog = this.create()
                    dialog.setCancelable(false)
                    dialog.setCanceledOnTouchOutside(false)
                    dialog.window!!.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE)
                    dialog.show()
                }
            } else {
                askPasswords(accounts)
            }
        } else {
            requestPermissionsLauncher.launch(permissions)
        }
    }

    private fun startBaresip() {
        if (!BaresipService.isStartReceived) {
            baresipService.action = "Start"
            startService(baresipService)
            if (atStartup)
                moveTaskToBack(true)
        }

    }

    private fun backup(password: String) {
        val files = arrayListOf("accounts", "config", "contacts", "call_history",
            "messages", "gzrtp.zid", "cert.pem", "ca_cert", "ca_certs.crt")
        File(BaresipService.filesPath).walk().forEach {
            if (it.name.endsWith(".png"))
                files.add(it.name)
        }
        val zipFile = getString(R.string.app_name_plus) + ".zip"
        val zipFilePath = BaresipService.filesPath + "/$zipFile"
        if (!Utils.zip(files, zipFile)) {
            Log.w(TAG, "Failed to write zip file '$zipFile'")
            Utils.alertView(this, getString(R.string.error),
                    String.format(getString(R.string.backup_failed),
                            Utils.fileNameOfUri(applicationContext, downloadsOutputUri!!)))
            return
        }
        val content = Utils.getFileContents(zipFilePath)
        if (content == null) {
            Log.w(TAG, "Failed to read zip file '$zipFile'")
            Utils.alertView(this, getString(R.string.error),
                    String.format(getString(R.string.backup_failed),
                            Utils.fileNameOfUri(applicationContext, downloadsOutputUri!!)))
            return
        }
        if (!Utils.encryptToUri(applicationContext, downloadsOutputUri!!, content, password)) {
            Utils.alertView(this, getString(R.string.error),
                    String.format(getString(R.string.backup_failed),
                            Utils.fileNameOfUri(applicationContext, downloadsOutputUri!!)))
            return
        }
        Utils.alertView(this, getString(R.string.info),
                String.format(getString(R.string.backed_up),
                        Utils.fileNameOfUri(applicationContext, downloadsOutputUri!!)))
        Utils.deleteFile(File(zipFilePath))
    }

    private fun restore(password: String) {
        val zipFile = getString(R.string.app_name_plus) + ".zip"
        val zipFilePath = BaresipService.filesPath + "/$zipFile"
        val zipData = Utils.decryptFromUri(applicationContext, downloadsInputUri!!, password)
        if (zipData == null) {
            Utils.alertView(this, getString(R.string.error),
                    String.format(getString(R.string.restore_failed),
                            Utils.fileNameOfUri(applicationContext, downloadsInputUri!!)))
            return
        }
        if (!Utils.putFileContents(zipFilePath, zipData)) {
            Log.w(TAG, "Failed to write zip file '$zipFile'")
            Utils.alertView(this, getString(R.string.error),
                    String.format(getString(R.string.restore_failed),
                    Utils.fileNameOfUri(applicationContext, downloadsInputUri!!)))
            return
        }
        if (!Utils.unZip(zipFilePath)) {
            Log.w(TAG, "Failed to unzip file '$zipFile'")
            Utils.alertView(this, getString(R.string.error),
                    String.format(getString(R.string.restore_unzip_failed), "baresip+", "47.0.0"))
            return
        }
        Utils.deleteFile(File(zipFilePath))

        File("${BaresipService.filesPath}/recordings").walk().forEach {
            if (it.name.startsWith("dump"))
                Utils.deleteFile(it)
        }

        CallHistoryNew.restore()
        for (h in BaresipService.callHistory)
            h.recording = arrayOf("", "")
        CallHistoryNew.save()

        with(MaterialAlertDialogBuilder(this, R.style.AlertDialogTheme)) {
            setTitle(getString(R.string.info))
            setMessage(getString(R.string.restored))
            setPositiveButton(getText(R.string.restart)) { dialog, _ ->
                quitRestart(true)
                dialog.dismiss()
            }
            setNeutralButton(getText(R.string.cancel)) { dialog, _ ->
                dialog.dismiss()
            }
            show()
        }
    }

    private fun spinToAor(aor: String) {
        for (accountIndex in BaresipService.uas.indices)
            if (BaresipService.uas[accountIndex].account.aor == aor) {
                aorSpinner.setSelection(accountIndex)
                aorSpinner.tag = aor
                return
            }
        if (BaresipService.uas.isNotEmpty()) {
            aorSpinner.setSelection(0)
            aorSpinner.tag = BaresipService.uas[0].account.aor
        } else {
            aorSpinner.setSelection(-1)
            aorSpinner.tag = ""
        }
    }

    private fun makeCall(kind: String, lookForContact: Boolean = true) {
        callUri.setAdapter(null)
        val ua = BaresipService.uas[aorSpinner.selectedItemPosition]
        val aor = ua.account.aor
        if (!Call.inCall()) {
            var uriText = callUri.text.toString().trim()
            if (uriText.isNotEmpty()) {
                if (lookForContact) {
                    val uris = Contact.contactUris(uriText)
                    if (uris.size == 1)
                        uriText = uris[0]
                    else if (uris.size > 1) {
                        val builder = MaterialAlertDialogBuilder(this, R.style.AlertDialogTheme)
                        with(builder) {
                            setTitle(R.string.choose_destination_uri)
                            setItems(uris.toTypedArray()) { _, which ->
                                callUri.setText(uris[which])
                                makeCall(kind, false)
                            }
                            setNeutralButton(getString(R.string.cancel)) { _: DialogInterface, _: Int -> }
                            show()
                        }
                        return
                    }
                }
                if (Utils.isTelNumber(uriText))
                    uriText = "tel:$uriText"
                val uri = if (Utils.isTelUri(uriText)) {
                    if (ua.account.telProvider == "") {
                        Utils.alertView(this, getString(R.string.notice),
                                String.format(getString(R.string.no_telephony_provider), aor))
                        return
                    }
                    Utils.telToSip(uriText, ua.account)
                } else {
                    Utils.uriComplete(uriText, aor)
                }
                if (!Utils.checkUri(uri)) {
                    Utils.alertView(this, getString(R.string.notice),
                            String.format(getString(R.string.invalid_sip_or_tel_uri), uri))
                } else if (!BaresipService.requestAudioFocus(applicationContext)) {
                    Toast.makeText(applicationContext, R.string.audio_focus_denied,
                            Toast.LENGTH_SHORT).show()
                } else {
                    callUri.isFocusable = false
                    uaAdapter.notifyDataSetChanged()
                    callButton.visibility = View.INVISIBLE
                    callButton.isEnabled = false
                    callVideoButton.visibility = View.INVISIBLE
                    callVideoButton.isEnabled = false
                    switchVideoLayout(true)
                    hangupButton.visibility = View.VISIBLE
                    hangupButton.isEnabled = true
                    if (Build.VERSION.SDK_INT < 31) {
                        Log.d(TAG, "Setting audio mode to MODE_IN_COMMUNICATION")
                        am.mode = AudioManager.MODE_IN_COMMUNICATION
                        runCall(ua, uri, kind)
                    } else {
                        if (am.mode == AudioManager.MODE_IN_COMMUNICATION) {
                            runCall(ua, uri, kind)
                        } else {
                            audioModeChangedListener = AudioManager.OnModeChangedListener { mode ->
                                if (mode == AudioManager.MODE_IN_COMMUNICATION) {
                                    Log.d(TAG, "Audio mode changed to MODE_IN_COMMUNICATION using " +
                                            "device ${am.communicationDevice!!.type}")
                                    if (audioModeChangedListener != null) {
                                        am.removeOnModeChangedListener(audioModeChangedListener!!)
                                        audioModeChangedListener = null
                                    }
                                    runCall(ua, uri, kind)
                                } else {
                                    Log.d(TAG, "Audio mode changed to mode ${am.mode} using " +
                                            "device ${am.communicationDevice!!.type}")
                                }
                            }
                            am.addOnModeChangedListener(mainExecutor, audioModeChangedListener!!)
                            Log.d(TAG, "Setting audio mode to MODE_IN_COMMUNICATION")
                            am.mode = AudioManager.MODE_IN_COMMUNICATION
                        }
                    }
                }
            } else {
                val latestPeerUri = CallHistoryNew.aorLatestPeerUri(aor)
                if (latestPeerUri != null)
                    callUri.setText(Utils.friendlyUri(this, latestPeerUri, ua.account))
            }
        }
    }

    private fun call(ua: UserAgent, uri: String, kind: String, onHoldCall: Call? = null): Boolean {
        if (ua.account.aor != aorSpinner.tag)
            spinToAor(ua.account.aor)
        val videoDir = when {
            kind == "voice" -> Api.SDP_INACTIVE
            Utils.isCameraAvailable(this) -> Api.SDP_SENDRECV
            else -> Api.SDP_RECVONLY
        }
        val callp = ua.callAlloc(0L, Api.VIDMODE_ON)
        return if (callp != 0L) {
            Log.d(TAG, "Adding outgoing $kind call ${ua.uap}/$callp/$uri")
            val call = Call(callp, ua, uri, "out", "outgoing", Utils.dtmfWatcher(callp))
            call.onHoldCall = onHoldCall
            call.setMediaDirection(Api.SDP_SENDRECV, videoDir)
            call.add()
            if (onHoldCall != null)
                onHoldCall.newCall = call
            if (call.connect(uri)) {
                showCall(ua)
                true
            } else {
                Log.w(TAG, "call_connect $callp failed")
                if (onHoldCall != null)
                    onHoldCall.newCall = null
                call.remove()
                call.destroy()
                showCall(ua)
                false
            }
        } else {
            Log.w(TAG, "callAlloc for ${ua.uap} to $uri failed")
            false
        }
    }

    private fun acceptTransfer(ua: UserAgent, call: Call, uri: String) {
        val newCallp = ua.callAlloc(call.callp, Api.VIDMODE_OFF)
        if (newCallp != 0L) {
            Log.d(TAG, "Adding outgoing call ${ua.uap}/$newCallp/$uri")
            val newCall = Call(newCallp, ua, uri, "out", "transferring",
                    Utils.dtmfWatcher(newCallp))
            newCall.add()
            if (newCall.connect(uri)) {
                if (ua.account.aor != aorSpinner.tag)
                    spinToAor(ua.account.aor)
                showCall(ua)
            } else {
                Log.w(TAG, "call_connect $newCallp failed")
                call.notifySipfrag(500, "Call Error")
            }
        } else {
            Log.w(TAG, "callAlloc for ua ${ua.uap} call ${call.callp} transfer failed")
            call.notifySipfrag(500, "Call Error")
        }
    }

    private fun setVideoSecurityButton(security: Int) {
        when (security) {
            R.drawable.unlocked -> {
                videoSecurityButton.setImageResource(R.drawable.unlocked_video)
            }
            R.drawable.locked_yellow -> {
                videoSecurityButton.setImageResource(R.drawable.locked_video_yellow)
            }
            R.drawable.locked_green -> {
                videoSecurityButton.setImageResource(R.drawable.locked_video_green)
            }
        }
    }

    private fun runCall(ua: UserAgent, uri: String, kind: String) {
        callRunnable = Runnable {
            callRunnable = null
            if (!call(ua, uri, kind)) {
                BaresipService.abandonAudioFocus(applicationContext)
                if(BaresipService.isSupportAudioCall) {
                    callButton.visibility = View.VISIBLE
                    callButton.isEnabled = true
                }
                //callVideoButton.visibility = View.VISIBLE
                callVideoButton.visibility = View.INVISIBLE     // modified by ritoseo
                callVideoButton.isEnabled = true
                hangupButton.visibility = View.INVISIBLE
                hangupButton.isEnabled = false
            }
        }
        callHandler.postDelayed(callRunnable!!, BaresipService.audioDelay)
    }

    private fun showCall(ua: UserAgent, showCall: Call? = null) {
        val call = showCall ?: ua.currentCall()
        if (call == null) {
            swipeRefresh.isEnabled = true
            videoLayout.visibility = View.INVISIBLE
            defaultLayout.visibility = View.VISIBLE
            callTitle.text = getString(R.string.outgoing_call_to_dots)
            callTimer.visibility = View.INVISIBLE
            if (ua.account.resumeUri != "")
                callUri.setText(ua.account.resumeUri)
            else
                callUri.text.clear()
            callUri.hint = getString(R.string.callee)
            callUri.isFocusable = true
            callUri.isFocusableInTouchMode = true
            imm.hideSoftInputFromWindow(callUri.windowToken, 0)
            callUri.setAdapter(ArrayAdapter(this, android.R.layout.select_dialog_item,
                    Contact.contactNames()))
            securityButton.visibility = View.INVISIBLE
            diverter.visibility = View.GONE
            if(BaresipService.isSupportAudioCall) {
                callButton.visibility = View.VISIBLE
                callButton.isEnabled = true
            }

            //callVideoButton.visibility = View.VISIBLE
            callVideoButton.visibility = View.INVISIBLE         // modified by ritoseo
            callVideoButton.isEnabled = true
            hangupButton.visibility = View.INVISIBLE
            answerButton.visibility = View.INVISIBLE
            answerVideoButton.visibility = View.INVISIBLE
            rejectButton.visibility = View.INVISIBLE
            callControl.visibility = View.INVISIBLE
            dialpadButton.isEnabled = true
            videoButton.visibility = View.INVISIBLE
            if (BaresipService.isMicMuted) {
                BaresipService.isMicMuted = false
                if(micIcon != null) {
                    micIcon!!.setIcon(R.drawable.mic_on)
                }
            }
            onHoldNotice.visibility = View.GONE
        } else {

            swipeRefresh.isEnabled = false
            callUri.isFocusable = false
            when (call.status) {
                "outgoing", "transferring", "answered" -> {
                    callTitle.text = if (call.status == "answered")
                        getString(R.string.incoming_call_from_dots)
                    else
                        getString(R.string.outgoing_call_to_dots)
                    callTimer.visibility = View.INVISIBLE
                    callUri.setText(Utils.friendlyUri(this, call.peerUri, ua.account,
                            call.status == "answered"))
                    videoButton.visibility = View.INVISIBLE
                    securityButton.visibility = View.INVISIBLE
                    diverter.visibility = View.GONE
                    callButton.visibility = View.INVISIBLE
                    hangupButton.visibility = View.VISIBLE
                    hangupButton.isEnabled = true
                    answerButton.visibility = View.INVISIBLE
                    answerVideoButton.visibility = View.INVISIBLE
                    rejectButton.visibility = View.INVISIBLE
                    callControl.visibility = View.INVISIBLE
                    onHoldNotice.visibility = View.GONE
                    dialpadButton.isEnabled = false
                }
                "incoming" -> {
                    callTitle.text = getString(R.string.incoming_call_from_dots)
                    callTimer.visibility = View.INVISIBLE
                    val caller =  Utils.friendlyUri(this, call.peerUri, ua.account)
                    callUri.setText(caller)
                    callUri.setAdapter(null)
                    videoButton.visibility = View.INVISIBLE
                    securityButton.visibility = View.INVISIBLE
                    val uri = call.diverterUri()
                    if (uri != "") {
                        diverterUri.text = Utils.friendlyUri(this, uri, ua.account)
                        diverter.visibility = View.VISIBLE
                    } else {
                        diverter.visibility = View.GONE
                    }
                    callButton.visibility = View.INVISIBLE
                    callVideoButton.visibility = View.INVISIBLE
                    switchVideoLayout(true)
                    hangupButton.visibility = View.INVISIBLE
                    answerButton.visibility = View.VISIBLE
                    answerButton.isEnabled = true
                    if (call.hasVideo()) {
                        answerVideoButton.visibility = View.VISIBLE
                        answerVideoButton.isEnabled = true
                    } else {
                        answerVideoButton.visibility = View.INVISIBLE
                        answerVideoButton.isEnabled = false
                    }
                    rejectButton.visibility = View.VISIBLE
                    rejectButton.isEnabled = true
                    callControl.visibility = View.INVISIBLE
                    onHoldNotice.visibility = View.GONE
                    dialpadButton.isEnabled = false
                }
                "connected" -> {
                    if (call.videoEnabled()) {
                        if (defaultLayout.visibility == View.VISIBLE) {
                            defaultLayout.visibility = View.INVISIBLE
                            videoLayout.visibility = View.VISIBLE
                        }
                        videoOnHoldNotice.visibility = if (call.held) View.VISIBLE else View.GONE
                        if (ua.account.mediaEnc == "") {
                            videoSecurityButton.visibility = View.INVISIBLE
                        } else {
                            securityButton.tag = call.security
                            setVideoSecurityButton(call.security)
                            videoSecurityButton.visibility = View.INVISIBLE     // 암호화 상태 버튼 표시 안함 by ritoseo
                            //videoSecurityButton.visibility = View.VISIBLE
                        }
                        return
                    }
                    if (defaultLayout.visibility == View.INVISIBLE) {
                        videoLayout.visibility = View.INVISIBLE
                        defaultLayout.visibility = View.VISIBLE
                    }
                    callControl.post {
                        callControl.scrollTo(videoButton.left, videoButton.top)
                    }
                    if (call.referTo != "") {
                        callTitle.text = getString(R.string.transferring_call_to_dots)
                        callUri.setText(Utils.friendlyUri(this, call.referTo, ua.account))
                        transferButton.isEnabled = false
                    } else {
                        if (call.dir == "out") {
                            callTitle.text = getString(R.string.outgoing_call_to_dots)
                            callUri.setText(Utils.friendlyUri(this, call.peerUri, ua.account))
                        } else {
                            callTitle.text = getString(R.string.incoming_call_from_dots)
                            callUri.setText(Utils.friendlyUri(this, call.peerUri, ua.account))
                        }
                        transferButton.isEnabled = true
                    }
                    if (call.onHoldCall == null)
                        transferButton.setImageResource(R.drawable.call_transfer)
                    else
                        transferButton.setImageResource(R.drawable.call_transfer_execute)
                    startCallTimer(call)
                    callTimer.visibility = View.VISIBLE
                    videoButton.setImageResource(R.drawable.video_on)
                    videoButton.visibility = View.VISIBLE
                    videoButton.isClickable = true
                    if (ua.account.mediaEnc == "") {
                        securityButton.visibility = View.INVISIBLE
                    } else {
                        securityButton.tag = call.security
                        securityButton.setImageResource(call.security)
                        securityButton.visibility = View.VISIBLE
                    }
                    callButton.visibility = View.INVISIBLE
                    callVideoButton.visibility = View.INVISIBLE

                    switchVideoLayout(true)

                    hangupButton.visibility = View.VISIBLE
                    hangupButton.isEnabled = true
                    answerButton.visibility = View.INVISIBLE
                    answerVideoButton.visibility = View.INVISIBLE
                    rejectButton.visibility = View.INVISIBLE
                    /*
                    if (call.onhold) {
                        holdButton.setImageResource(R.drawable.resume)
                    } else {
                        holdButton.setImageResource(R.drawable.call_hold)
                    }
                    dialpadButton.setImageResource(R.drawable.dialpad_on)
                    dialpadButton.tag = "on"
                    dialpadButton.isEnabled = false
                    infoButton.isEnabled = true
                    callControl.visibility = View.VISIBLE
                    Handler(Looper.getMainLooper()).postDelayed({
                        onHoldNotice.visibility = if (call.held) View.VISIBLE else View.GONE
                    }, 100)

                    if (call.held) {
                        imm.hideSoftInputFromWindow(dtmf.windowToken, 0)
                        dtmf.isEnabled = false
                    } else {
                        dtmf.isEnabled = true
                        dtmf.requestFocus()
                        if (resources.configuration.orientation == ORIENTATION_PORTRAIT)
                            imm.showSoftInput(dtmf, InputMethodManager.SHOW_IMPLICIT)
                        if (dtmfWatcher != null) dtmf.removeTextChangedListener(dtmfWatcher)
                        dtmfWatcher = call.dtmfWatcher
                        dtmf.addTextChangedListener(dtmfWatcher)
                    }

                     */
                }
            }
        }
    }

    private fun updateIcons(acc: Account) {
        if (acc.missedCalls)
            callsButton.setImageResource(R.drawable.calls_missed)
        else
            callsButton.setImageResource(R.drawable.calls)
        if (acc.unreadMessages)
            messagesButton.setImageResource(R.drawable.messages_unread)
        else
            messagesButton.setImageResource(R.drawable.messages)
        if (acc.vmUri != "") {
            if (acc.vmNew > 0)
                voicemailButton.setImageResource(R.drawable.voicemail_new)
            else
                voicemailButton.setImageResource(R.drawable.voicemail)
            voicemailButton.visibility = View.VISIBLE
            voicemailButtonSpace.visibility = View.VISIBLE
        } else {
            voicemailButton.visibility = View.GONE
            voicemailButtonSpace.visibility =View.GONE
        }
    }

    private fun restoreActivities() {
        if (BaresipService.activities.isEmpty()) return
        Log.d(TAG, "Activity stack ${BaresipService.activities}")
        val activity = BaresipService.activities[0].split(",")
        BaresipService.activities.removeAt(0)
        when (activity[0]) {
            "main" -> {
                if (!Call.inCall() && (BaresipService.activities.size > 1))
                    restoreActivities()
            }
            "config" -> {
                configRequest.launch(Intent(this, ConfigActivity::class.java))
            }
            "audio" -> {
                startActivity(Intent(this, AudioActivity::class.java))
            }
            "accounts" -> {
                val i = Intent(this, AccountsActivity::class.java)
                val b = Bundle()
                b.putString("aor", activity[1])
                i.putExtras(b)
                accountsRequest.launch(i)
            }
            "account" -> {
                val i = Intent(this, AccountActivity::class.java)
                val b = Bundle()
                b.putString("aor", activity[1])
                i.putExtras(b)
                accountsRequest.launch(i)
            }
            "codecs" -> {
                val i = Intent(this, CodecsActivity::class.java)
                val b = Bundle()
                b.putString("aor", activity[1])
                b.putString("media", activity[2])
                i.putExtras(b)
                startActivity(i)
            }
            "about" -> {
                startActivity(Intent(this, AboutActivity::class.java))
            }
            "contacts" -> {
                val i = Intent(this, ContactsActivity::class.java)
                val b = Bundle()
                b.putString("aor", activity[1])
                i.putExtras(b)
                contactsRequest.launch(i)

            }
            "contact" -> {
                val i = Intent(this, ContactActivity::class.java)
                val b = Bundle()
                if (activity[1] == "true") {
                    b.putBoolean("new", true)
                    b.putString("uri", activity[2])
                } else {
                    b.putBoolean("new", false)
                    b.putInt("index", activity[2].toInt())
                }
                i.putExtras(b)
                startActivity(i)
            }
            "chats" -> {
                val i = Intent(this, ChatsActivity::class.java)
                val b = Bundle()
                b.putString("aor", activity[1])
                i.putExtras(b)
                chatRequests.launch(i)
            }
            "chat" -> {
                val i = Intent(this, ChatActivity::class.java)
                val b = Bundle()
                b.putString("aor", activity[1])
                b.putString("peer", activity[2])
                b.putBoolean("focus", activity[3] == "true")
                i.putExtras(b)
                chatRequests.launch(i)
            }
            "calls" -> {
                val i = Intent(this, CallsActivity::class.java)
                val b = Bundle()
                b.putString("aor", activity[1])
                i.putExtras(b)
                callsRequest.launch(i)
            }
            "call_details" -> {
                val i = Intent(this, CallDetailsActivity::class.java)
                val b = Bundle()
                b.putString("aor", activity[1])
                b.putString("peer", activity[2])
                b.putInt("position", activity[3].toInt())
                i.putExtras(b)
                callsRequest.launch(i)
            }
        }
        return
    }

    private fun saveCallUri() {
        if (BaresipService.uas.isNotEmpty() && aorSpinner.selectedItemPosition >= 0) {
            val ua = BaresipService.uas[aorSpinner.selectedItemPosition]
            if (ua.calls().isEmpty())
                ua.account.resumeUri = callUri.text.toString()
            else
                ua.account.resumeUri = ""
        }
    }

    private fun startCallTimer(call: Call) {
        callTimer.stop()
        callTimer.base = SystemClock.elapsedRealtime() - (call.duration() * 1000L)
        callTimer.start()
    }

    companion object {

        var accountRequest: ActivityResultLauncher<Intent>? = null
        var activityAor = ""

        lateinit var videoView: VideoView

    }

    init {
        if (!BaresipService.libraryLoaded) {
            Log.i(TAG, "Loading baresip library")
            System.loadLibrary("baresip")
            BaresipService.libraryLoaded = true
        }
    }

}
