- In Android 10+ use picker to choose TLS Certificate and TLS CA Files

- Ask confirmation to reset settings to factory defaults
- Replaced downloadsPath companion variable with Utils function call
- Fixed several Kotlin lint warnings
This commit is contained in:
Juha Heinanen
2021-05-16 11:45:56 +03:00
parent 1177618d42
commit 6e8190ba01
9 changed files with 270 additions and 115 deletions

View File

@ -65,12 +65,10 @@ class Account(val accp: String) {
fun print() : String {
var res: String
if (displayName != "")
res = "\"${displayName}\" "
var res = if (displayName != "")
"\"${displayName}\" "
else
res = ""
""
res = "$res<$luri>"
@ -112,10 +110,10 @@ class Account(val accp: String) {
if (preferIPv6Media) res += ";mediaaf=ipv6"
if (vmUri == "")
res = "$res;mwi=no"
res = if (vmUri == "")
"$res;mwi=no"
else
res = "$res;mwi=yes;vm_uri=\"$vmUri\""
"$res;mwi=yes;vm_uri=\"$vmUri\""
if (answerMode == Api.ANSWERMODE_AUTO)
res += ";answermode=auto"
@ -134,29 +132,35 @@ class Account(val accp: String) {
}
fun vmMessages(cxt: Context) : String {
var new = ""
var old = ""
if (vmNew > 0)
val new = if (vmNew > 0) {
if (vmNew == 1)
new = cxt.getString(R.string.one_new_message)
cxt.getString(R.string.one_new_message)
else
new = "$vmNew ${cxt.getString(R.string.new_messages)}"
if (vmOld > 0)
"$vmNew ${cxt.getString(R.string.new_messages)}"
} else
""
val old = if (vmOld > 0) {
if (vmOld == 1)
old = cxt.getString(R.string.one_old_message)
cxt.getString(R.string.one_old_message)
else
old = "$vmOld ${cxt.getString(R.string.old_messages)}"
"$vmOld ${cxt.getString(R.string.old_messages)}"
} else
""
var msg = cxt.getString(R.string.you_have)
if (new != "") {
msg = "$msg $new"
if (old != "") msg = "$msg ${cxt.getString(R.string.and)} $old"
} else {
if (old != "")
msg = "$msg $old"
msg = if (old != "")
"$msg $old"
else
msg = cxt.getString(R.string.no_messages)
cxt.getString(R.string.no_messages)
}
return "$msg."
return "$msg."
}
fun host() : String {
@ -166,7 +170,7 @@ class Account(val accp: String) {
private fun removeAudioCodecsStartingWith(prefix: String) {
val newCodecs = ArrayList<String>()
for (acSpec in audioCodec)
if (!acSpec.toLowerCase(Locale.ROOT).startsWith(prefix)) newCodecs.add(acSpec)
if (!acSpec.lowercase(Locale.ROOT).startsWith(prefix)) newCodecs.add(acSpec)
audioCodec = newCodecs
}

View File

@ -185,10 +185,8 @@ class AccountsActivity : AppCompatActivity() {
fun saveAccounts() {
var accounts = ""
var count = 0
for (a in Account.accounts()) {
accounts = accounts + a.print() + "\n"
count++
}
Utils.putFileContents(BaresipService.filesPath + "/accounts", accounts.toByteArray())
// Log.d(TAG, "Saved accounts '${accounts}' to '${BaresipService.filesPath}/accounts'")
@ -196,7 +194,7 @@ class AccountsActivity : AppCompatActivity() {
fun noAccounts(): Boolean {
val contents = Utils.getFileContents(BaresipService.filesPath + "/accounts")
return contents == null || contents.size == 0
return contents == null || contents.isEmpty()
}
fun setAuthPass(ua: UserAgent, ap: String) {

View File

@ -66,7 +66,7 @@ class AudioActivity : AppCompatActivity() {
cb.layoutParams = cbParams
cb.gravity = Gravity.END
cb.isChecked = Config.variable("module $module.so").size > 0
oldAudioModules.put(module, cb.isChecked)
oldAudioModules[module] = cb.isChecked
rl.addView(cb)
audioModulesList.addView(rl)
}

View File

@ -63,7 +63,6 @@ class BaresipService: Service() {
intent.setPackage("com.tutpro.baresip")
filesPath = filesDir.absolutePath
downloadsPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).path
am = getSystemService(Context.AUDIO_SERVICE) as AudioManager
@ -1193,7 +1192,6 @@ class BaresipService: Service() {
var dynDns = false
var netInterface = ""
var filesPath = ""
var downloadsPath = ""
var logLevel = 2
var sipTrace = false
var callActionUri = ""

View File

@ -1,23 +1,33 @@
package com.tutpro.baresip
import android.Manifest
import android.app.Activity
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import android.content.pm.PackageManager
import android.os.Build
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.widget.*
import android.widget.AdapterView
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatDelegate
import com.tutpro.baresip.Utils.copyInputStreamToFile
import com.tutpro.baresip.databinding.ActivityConfigBinding
import java.io.File
import java.io.FileInputStream
import java.util.*
class ConfigActivity : AppCompatActivity() {
private val READ_CERT_PERMISSION_CODE = 1
private val READ_CA_PERMISSION_CODE = 2
private val CERTIFICATE_CODE = 1
private val CA_CERTIFICATES_CODE = 2
private lateinit var binding: ActivityConfigBinding
private lateinit var autoStart: CheckBox
private lateinit var listenAddr: EditText
@ -78,6 +88,39 @@ class ConfigActivity : AppCompatActivity() {
oldCertificateFile = Config.variable("sip_certificate").isNotEmpty()
certificateFile.isChecked = oldCertificateFile
certificateFile.setOnCheckedChangeListener { _, isChecked ->
if (isChecked) {
if (Build.VERSION.SDK_INT < 29) {
if (!Utils.requestPermission(this,
Manifest.permission.READ_EXTERNAL_STORAGE,
READ_CERT_PERMISSION_CODE)) {
certificateFile.isChecked = false
return@setOnCheckedChangeListener
}
val downloadsPath = Utils.downloadsPath("cert.pem")
val content = Utils.getFileContents(downloadsPath)
if (content == null) {
Utils.alertView(this, getString(R.string.error),
getString(R.string.read_cert_error))
certificateFile.isChecked = false
return@setOnCheckedChangeListener
}
val filesPath = BaresipService.filesPath + "/cert.pem"
Utils.putFileContents(filesPath, content)
Config.removeVariable("sip_certificate")
Config.addLine("sip_certificate $filesPath")
save = true
restart = true
} else {
Utils.selectInputFile(this, CERTIFICATE_CODE)
}
} else {
Config.removeVariable("sip_certificate")
save = true
restart = true
}
}
verifyServer = binding.VerifyServer
val vsCv = Config.variable("sip_verify_server")
oldVerifyServer = if (vsCv.size == 0) "no" else vsCv[0]
@ -87,6 +130,39 @@ class ConfigActivity : AppCompatActivity() {
oldCAFile = Config.variable("sip_cafile").isNotEmpty()
caFile.isChecked = oldCAFile
caFile.setOnCheckedChangeListener { _, isChecked ->
if (isChecked) {
if (Build.VERSION.SDK_INT < 29) {
if (!Utils.requestPermission(this,
Manifest.permission.READ_EXTERNAL_STORAGE,
READ_CERT_PERMISSION_CODE)) {
caFile.isChecked = false
return@setOnCheckedChangeListener
}
val downloadsPath = Utils.downloadsPath("ca_certs.crt")
val content = Utils.getFileContents(downloadsPath)
if (content == null) {
Utils.alertView(this, getString(R.string.error),
getString(R.string.read_ca_certs_error))
caFile.isChecked = false
return@setOnCheckedChangeListener
}
val filesPath = BaresipService.filesPath + "/ca_certs.crt"
Utils.putFileContents(filesPath, content)
Config.removeVariable("sip_cafile")
Config.addLine("sip_cafile $filesPath")
save = true
restart = true
} else {
Utils.selectInputFile(this, CA_CERTIFICATES_CODE)
}
} else {
Config.removeVariable("sip_cafile")
save = true
restart = true
}
}
val callVolSpinner = binding.VolumeSpinner
val volKeys = arrayListOf("None", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10")
val volVals = arrayListOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
@ -114,10 +190,10 @@ class ConfigActivity : AppCompatActivity() {
debug = binding.Debug
val dbCv = Config.variable("log_level")
if (dbCv.size == 0)
oldLogLevel = "2"
oldLogLevel = if (dbCv.size == 0)
"2"
else
oldLogLevel = dbCv[0]
dbCv[0]
debug.isChecked = oldLogLevel == "0"
sipTrace = binding.SipTrace
@ -126,6 +202,29 @@ class ConfigActivity : AppCompatActivity() {
reset = binding.Reset
reset.isChecked = false
reset.setOnCheckedChangeListener { _, isChecked ->
if (isChecked) {
val titleView = View.inflate(this, R.layout.alert_title, null) as TextView
titleView.text = getString(R.string.confirmation)
with (AlertDialog.Builder(this@ConfigActivity)) {
setCustomTitle(titleView)
setMessage(getString(R.string.reset_config_alert))
setPositiveButton(getText(R.string.reset)) { dialog, _ ->
Config.reset(this@ConfigActivity)
save = false
restart = true
done()
dialog.dismiss()
}
setNegativeButton(getText(R.string.cancel)) { dialog, _ ->
reset.isChecked = false
dialog.dismiss()
}
show()
}
}
}
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
@ -170,7 +269,8 @@ class ConfigActivity : AppCompatActivity() {
restart = true
}
val dnsServers = addMissingPorts(dnsServers.text.toString().trim().toLowerCase())
val dnsServers = addMissingPorts(
dnsServers.text.toString().trim().lowercase(Locale.ROOT))
if (dnsServers != oldDnsServers) {
if (!checkDnsServers(dnsServers)) {
Utils.alertView(this, getString(R.string.notice),
@ -196,56 +296,6 @@ class ConfigActivity : AppCompatActivity() {
save = true
}
if (certificateFile.isChecked != oldCertificateFile) {
if (certificateFile.isChecked) {
if (!Utils.requestPermission(this,
android.Manifest.permission.READ_EXTERNAL_STORAGE,
READ_CERT_PERMISSION_CODE))
return false
val content = Utils.getFileContents(BaresipService.downloadsPath + "/cert.pem")
if (content == null) {
Utils.alertView(this, getString(R.string.error),
getString(R.string.read_cert_error))
certificateFile.isChecked = false
return false
}
Utils.putFileContents(BaresipService.filesPath + "/cert.pem", content)
Config.removeVariable("sip_certificate")
Config.addLine("sip_certificate ${BaresipService.filesPath}/cert.pem")
} else {
Config.removeVariable("sip_certificate")
}
save = true
restart = true
}
if (caFile.isChecked != oldCAFile) {
if (caFile.isChecked) {
if (!Utils.requestPermission(this,
android.Manifest.permission.READ_EXTERNAL_STORAGE,
READ_CA_PERMISSION_CODE)) {
caFile.isChecked = false
return false
}
val content = Utils.getFileContents(BaresipService.downloadsPath +
"/ca_certs.crt")
if (content == null) {
Utils.alertView(this, getString(R.string.error),
getString(R.string.read_ca_certs_error))
caFile.isChecked = false
return false
}
Utils.putFileContents(BaresipService.filesPath + "/ca_certs.crt",
content)
Config.removeVariable("sip_cafile")
Config.addLine("sip_cafile ${BaresipService.filesPath}/ca_certs.crt")
} else {
Config.removeVariable("sip_cafile")
}
save = true
restart = true
}
if (verifyServer.isChecked && !caFile.isChecked) {
Utils.alertView(this, getString(R.string.error),
getString(R.string.verify_server_error))
@ -285,19 +335,7 @@ class ConfigActivity : AppCompatActivity() {
BaresipService.sipTrace = sipTrace.isChecked
Api.uag_enable_sip_trace(sipTrace.isChecked)
if (reset.isChecked) {
Config.reset(this)
save = false
restart = true
}
if (save) Config.save()
BaresipService.activities.remove("config")
val intent = Intent(this, MainActivity::class.java)
if (restart) intent.putExtra("restart", true)
setResult(RESULT_OK, intent)
finish()
done()
}
@ -315,6 +353,74 @@ class ConfigActivity : AppCompatActivity() {
}
private fun done() {
if (save)
Config.save()
BaresipService.activities.remove("config")
val intent = Intent(this, MainActivity::class.java)
if (restart)
intent.putExtra("restart", true)
setResult(RESULT_OK, intent)
finish()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
CERTIFICATE_CODE -> {
if (resultCode == Activity.RESULT_OK)
data?.data?.also {
try {
val inputStream = applicationContext.contentResolver.openInputStream(it)
as FileInputStream
File(BaresipService.filesPath + "/cert.pem")
.copyInputStreamToFile(inputStream)
inputStream.close()
Config.removeVariable("sip_certificate")
Config.addLine("sip_certificate ${BaresipService.filesPath}/cert.pem")
save = true
restart = true
} catch (e: Error) {
Utils.alertView(this, getString(R.string.error),
getString(R.string.read_cert_error))
certificateFile.isChecked = false
}
}
else
certificateFile.isChecked = false
}
CA_CERTIFICATES_CODE -> {
if (resultCode == Activity.RESULT_OK)
data?.data?.also {
try {
val inputStream = applicationContext.contentResolver.openInputStream(it)
as FileInputStream
File(BaresipService.filesPath + "/ca_certs.crt")
.copyInputStreamToFile(inputStream)
inputStream.close()
Config.removeVariable("sip_cafile")
Config.addLine("sip_cafile ${BaresipService.filesPath}/ca_certs.crt")
save = true
restart = true
} catch (e: Error) {
Utils.alertView(this, getString(R.string.error),
getString(R.string.read_ca_certs_error))
caFile.isChecked = false
}
}
else
caFile.isChecked = false
}
}
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>,
grantResults: IntArray) {
@ -322,15 +428,15 @@ class ConfigActivity : AppCompatActivity() {
when (requestCode) {
READ_CERT_PERMISSION_CODE ->
if (grantResults.isNotEmpty() && (grantResults[0] ==
PackageManager.PERMISSION_GRANTED))
menu!!.performIdentifierAction(R.id.checkIcon, 0)
if (grantResults.isNotEmpty() &&
(grantResults[0] == PackageManager.PERMISSION_GRANTED))
menu!!.performIdentifierAction(R.id.checkIcon, 0)
else
certificateFile.isChecked = false
READ_CA_PERMISSION_CODE ->
if ((grantResults.size > 0) && (grantResults[0] ==
PackageManager.PERMISSION_GRANTED))
menu!!.performIdentifierAction(R.id.checkIcon, 0)
if (grantResults.isNotEmpty() &&
(grantResults[0] == PackageManager.PERMISSION_GRANTED))
menu!!.performIdentifierAction(R.id.checkIcon, 0)
else
caFile.isChecked = false
}
@ -400,7 +506,7 @@ class ConfigActivity : AppCompatActivity() {
}
private fun checkDnsServers(dnsServers: String): Boolean {
if (dnsServers.length == 0) return true
if (dnsServers.isEmpty()) return true
for (server in dnsServers.split(","))
if (!Utils.checkIpPort(server.trim())) return false
return true
@ -410,13 +516,13 @@ class ConfigActivity : AppCompatActivity() {
if (addressList == "") return ""
var result = ""
for (addr in addressList.split(","))
if (Utils.checkIpPort(addr)) {
result = "$result,$addr"
result = if (Utils.checkIpPort(addr)) {
"$result,$addr"
} else {
if (Utils.checkIpV4(addr))
result = "$result,$addr:53"
"$result,$addr:53"
else
result = "$result,[$addr]:53"
"$result,[$addr]:53"
}
return result.substring(1)
}

View File

@ -14,7 +14,6 @@ import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.CountDownTimer
import android.os.Environment
import android.provider.DocumentsContract
import android.provider.MediaStore
import android.text.InputType
@ -1086,11 +1085,14 @@ class MainActivity : AppCompatActivity() {
if (Build.VERSION.SDK_INT >= 29) {
pickupFileFromDownloads(BACKUP_CODE)
} else {
if (Utils.requestPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE,
BACKUP_PERMISSION_REQUEST_CODE)) {
val filePath = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DOWNLOADS).path + "/baresip/bs"
downloadsOutputStream = FileOutputStream(File(filePath))
if (Utils.requestPermission(
this,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
BACKUP_PERMISSION_REQUEST_CODE
)
) {
val path = Utils.downloadsPath("baresip.bs")
downloadsOutputStream = FileOutputStream(File(path))
askPassword(getString(R.string.encrypt_password))
}
}
@ -1240,7 +1242,7 @@ class MainActivity : AppCompatActivity() {
if (Build.VERSION.SDK_INT >= 29) {
pickupFileFromDownloads(RESTORE_CODE)
} else {
val path = BaresipService.downloadsPath + "/baresip.bs"
val path = Utils.downloadsPath("baresip.bs")
downloadsInputStream = FileInputStream(File(path))
askPassword(getString(R.string.decrypt_password))
}

View File

@ -13,14 +13,18 @@ import android.net.LinkAddress
import android.net.LinkProperties
import android.net.Uri
import android.os.Bundle
import android.os.Environment
import android.provider.DocumentsContract
import android.provider.MediaStore
import android.provider.OpenableColumns
import android.text.Editable
import android.text.TextWatcher
import android.view.View
import android.widget.TextView
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AlertDialog
import androidx.core.app.ActivityCompat
import androidx.core.content.ContentResolverCompat.query
import androidx.core.app.ActivityCompat.startActivityForResult
import androidx.core.content.ContextCompat
import java.io.*
@ -54,7 +58,7 @@ object Utils {
fun removeLinesStartingWithString(lines: String, string: String): String {
var result = ""
for (line in lines.split("\n"))
if (!line.startsWith(string) && (line.length > 0)) result += line + "\n"
if (!line.startsWith(string) && (line.isNotEmpty())) result += line + "\n"
return result
}
@ -445,6 +449,44 @@ object Utils {
return true
}
fun File.copyInputStreamToFile(inputStream: InputStream): Boolean {
try {
this.outputStream().use { fileOut ->
inputStream.copyTo(fileOut)
}
return true
}
catch (e: IOException) {
Log.e(TAG, "Failed to write file '${this.absolutePath}': $e")
}
return false
}
@RequiresApi(29)
fun selectInputFile(activity: Activity, activityCode: Int) {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "*/*"
putExtra(DocumentsContract.EXTRA_INITIAL_URI, MediaStore.Downloads.EXTERNAL_CONTENT_URI)
}
startActivityForResult(activity, intent, activityCode, null)
}
@RequiresApi(29)
fun selectOutputFile(title: String) {
Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "application/octet-stream"
putExtra(Intent.EXTRA_TITLE, title)
putExtra(DocumentsContract.EXTRA_INITIAL_URI, MediaStore.Downloads.EXTERNAL_CONTENT_URI)
}
}
fun downloadsPath(fileName: String): String {
return Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DOWNLOADS).path + "/$fileName"
}
fun fileNameOfUri(ctx: Context, uri: Uri): String {
val cursor = ctx.contentResolver.query(uri, null, null, null, null)
var name = ""

View File

@ -353,6 +353,8 @@
<string name="reset_config_help">Jos merkitty, oletusasetukset palautetaan, kun
baresip seuraavan kerran käynnistetään.
</string>
<string name="reset_config_alert">Oletko varma, että haluat palauttaa oletusasetukset?</string>
<string name="reset">Palauta</string>
<string name="read_cert_error">Tiedoston \'cert.pem\' luku epäonnistui.</string>
<string name="read_ca_certs_error">Tiedoston \'ca_certs.crt\' epäonnistui.</string>
<string name="config_restart">baresip täytyy käynnistää uudelleen, jotta saat uudet asetukset

View File

@ -315,6 +315,9 @@
request and response trace to Logcat. Unchecked automatically at baresip start.</string>
<string name="reset_config">Reset to Factory Defaults</string>
<string name="reset_config_help">If checked, settings are reset to factory default values.</string>
<string name="reset_config_alert">Are you sure you want to reset settings to factory
default values?</string>
<string name="reset">Reset</string>
<string name="read_cert_error">Failed to read file \'cert.pem\'.</string>
<string name="read_ca_certs_error">Failed to read file \'ca_certs.crt\'.</string>
<string name="config_restart">You need to restart baresip in order to activate the new