3307 lines
136 KiB
Kotlin
3307 lines
136 KiB
Kotlin
|
|
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.content.res.Configuration.ORIENTATION_PORTRAIT
|
||
|
|
import android.hardware.display.DisplayManager
|
||
|
|
import android.media.AudioManager
|
||
|
|
import android.media.MediaActionSound
|
||
|
|
import android.net.Uri
|
||
|
|
import android.os.*
|
||
|
|
import android.provider.DocumentsContract
|
||
|
|
import android.provider.MediaStore
|
||
|
|
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.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.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 org.w3c.dom.Text
|
||
|
|
import java.io.File
|
||
|
|
import java.text.SimpleDateFormat
|
||
|
|
import java.time.LocalDateTime
|
||
|
|
import java.time.format.DateTimeFormatter
|
||
|
|
import java.util.*
|
||
|
|
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 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)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
@SuppressLint("ClickableViewAccessibility")
|
||
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||
|
|
|
||
|
|
// theme.applyStyle(R.style.OptOutEdgeToEdgeEnforcement,false)
|
||
|
|
|
||
|
|
super.onCreate(savedInstanceState)
|
||
|
|
|
||
|
|
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
|
||
|
|
}
|
||
|
|
|
||
|
|
// 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
|
||
|
|
|
||
|
|
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
|
||
|
|
|
||
|
|
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.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
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
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}")
|
||
|
|
if(BaresipService.connectedDisplayCount < 2) {
|
||
|
|
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 {
|
||
|
|
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) {
|
||
|
|
}
|
||
|
|
|
||
|
|
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
|
||
|
|
|
||
|
|
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) {
|
||
|
|
}
|
||
|
|
videoView.standbyView.bringToFront()
|
||
|
|
presentation?.show()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
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)
|
||
|
|
} else {
|
||
|
|
val useSecondScreenAsFar = false;
|
||
|
|
// DisplayManager 가져오기
|
||
|
|
val displayManager = getSystemService(DISPLAY_SERVICE) as DisplayManager
|
||
|
|
val displays = displayManager.displays
|
||
|
|
|
||
|
|
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(videoView.surfaceView)
|
||
|
|
} 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'")
|
||
|
|
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") {
|
||
|
|
videoButton.performClick()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
"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
|
||
|
|
//println("onKeyDown : ${keyCode}")
|
||
|
|
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
|
||
|
|
}
|
||
|
|
BTN_MISC -> {
|
||
|
|
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()
|
||
|
|
}
|
||
|
|
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()
|
||
|
|
}
|
||
|
|
"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.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.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
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|