From e47f7bf85515273234f13521c857a0b9acd41b30 Mon Sep 17 00:00:00 2001 From: Juha Heinanen Date: Thu, 17 Feb 2022 18:00:44 +0200 Subject: [PATCH] Replaced Show Android Contacts setting with Contacts setting with possible values 'baresip', 'Android', and Both --- app/src/main/AndroidManifest.xml | 11 +- .../com/tutpro/baresip/AndroidContact.kt | 17 -- .../tutpro/baresip/AndroidContactActivity.kt | 6 +- .../baresip/AndroidContactListAdapter.kt | 75 ------ .../tutpro/baresip/AndroidContactsActivity.kt | 228 ------------------ .../com/tutpro/baresip/BaresipService.kt | 75 ++++-- .../com/tutpro/baresip/CallListAdapter.kt | 2 +- .../com/tutpro/baresip/CallsActivity.kt | 4 +- .../kotlin/com/tutpro/baresip/ChatActivity.kt | 4 +- .../com/tutpro/baresip/ChatListAdapter.kt | 2 +- .../com/tutpro/baresip/ChatsActivity.kt | 12 +- .../main/kotlin/com/tutpro/baresip/Config.kt | 17 +- .../com/tutpro/baresip/ConfigActivity.kt | 119 +++++++-- .../kotlin/com/tutpro/baresip/Constants.kt | 2 +- .../main/kotlin/com/tutpro/baresip/Contact.kt | 186 ++++++++++++-- .../com/tutpro/baresip/ContactActivity.kt | 158 ++++-------- .../com/tutpro/baresip/ContactListAdapter.kt | 204 ++++++++++------ .../com/tutpro/baresip/ContactsActivity.kt | 65 +---- .../kotlin/com/tutpro/baresip/MainActivity.kt | 87 +++---- .../com/tutpro/baresip/MessageListAdapter.kt | 2 +- .../main/kotlin/com/tutpro/baresip/Utils.kt | 70 +++--- app/src/main/res/layout/activity_config.xml | 36 +-- app/src/main/res/layout/activity_main.xml | 1 + app/src/main/res/values/strings.xml | 10 +- 24 files changed, 619 insertions(+), 774 deletions(-) delete mode 100644 app/src/main/kotlin/com/tutpro/baresip/AndroidContact.kt delete mode 100644 app/src/main/kotlin/com/tutpro/baresip/AndroidContactListAdapter.kt delete mode 100644 app/src/main/kotlin/com/tutpro/baresip/AndroidContactsActivity.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 62fd92a1..82a2e3a1 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -85,7 +85,7 @@ @@ -95,18 +95,11 @@ android:label="@string/contact" android:parentActivityName=".ContactsActivity" > - - + android:parentActivityName=".ContactsActivity" > () - - companion object { - - fun contacts(): ArrayList { - return BaresipService.androidContacts - } - } - -} diff --git a/app/src/main/kotlin/com/tutpro/baresip/AndroidContactActivity.kt b/app/src/main/kotlin/com/tutpro/baresip/AndroidContactActivity.kt index ae9ce1b4..f8cfeb10 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/AndroidContactActivity.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/AndroidContactActivity.kt @@ -26,7 +26,6 @@ class AndroidContactActivity : AppCompatActivity() { private var index = 0 private var color = 0 - private var id: Long = 0 override fun onCreate(savedInstanceState: Bundle?) { @@ -43,10 +42,9 @@ class AndroidContactActivity : AppCompatActivity() { cardImageAvatarView = binding.ImageAvatar nameView = binding.Name - val contact = BaresipService.androidContacts[index] + val contact = Contact.contacts()[index] as Contact.AndroidContact val name = contact.name color = contact.color - id = contact.id val thumbnailUri = contact.thumbnailUri if (thumbnailUri != null) showImageAvatar(thumbnailUri) @@ -105,5 +103,3 @@ class AndroidContactActivity : AppCompatActivity() { } } - - diff --git a/app/src/main/kotlin/com/tutpro/baresip/AndroidContactListAdapter.kt b/app/src/main/kotlin/com/tutpro/baresip/AndroidContactListAdapter.kt deleted file mode 100644 index ca0bd64d..00000000 --- a/app/src/main/kotlin/com/tutpro/baresip/AndroidContactListAdapter.kt +++ /dev/null @@ -1,75 +0,0 @@ -package com.tutpro.baresip - -import android.content.Context -import android.content.Intent -import android.os.Bundle -import android.os.SystemClock -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.ArrayAdapter -import android.widget.ImageView -import android.widget.TextView -import androidx.core.content.ContextCompat.startActivity - -class AndroidContactListAdapter(private val ctx: Context, - private val rows: ArrayList, - private val aor: String) : - ArrayAdapter(ctx, R.layout.android_contact_row, rows) { - - private val layoutInflater = LayoutInflater.from(context) - private var lastClick: Long = 0 - - private class ViewHolder(view: View?) { - val textAvatarView = view?.findViewById(R.id.TextAvatar) as TextView - val imageAvatarView = view?.findViewById(R.id.ImageAvatar) as ImageView - val nameView = view?.findViewById(R.id.contactName) as TextView - } - - override fun getView(position: Int, view: View?, parent: ViewGroup): View { - - val viewHolder: ViewHolder - val rowView: View - - if (view == null) { - rowView = layoutInflater.inflate(R.layout.android_contact_row, parent, false) - viewHolder = ViewHolder(rowView) - rowView.tag = viewHolder - } else { - rowView = view - viewHolder = rowView.tag as ViewHolder - } - - val contact = rows[position] - - val thumbNailUri = contact.thumbnailUri - if (thumbNailUri != null) { - viewHolder.imageAvatarView.setImageURI(thumbNailUri) - } else { - viewHolder.textAvatarView.background.setTint(contact.color) - if (contact.name.isNotEmpty()) - viewHolder.textAvatarView.text = "${contact.name[0]}" - else - viewHolder.textAvatarView.text = "" - viewHolder.imageAvatarView.setImageBitmap(Utils.bitmapFromView(viewHolder.textAvatarView)) - } - - viewHolder.nameView.text = contact.name - viewHolder.nameView.textSize = 20f - viewHolder.nameView.setPadding(6, 6, 0, 6) - - viewHolder.nameView.setOnClickListener { - if (SystemClock.elapsedRealtime() - lastClick > 1000) { - lastClick = SystemClock.elapsedRealtime() - val i = Intent(ctx, AndroidContactActivity::class.java) - val b = Bundle() - b.putString("aor", aor) - b.putInt("index", position) - i.putExtras(b) - startActivity(ctx, i, null) - } - } - - return rowView - } -} diff --git a/app/src/main/kotlin/com/tutpro/baresip/AndroidContactsActivity.kt b/app/src/main/kotlin/com/tutpro/baresip/AndroidContactsActivity.kt deleted file mode 100644 index 5b322597..00000000 --- a/app/src/main/kotlin/com/tutpro/baresip/AndroidContactsActivity.kt +++ /dev/null @@ -1,228 +0,0 @@ -package com.tutpro.baresip - -import android.Manifest -import android.app.Activity -import android.content.* -import android.content.pm.PackageManager -import android.database.Cursor -import android.os.Build -import android.os.Bundle -import android.os.SystemClock -import android.provider.ContactsContract -import android.view.Menu -import androidx.appcompat.app.AppCompatActivity -import android.view.MenuItem -import android.widget.RelativeLayout -import androidx.activity.result.ActivityResultLauncher -import androidx.activity.result.contract.ActivityResultContracts -import androidx.core.app.ActivityCompat -import androidx.core.net.toUri -import com.google.android.material.snackbar.Snackbar -import com.tutpro.baresip.Utils.showSnackBar -import com.tutpro.baresip.databinding.ActivityAndroidContactsBinding - -class AndroidContactsActivity : AppCompatActivity() { - - private lateinit var binding: ActivityAndroidContactsBinding - private lateinit var layout: RelativeLayout - private lateinit var clAdapter: AndroidContactListAdapter - private lateinit var aor: String - private var lastSwap: Long = 0 - private lateinit var permissionsLauncher: ActivityResultLauncher> - private val permissions = arrayOf(Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS) - - public override fun onCreate(savedInstanceState: Bundle?) { - - super.onCreate(savedInstanceState) - binding = ActivityAndroidContactsBinding.inflate(layoutInflater) - setContentView(binding.root) - layout = binding.AndroidContactsView - - aor = intent.getStringExtra("aor")!! - - val listView = binding.androidContacts - - permissionsLauncher = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions -> - var allowed = true - permissions.entries.forEach { - allowed = allowed && it.value - } - if (allowed) { - fetchAndroidContacts(this) - clAdapter.notifyDataSetChanged() - } - } - - if (Build.VERSION.SDK_INT >= 23 && !Utils.checkPermissions(this, permissions)) - requestPermissions(permissions, CONTACT_PERMISSION_REQUEST_CODE) - - clAdapter = AndroidContactListAdapter(this, BaresipService.androidContacts, aor) - listView.adapter = clAdapter - - Utils.addActivity("android contacts,$aor") - - } - - override fun onCreateOptionsMenu(menu: Menu): Boolean { - menuInflater.inflate(R.menu.swap_contacts_icon, menu) - return super.onCreateOptionsMenu(menu) - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - - when (item.itemId) { - - R.id.swapIcon -> { - if (SystemClock.elapsedRealtime() - lastSwap > 1000) { - lastSwap = SystemClock.elapsedRealtime() - BaresipService.activities.remove("android contacts,$aor") - val intent = Intent(this, ContactsActivity::class.java) - intent.putExtra("aor", aor) - startActivity(intent) - finish() - return true - } - } - - android.R.id.home -> { - BaresipService.activities.remove("android contacts,$aor") - setResult(Activity.RESULT_OK, Intent()) - finish() - } - } - - return true - - } - - override fun onBackPressed() { - - BaresipService.activities.remove("android contacts,$aor") - setResult(Activity.RESULT_OK, Intent()) - finish() - - } - - override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, - grandResults: IntArray) { - super.onRequestPermissionsResult(requestCode, permissions, grandResults) - when (requestCode) { - CONTACT_PERMISSION_REQUEST_CODE -> { - var allowed = true - for (res in grandResults) - allowed = allowed && res == PackageManager.PERMISSION_GRANTED - if (allowed) { - fetchAndroidContacts(this) - clAdapter.notifyDataSetChanged() - } else { - when { - ActivityCompat.shouldShowRequestPermissionRationale(this, - Manifest.permission.READ_CONTACTS) -> { - layout.showSnackBar( - binding.root, - getString(R.string.no_android_contacts), - Snackbar.LENGTH_INDEFINITE, - getString(R.string.ok) - ) { - permissionsLauncher.launch(permissions) - } - } - ActivityCompat.shouldShowRequestPermissionRationale(this, - Manifest.permission.WRITE_CONTACTS) -> { - layout.showSnackBar( - binding.root, - getString(R.string.no_android_contacts), - Snackbar.LENGTH_INDEFINITE, - getString(R.string.ok) - ) { - permissionsLauncher.launch(permissions) - } - } - else -> { - permissionsLauncher.launch(permissions) - } - } - } - } - } - } - - companion object { - - fun fetchAndroidContacts(ctx: Context) { - val projection = arrayOf(ContactsContract.Data.CONTACT_ID, ContactsContract.Data.DISPLAY_NAME, - ContactsContract.Data.MIMETYPE, ContactsContract.Data.DATA1, - ContactsContract.Data.PHOTO_THUMBNAIL_URI) - val selection = - ContactsContract.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE + "' OR " + - ContactsContract.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE + "'" - val cur: Cursor? = ctx.contentResolver.query(ContactsContract.Data.CONTENT_URI, projection, - selection, null, null) - val contacts = HashMap() - while (cur != null && cur.moveToNext()) { - val id = cur.getLong(0) - val name = cur.getString(1) ?: "" - val mime = cur.getString(2) - val data = cur.getString(3) - val thumb = cur.getString(4)?.toUri() - val contact = if (contacts.containsKey(id)) - contacts[id]!! - else - AndroidContact(id, name, Utils.randomColor(), thumb) - if (contact.name == "" && name != "") - contact.name = name - if (contact.thumbnailUri == null && thumb != null) - contact.thumbnailUri = thumb - if (mime == ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE) - contact.uris.add("tel:${data.filterNot{setOf('-', ' ').contains(it)}}") - else if (mime == ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE) - contact.uris.add("sip:$data") - else - continue - if (!contacts.containsKey(id)) - contacts[id] = contact - } - cur?.close() - BaresipService.androidContacts.clear() - for ((_, value) in contacts) - if (value.name != "") - BaresipService.androidContacts.add(value) - Utils.reloadContactNames() - return - } - - fun findContact(uri: String): AndroidContact? { - for (c in BaresipService.androidContacts) - for (u in c.uris) - if (Utils.uriMatch(u, uri)) - return c - return null - } - - // Return contact name of uri or uri itself if contact with uri is not found - fun contactName(uri: String): String { - val userPart = Utils.uriUserPart(uri) - return if (Utils.isTelNumber(userPart)) - findContact("tel:$userPart")?.name ?: uri - else - findContact(uri)?.name ?: uri - } - - // Return first sip (preferred) or tel uri of contact name or - // null if contact is not found or if it has no uris - fun contactUri(name: String): String? { - for (c in BaresipService.androidContacts) - if (c.name == name) { - if (c.uris.isNotEmpty()) { - for (u in c.uris) - if (u.startsWith("sip:")) - return u - return c.uris.first() - } - } - return null - } - - } - -} diff --git a/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt b/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt index 4e4ba9f6..332a02ec 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt @@ -41,6 +41,7 @@ import java.io.File import java.net.InetAddress import java.nio.charset.StandardCharsets import java.util.* +import kotlin.collections.ArrayList import kotlin.concurrent.schedule import kotlin.math.roundToInt @@ -74,6 +75,7 @@ class BaresipService: Service() { private var hotSpotIsEnabled = false private var hotSpotAddresses = mapOf() private var mediaPlayer: MediaPlayer? = null + private var contentObserverRegistered = false @SuppressLint("WakelockTimeout") override fun onCreate() { @@ -299,33 +301,28 @@ class BaresipService: Service() { this.registerReceiver(bluetoothReceiver, filter) } - val contentObserver: ContentObserver = object : ContentObserver(null) { + contentObserver = object : ContentObserver(null) { override fun onChange(self: Boolean) { Log.d(TAG, "Contacts change") - AndroidContactsActivity.fetchAndroidContacts(this@BaresipService.applicationContext) + if (contactsMode != "baresip") { + Contact.loadAndroidContacts(this@BaresipService.applicationContext) + Contact.contactsUpdate() + } } } - try { - AndroidContactsActivity.fetchAndroidContacts(this) - contentResolver.registerContentObserver( - ContactsContract.Contacts.CONTENT_URI, true, contentObserver) - } catch(e: SecurityException) { - Log.i(TAG, "No Contacts permission") - } - } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { - val action: String + val action: String? if (intent == null) { action = "Start" Log.d(TAG, "Received onStartCommand with null intent") } else { // Utils.dumpIntent(intent) - action = intent.action!! + action = intent.action Log.d(TAG, "Received onStartCommand action $action") } @@ -359,13 +356,21 @@ class BaresipService: Service() { Log.i(TAG, "Asset '$a' already copied") } if (a == "config") - Config.initialize() + Config.initialize(applicationContext) } if (File(filesDir, "history").exists()) File(filesDir, "history").renameTo(File(filesDir, "calls")) - Contact.restore() + + if (contactsMode != "Android") + Contact.restoreBaresipContacts() + if (contactsMode != "baresip") { + Contact.loadAndroidContacts(applicationContext) + registerContentObserver() + } + Contact.contactsUpdate() + val history = CallHistory.get() if (history.isEmpty()) { NewCallHistory.restore() @@ -417,6 +422,14 @@ class BaresipService: Service() { } + "Start Content Observer" -> { + registerContentObserver() + } + + "Stop Content Observer" -> { + unRegisterContentObserver() + } + "Call Answer" -> { val uap = intent!!.getStringExtra("uap")!! val callp = intent.getStringExtra("callp")!! @@ -666,7 +679,7 @@ class BaresipService: Service() { PendingIntent.getActivity(applicationContext, CALL_REQ_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT) val nb = NotificationCompat.Builder(this, HIGH_CHANNEL_ID) - val caller = Utils.friendlyUri(Utils.contactName(peerUri), Utils.aorDomain(aor)) + val caller = Utils.friendlyUri(Contact.contactName(peerUri), Utils.aorDomain(aor)) nb.setSmallIcon(R.drawable.ic_stat_call) .setColor(ContextCompat.getColor(this, R.color.colorBaresip)) .setContentIntent(pi) @@ -760,7 +773,7 @@ class BaresipService: Service() { PendingIntent.getActivity(applicationContext, TRANSFER_REQ_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT) val nb = NotificationCompat.Builder(this, HIGH_CHANNEL_ID) - val target = Utils.friendlyUri(Utils.contactName(ev[1]), Utils.aorDomain(aor)) + val target = Utils.friendlyUri(Contact.contactName(ev[1]), Utils.aorDomain(aor)) nb.setSmallIcon(R.drawable.ic_stat_call) .setColor(ContextCompat.getColor(this, R.color.colorBaresip)) .setContentIntent(pi) @@ -836,7 +849,7 @@ class BaresipService: Service() { } if (!Utils.isVisible()) { if (missed) { - val caller = Utils.friendlyUri(Utils.contactName(call.peerUri), Utils.aorDomain(aor)) + val caller = Utils.friendlyUri(Contact.contactName(call.peerUri), Utils.aorDomain(aor)) val intent = Intent(applicationContext, MainActivity::class.java) intent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK @@ -942,7 +955,7 @@ class BaresipService: Service() { PendingIntent.getActivity(applicationContext, MESSAGE_REQ_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT) val nb = NotificationCompat.Builder(this, HIGH_CHANNEL_ID) - val sender = Utils.friendlyUri(Utils.contactName(peer), Utils.aorDomain(ua.account.aor)) + val sender = Utils.friendlyUri(Contact.contactName(peer), Utils.aorDomain(ua.account.aor)) nb.setSmallIcon(R.drawable.ic_stat_message) .setColor(ContextCompat.getColor(this, R.color.colorBaresip)) .setContentIntent(pi) @@ -1389,6 +1402,24 @@ class BaresipService: Service() { return false } + private fun registerContentObserver() { + if (!contentObserverRegistered) + try { + contentResolver.registerContentObserver(ContactsContract.Contacts.CONTENT_URI, + true, contentObserver) + contentObserverRegistered = true + } catch (e: SecurityException) { + Log.i(TAG, "No Contacts permission") + } + } + + private fun unRegisterContentObserver() { + if (contentObserverRegistered) { + contentResolver.unregisterContentObserver(contentObserver) + contentObserverRegistered = false + } + } + private fun cleanService() { am.mode = AudioManager.MODE_NORMAL abandonAudioFocus() @@ -1429,7 +1460,6 @@ class BaresipService: Service() { var callActionUri = "" var isMainVisible = false var isMicMuted = false - var preferAndroidContacts = false val uas = ArrayList() val status = ArrayList() @@ -1437,10 +1467,13 @@ class BaresipService: Service() { var callHistory = ArrayList() var messages = ArrayList() val messageUpdate = MutableLiveData() + val contactUpdate = MutableLiveData() val registrationUpdate = MutableLiveData() + val baresipContacts = ArrayList() + val androidContacts = ArrayList() val contacts = ArrayList() - val androidContacts = ArrayList() - val contactNames = mutableListOf() + val contactNames = ArrayList() + var contactsMode = "baresip" val chatTexts: MutableMap = mutableMapOf() val activities = mutableListOf() var dnsServers = listOf() diff --git a/app/src/main/kotlin/com/tutpro/baresip/CallListAdapter.kt b/app/src/main/kotlin/com/tutpro/baresip/CallListAdapter.kt index 272dd511..3fcb65c7 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/CallListAdapter.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/CallListAdapter.kt @@ -62,7 +62,7 @@ class CallListAdapter(private val ctx: Context, private val aor: String, private if (count <= 3) viewHolder.etcView.text = "" - val contactName = Utils.contactName(callRow.peerUri) + val contactName = Contact.contactName(callRow.peerUri) if (contactName.startsWith("sip:")) viewHolder.peerURIView.text = Utils.friendlyUri(contactName, Utils.aorDomain(callRow.aor)) else diff --git a/app/src/main/kotlin/com/tutpro/baresip/CallsActivity.kt b/app/src/main/kotlin/com/tutpro/baresip/CallsActivity.kt index ce5f435c..d0cada1a 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/CallsActivity.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/CallsActivity.kt @@ -50,7 +50,7 @@ class CallsActivity : AppCompatActivity() { listView.onItemClickListener = AdapterView.OnItemClickListener { _, _, pos, _ -> val peerUri = uaHistory[pos].peerUri - var peerName = Utils.contactName(peerUri) + var peerName = Contact.contactName(peerUri) if (peerName.startsWith("sip:")) peerName = Utils.friendlyUri(peerName, Utils.aorDomain(aor)) val dialogClickListener = DialogInterface.OnClickListener { _, which -> @@ -95,7 +95,7 @@ class CallsActivity : AppCompatActivity() { listView.onItemLongClickListener = AdapterView.OnItemLongClickListener { _, _, pos, _ -> val peerUri = uaHistory[pos].peerUri - val peerName = Utils.contactName(peerUri) + val peerName = Contact.contactName(peerUri) val dialogClickListener = DialogInterface.OnClickListener { _, which -> when (which) { DialogInterface.BUTTON_NEGATIVE -> { diff --git a/app/src/main/kotlin/com/tutpro/baresip/ChatActivity.kt b/app/src/main/kotlin/com/tutpro/baresip/ChatActivity.kt index 62453d07..8d79cd7a 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/ChatActivity.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/ChatActivity.kt @@ -63,7 +63,7 @@ class ChatActivity : AppCompatActivity() { imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager this@ChatActivity.window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN) - var chatPeer = Utils.contactName(peerUri) + var chatPeer = Contact.contactName(peerUri) if (chatPeer.startsWith("sip:")) chatPeer = Utils.friendlyUri(chatPeer, Utils.aorDomain(aor)) @@ -112,7 +112,7 @@ class ChatActivity : AppCompatActivity() { } } val builder = AlertDialog.Builder(this@ChatActivity, R.style.Theme_AppCompat) - if (Utils.contactName(peerUri) == peerUri) + if (Contact.contactName(peerUri) == peerUri) with (builder) { setMessage(String.format(getString(R.string.long_message_question), chatPeer)) diff --git a/app/src/main/kotlin/com/tutpro/baresip/ChatListAdapter.kt b/app/src/main/kotlin/com/tutpro/baresip/ChatListAdapter.kt index 6d4fa48d..2c0e92ed 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/ChatListAdapter.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/ChatListAdapter.kt @@ -52,7 +52,7 @@ class ChatListAdapter(private val ctx: Context, private val rows: ArrayList { val peerUri = uaMessages[pos].peerUri - val msgs = ArrayList() + val messages = ArrayList() for (m in Message.messages()) if ((m.aor != aor) || (m.peerUri != peerUri)) - msgs.add(m) + messages.add(m) else clAdapter.remove(m) clAdapter.notifyDataSetChanged() - BaresipService.messages = msgs + BaresipService.messages = messages Message.save() uaMessages = uaMessages(aor) } @@ -95,7 +95,7 @@ class ChatsActivity: AppCompatActivity() { } val builder = AlertDialog.Builder(this@ChatsActivity, R.style.Theme_AppCompat) - val peer = Utils.contactName(uaMessages[pos].peerUri) + val peer = Contact.contactName(uaMessages[pos].peerUri) if (peer.startsWith("sip:")) with (builder) { setMessage(String.format(getString(R.string.long_chat_question), @@ -118,12 +118,12 @@ class ChatsActivity: AppCompatActivity() { peerUri = binding.peer peerUri.threshold = 2 peerUri.setAdapter(ArrayAdapter(this, android.R.layout.select_dialog_item, - Utils.contactNames())) + Contact.contactNames())) plusButton.setOnClickListener { val uriText = peerUri.text.toString().trim() if (uriText.isNotEmpty()) { - var uri = Utils.contactUri(uriText) + var uri = Contact.contactUri(uriText) if (uri == null) uri = if (Utils.isTelNumber(uriText)) "tel:$uriText" diff --git a/app/src/main/kotlin/com/tutpro/baresip/Config.kt b/app/src/main/kotlin/com/tutpro/baresip/Config.kt index b9ac9d59..31d41a0b 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/Config.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/Config.kt @@ -1,5 +1,6 @@ package com.tutpro.baresip +import android.Manifest import android.content.Context import java.net.InetAddress import java.nio.charset.StandardCharsets @@ -9,7 +10,7 @@ object Config { private val configPath = BaresipService.filesPath + "/config" private var config = String(Utils.getFileContents(configPath)!!, StandardCharsets.ISO_8859_1) - fun initialize() { + fun initialize(ctx: Context) { config = config.replace("module_tmp uuid.so", "module uuid.so") @@ -85,7 +86,19 @@ object Config { config = "${config}jitter_buffer_wish 6\n" } - BaresipService.preferAndroidContacts = config.contains("prefer_android_contacts yes") + removeVariable("prefer_android_contacts") + + if (config.contains("contacts_mode")) { + BaresipService.contactsMode = variable("contacts_mode")[0] + if (BaresipService.contactsMode != "baresip" && + !Utils.checkPermissions(ctx, arrayOf(Manifest.permission.READ_CONTACTS, + Manifest.permission.WRITE_CONTACTS))) { + BaresipService.contactsMode = "baresip" + replaceVariable("contacts_mode", "baresip") + } + } else { + BaresipService.contactsMode = "baresip" + } Utils.putFileContents(configPath, config.toByteArray()) BaresipService.isConfigInitialized = true diff --git a/app/src/main/kotlin/com/tutpro/baresip/ConfigActivity.kt b/app/src/main/kotlin/com/tutpro/baresip/ConfigActivity.kt index 8583627a..012d7393 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/ConfigActivity.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/ConfigActivity.kt @@ -32,6 +32,7 @@ class ConfigActivity : AppCompatActivity() { private lateinit var binding: ActivityConfigBinding private lateinit var layout: ScrollView + private lateinit var baresipService: Intent private lateinit var autoStart: CheckBox private lateinit var batteryOptimizations: CheckBox private lateinit var listenAddr: EditText @@ -40,11 +41,14 @@ class ConfigActivity : AppCompatActivity() { private lateinit var verifyServer: CheckBox private lateinit var caFile: CheckBox private lateinit var darkTheme: CheckBox - private lateinit var androidContacts: CheckBox + private lateinit var contactsSpinner: Spinner + private lateinit var contactsMode: String + private lateinit var contactsModes: ArrayList private lateinit var debug: CheckBox private lateinit var sipTrace: CheckBox private lateinit var reset: CheckBox private lateinit var requestPermissionLauncher: ActivityResultLauncher + private lateinit var requestPermissionsLauncher: ActivityResultLauncher> private var oldAutoStart = "" private var oldListenAddr = "" @@ -54,7 +58,7 @@ class ConfigActivity : AppCompatActivity() { private var oldCAFile = false private var oldLogLevel = "" private var oldDisplayTheme = -1 - private var oldAndroidContacts = "" + private var oldContactsMode = "" private var save = false private var restart = false private var menu: Menu? = null @@ -63,12 +67,16 @@ class ConfigActivity : AppCompatActivity() { super.onCreate(savedInstanceState) + val contactsPermissions = arrayOf(Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS) + binding = ActivityConfigBinding.inflate(layoutInflater) setContentView(binding.root) layout = binding.ConfigView Utils.addActivity("config") + baresipService = Intent(this@ConfigActivity, BaresipService::class.java) + autoStart = binding.AutoStart val asCv = Config.variable("auto_start") oldAutoStart = if (asCv.size == 0) "no" else asCv[0] @@ -291,10 +299,27 @@ class ConfigActivity : AppCompatActivity() { oldDisplayTheme = Preferences(applicationContext).displayTheme darkTheme.isChecked = oldDisplayTheme == AppCompatDelegate.MODE_NIGHT_YES - androidContacts = binding.AndroidContacts - val acCv = Config.variable("prefer_android_contacts") - oldAndroidContacts = if (acCv.size == 0) "no" else acCv[0] - androidContacts.isChecked = oldAndroidContacts == "yes" + contactsModes = arrayListOf( + getString(R.string.baresip), getString(R.string.android), getString(R.string.both)) + contactsSpinner = binding.contactsSpinner + val ctCv = Config.variable("contacts_mode") + oldContactsMode = if (ctCv.size == 0) "baresip" else ctCv[0] + contactsMode = oldContactsMode + contactsModes.removeAt(contactsModes.indexOf(oldContactsMode)) + contactsModes.add(0, oldContactsMode) + val contactsAdapter = ArrayAdapter(this,android.R.layout.simple_spinner_item, + contactsModes) + contactsAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) + contactsSpinner.adapter = contactsAdapter + contactsSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) { + contactsMode = contactsModes[position] + if (contactsMode != "baresip" && Build.VERSION.SDK_INT >= 23 && + !Utils.checkPermissions(applicationContext, contactsPermissions)) + requestPermissions(contactsPermissions, CONTACTS_PERMISSION_REQUEST_CODE) + } + override fun onNothingSelected(parent: AdapterView<*>) {} + } debug = binding.Debug val dbCv = Config.variable("log_level") @@ -340,7 +365,14 @@ class ConfigActivity : AppCompatActivity() { override fun onStart() { super.onStart() requestPermissionLauncher = - registerForActivityResult(ActivityResultContracts.RequestPermission()) {} + registerForActivityResult(ActivityResultContracts.RequestPermission()) {} + requestPermissionsLauncher = + registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { + if (it.containsValue(false)) { + contactsMode = oldContactsMode + contactsSpinner.setSelection(contactsModes.indexOf(oldContactsMode)) + } + } } override fun onCreateOptionsMenu(menu: Menu): Boolean { @@ -356,6 +388,44 @@ class ConfigActivity : AppCompatActivity() { } + override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, + grandResults: IntArray) { + super.onRequestPermissionsResult(requestCode, permissions, grandResults) + when (requestCode) { + CONTACTS_PERMISSION_REQUEST_CODE -> { + if (grandResults.contains(PackageManager.PERMISSION_DENIED)) { + when { + ActivityCompat.shouldShowRequestPermissionRationale(this, + Manifest.permission.READ_CONTACTS) -> { + layout.showSnackBar( + binding.root, + getString(R.string.no_android_contacts), + Snackbar.LENGTH_INDEFINITE, + getString(R.string.ok) + ) { + requestPermissionsLauncher.launch(permissions) + } + } + ActivityCompat.shouldShowRequestPermissionRationale(this, + Manifest.permission.WRITE_CONTACTS) -> { + layout.showSnackBar( + binding.root, + getString(R.string.no_android_contacts), + Snackbar.LENGTH_INDEFINITE, + getString(R.string.ok) + ) { + requestPermissionsLauncher.launch(permissions) + } + } + else -> { + requestPermissionsLauncher.launch(permissions) + } + } + } + } + } + } + override fun onOptionsItemSelected(item: MenuItem): Boolean { if (BaresipService.activities.indexOf("config") == -1) return true @@ -433,13 +503,28 @@ class ConfigActivity : AppCompatActivity() { if (oldDisplayTheme != newDisplayTheme) Preferences(applicationContext).displayTheme = newDisplayTheme - val androidContactsString = if (androidContacts.isChecked) - "yes" - else - "no" - if (oldAndroidContacts != androidContactsString) { - Config.replaceVariable("prefer_android_contacts", androidContactsString) - BaresipService.preferAndroidContacts = androidContacts.isChecked + if (oldContactsMode != contactsMode) { + Config.replaceVariable("contacts_mode", contactsMode) + BaresipService.contactsMode = contactsMode + when (contactsMode) { + "baresip" -> { + BaresipService.androidContacts.clear() + Contact.restoreBaresipContacts() + baresipService.action = "Stop Content Observer" + } + "Android" -> { + BaresipService.baresipContacts.clear() + Contact.loadAndroidContacts(this) + baresipService.action = "Start Content Observer" + } + "Both" -> { + Contact.restoreBaresipContacts() + Contact.loadAndroidContacts(this) + baresipService.action = "Start Content Observer" + } + } + Contact.contactsUpdate() + startService(baresipService) save = true } @@ -530,9 +615,9 @@ class ConfigActivity : AppCompatActivity() { Utils.alertView(this, getString(R.string.dark_theme), getString(R.string.dark_theme_help)) } - binding.AndroidContactsTitle.setOnClickListener { - Utils.alertView(this, getString(R.string.show_android_contacts), - getString(R.string.show_android_contacts_help)) + binding.ContactsTitle.setOnClickListener { + Utils.alertView(this, getString(R.string.contacts), + getString(R.string.contacts_help)) } binding.DebugTitle.setOnClickListener { Utils.alertView(this, getString(R.string.debug), getString(R.string.debug_help)) diff --git a/app/src/main/kotlin/com/tutpro/baresip/Constants.kt b/app/src/main/kotlin/com/tutpro/baresip/Constants.kt index b54ad47e..45491b0f 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/Constants.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/Constants.kt @@ -6,7 +6,7 @@ const val DEFAULT_CHANNEL_ID = "com.tutpro.baresip.default" const val HIGH_CHANNEL_ID = "com.tutpro.baresip.high" const val MIC_PERMISSION_REQUEST_CODE = 1 -const val CONTACT_PERMISSION_REQUEST_CODE = 2 +const val CONTACTS_PERMISSION_REQUEST_CODE = 2 const val STATUS_NOTIFICATION_ID = 101 const val CALL_NOTIFICATION_ID = 102 diff --git a/app/src/main/kotlin/com/tutpro/baresip/Contact.kt b/app/src/main/kotlin/com/tutpro/baresip/Contact.kt index 1b4f576f..e5506a76 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/Contact.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/Contact.kt @@ -1,39 +1,165 @@ package com.tutpro.baresip +import android.content.Context +import android.database.Cursor import android.graphics.Bitmap import android.graphics.BitmapFactory - +import android.net.Uri +import android.provider.ContactsContract +import androidx.core.net.toUri import java.io.File import java.util.ArrayList -class Contact(var name: String, var uri: String, var color: Int, val id: Long) { +sealed class Contact { - var avatarImage: Bitmap? = null - var androidContact = false + class BaresipContact(var name: String, var uri: String, var color: Int, val id: Long): Contact() { + var avatarImage: Bitmap? = null + } + + class AndroidContact(var name: String, var color: Int, var thumbnailUri: Uri?): Contact() { + val uris = ArrayList() + } companion object { - const val CONTACTS_SIZE = 100 + const val CONTACTS_SIZE = 256 fun contacts(): ArrayList { return BaresipService.contacts } - fun save(): Boolean { + fun contactNames(): ArrayList { + return BaresipService.contactNames + } + + // Return contact name of uri or uri itself if contact with uri is not found + fun contactName(uri: String): String { + val userPart = Utils.uriUserPart(uri) + val contact = if (Utils.isTelNumber(userPart)) + findContact("tel:$userPart") + else + findContact(uri) + if (contact != null) { + return when (contact) { + is BaresipContact -> + contact.name + is AndroidContact -> + contact.name + } + } + return uri + } + + // Return uri of contact name or null if contact is not found + fun contactUri(name: String): String? { + for (c in contacts()) + when (c) { + is BaresipContact -> { + if (c.name.equals(name, ignoreCase = true)) + return c.uri.removePrefix("<") + .replaceAfter(">", "") + .replace(">", "") + } + is AndroidContact -> { + if (c.name == name) { + if (c.uris.isNotEmpty()) { + for (u in c.uris) + if (u.startsWith("sip:")) + return u + return c.uris.first() + } + } + } + } + return null + } + + fun findContact(uri: String): Contact? { + for (c in contacts()) + when (c) { + is BaresipContact -> { + if (Utils.uriMatch(c.uri, uri)) + return c + } + is AndroidContact -> { + for (u in c.uris) + if (Utils.uriMatch(u, uri)) + return c + } + } + return null + } + + fun nameExists(name: String, ignoreCase: Boolean): Boolean { + for (c in BaresipService.baresipContacts) + if (c.name.equals(name, ignoreCase = ignoreCase)) + return true + for (c in BaresipService.androidContacts) + if (c.name.equals(name, ignoreCase = ignoreCase)) + return true + return false + } + + fun saveBaresipContacts() { var contents = "" - for (c in BaresipService.contacts) contents += - "\"${c.name}\" <${c.uri}>;id=${c.id};color=${c.color};android=${c.androidContact}\n" - return Utils.putFileContents(BaresipService.filesPath + "/contacts", + for (c in BaresipService.contacts) + if (c is BaresipContact) + contents += "\"${c.name}\" <${c.uri}>;id=${c.id};color=${c.color}\n" + Utils.putFileContents(BaresipService.filesPath + "/contacts", contents.toByteArray()) } - fun restore(): Boolean { + fun loadAndroidContacts(ctx: Context) { + val projection = arrayOf(ContactsContract.Data.CONTACT_ID, ContactsContract.Data.DISPLAY_NAME, + ContactsContract.Data.MIMETYPE, ContactsContract.Data.DATA1, + ContactsContract.Data.PHOTO_THUMBNAIL_URI) + val selection = + ContactsContract.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE + "' OR " + + ContactsContract.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE + "'" + val cur: Cursor? = ctx.contentResolver.query(ContactsContract.Data.CONTENT_URI, projection, + selection, null, null) + BaresipService.androidContacts.clear() + val contacts = HashMap() + while (cur != null && cur.moveToNext()) { + val id = cur.getLong(0) + val name = cur.getString(1) ?: "" + val mime = cur.getString(2) + val data = cur.getString(3) + val thumb = cur.getString(4)?.toUri() + if (nameExists(name, true)) { + Log.d(TAG, "Skipping Android contact with existing name '$name'") + continue + } + val contact = if (contacts.containsKey(id)) + contacts[id]!! + else + AndroidContact(name, Utils.randomColor(), thumb) + if (contact.name == "" && name != "") + contact.name = name + if (contact.thumbnailUri == null && thumb != null) + contact.thumbnailUri = thumb + if (mime == ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE) + contact.uris.add("tel:${data.filterNot{setOf('-', ' ').contains(it)}}") + else if (mime == ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE) + contact.uris.add("sip:$data") + else + continue + if (!contacts.containsKey(id)) + contacts[id] = contact + } + cur?.close() + for ((_, value) in contacts) + if (value.name != "") + BaresipService.androidContacts.add(value) + } + + fun restoreBaresipContacts(): Boolean { val content = Utils.getFileContents(BaresipService.filesPath + "/contacts") ?: return false val contacts = String(content) - BaresipService.contacts.clear() var contactNo = 0 val baseId = System.currentTimeMillis() + BaresipService.baresipContacts.clear() contacts.lines().forEach { val parts = it.split("\"") if (parts.size == 3) { @@ -47,15 +173,13 @@ class Contact(var name: String, var uri: String, var color: Int, val id: Long) { colorValue.toInt() else Utils.randomColor() - val androidContact = Utils.paramValue(params, "android" ) == "true" val idValue = Utils.paramValue(params, "id" ) val id: Long = if (idValue != "") idValue.toLong() else baseId + contactNo - Log.d(TAG, "Restoring contact $name, $uri, $color, $id, $androidContact") - val contact = Contact(name, uri, color, id) - contact.androidContact = androidContact + Log.d(TAG, "Restoring contact $name, $uri, $color, $id") + val contact = BaresipContact(name, uri, color, id) val avatarFilePath = BaresipService.filesPath + "/$id.png" if (File(avatarFilePath).exists()) { try { @@ -67,12 +191,38 @@ class Contact(var name: String, var uri: String, var color: Int, val id: Long) { Log.e(TAG, "Could not read avatar image from '$id.img") } } - BaresipService.contacts.add(contact) + BaresipService.baresipContacts.add(contact) } } - Utils.reloadContactNames() return true } + fun contactsUpdate() { + BaresipService.contacts.clear() + if (BaresipService.contactsMode != "Android") + BaresipService.contacts.addAll(BaresipService.baresipContacts) + if (BaresipService.contactsMode != "baresip") + BaresipService.contacts.addAll(BaresipService.androidContacts) + sortContacts() + generateContactNames() + BaresipService.contactUpdate.postValue(System.nanoTime()) + } + + fun sortContacts() { + BaresipService.contacts.sortBy{ when (it) { + is BaresipContact -> it.name + is AndroidContact -> it.name + }} + } + + fun generateContactNames () { + BaresipService.contactNames.clear() + BaresipService.contactNames.addAll( + BaresipService.contacts.map { + when (it) { + is BaresipContact -> it.name + is AndroidContact -> it.name + }}) + } } -} \ No newline at end of file +} diff --git a/app/src/main/kotlin/com/tutpro/baresip/ContactActivity.kt b/app/src/main/kotlin/com/tutpro/baresip/ContactActivity.kt index 58a4672f..7f2fc2ee 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/ContactActivity.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/ContactActivity.kt @@ -1,18 +1,15 @@ package com.tutpro.baresip -import android.Manifest import android.app.Activity import android.content.ContentProviderOperation import android.content.ContentValues import android.content.Context import android.content.Intent -import android.content.pm.PackageManager import android.database.Cursor import android.graphics.Bitmap import android.graphics.BitmapFactory import android.graphics.Matrix import android.graphics.drawable.BitmapDrawable -import android.os.Build import android.os.Bundle import android.provider.ContactsContract import android.provider.ContactsContract.CommonDataKinds @@ -21,14 +18,10 @@ import android.view.Menu import android.view.MenuItem import android.view.View import android.widget.* -import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AppCompatActivity import androidx.cardview.widget.CardView -import androidx.core.app.ActivityCompat import androidx.exifinterface.media.ExifInterface -import com.google.android.material.snackbar.Snackbar -import com.tutpro.baresip.Utils.showSnackBar import com.tutpro.baresip.databinding.ActivityContactBinding import java.io.ByteArrayOutputStream import java.io.File @@ -44,7 +37,6 @@ class ContactActivity : AppCompatActivity() { private lateinit var uriView: EditText private lateinit var androidCheck: CheckBox private lateinit var menu: Menu - private lateinit var requestPermissionsLauncher: ActivityResultLauncher> private var newContact = false private var newAvatar = "" @@ -53,10 +45,6 @@ class ContactActivity : AppCompatActivity() { private var index = 0 private var color = 0 private var id: Long = 0 - private var oldAndroid = false - - private val permissions = - arrayOf(Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS) override fun onCreate(savedInstanceState: Bundle?) { @@ -75,6 +63,10 @@ class ContactActivity : AppCompatActivity() { newContact = intent.getBooleanExtra("new", false) if (newContact) { + if (BaresipService.contactsMode == "baresip") { + binding.AndroidTitle.visibility = View.GONE + androidCheck.visibility = View.GONE + } title = getString(R.string.new_contact) color = Utils.randomColor() id = System.currentTimeMillis() @@ -88,27 +80,33 @@ class ContactActivity : AppCompatActivity() { else uriView.setText(uri) uOrI = uri - androidCheck.isChecked = false + if ((BaresipService.contactsMode == "Android")) { + androidCheck.isChecked = true + androidCheck.isClickable = false + } else { + androidCheck.isChecked = false + } } else { + binding.AndroidTitle.visibility = View.GONE + androidCheck.visibility = View.GONE index = intent.getIntExtra("index", 0) val contact = Contact.contacts()[index] - val name = contact.name - color = contact.color - id = contact.id - val avatarImage = contact.avatarImage - if (avatarImage != null) - showImageAvatar(avatarImage) - else - showTextAvatar(name, color) - title = name - nameView.setText(name) - uriView.setText(contact.uri) - uOrI = index.toString() - androidCheck.isChecked = contact.androidContact + if (contact is Contact.BaresipContact) { + val name = contact.name + color = contact.color + id = contact.id + val avatarImage = contact.avatarImage + if (avatarImage != null) + showImageAvatar(avatarImage) + else + showTextAvatar(name, color) + title = name + nameView.setText(name) + uriView.setText(contact.uri) + uOrI = index.toString() + } } - oldAndroid = androidCheck.isChecked - val avatarRequest = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { if (it.resultCode == Activity.RESULT_OK) { @@ -175,12 +173,6 @@ class ContactActivity : AppCompatActivity() { getString(R.string.android_contact_help)) } - androidCheck.setOnClickListener{ - if (Build.VERSION.SDK_INT >= 23) - if (!Utils.checkPermissions(this, permissions)) - requestPermissions(permissions, CONTACT_PERMISSION_REQUEST_CODE) - } - // Log.d(TAG, "Android sip contacts ${logAndroidContacts(this, "sip")}") // Log.d(TAG, "Android tel contacts ${logAndroidContacts(this, "tel")}") @@ -188,12 +180,6 @@ class ContactActivity : AppCompatActivity() { } - override fun onStart() { - super.onStart() - requestPermissionsLauncher = - registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) {} - } - override fun onCreateOptionsMenu(optionsMenu: Menu): Boolean { super.onCreateOptionsMenu(optionsMenu) @@ -205,48 +191,6 @@ class ContactActivity : AppCompatActivity() { } - override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, - grandResults: IntArray) { - super.onRequestPermissionsResult(requestCode, permissions, grandResults) - when (requestCode) { - CONTACT_PERMISSION_REQUEST_CODE -> { - var allowed = true - for (res in grandResults) - allowed = allowed && res == PackageManager.PERMISSION_GRANTED - if (!allowed) { - androidCheck.isChecked = oldAndroid - when { - ActivityCompat.shouldShowRequestPermissionRationale(this, - Manifest.permission.READ_CONTACTS) -> { - layout.showSnackBar( - binding.root, - getString(R.string.no_android_contacts), - Snackbar.LENGTH_INDEFINITE, - getString(R.string.ok) - ) { - requestPermissionsLauncher.launch(permissions) - } - } - ActivityCompat.shouldShowRequestPermissionRationale(this, - Manifest.permission.WRITE_CONTACTS) -> { - layout.showSnackBar( - binding.root, - getString(R.string.no_android_contacts), - Snackbar.LENGTH_INDEFINITE, - getString(R.string.ok) - ) { - requestPermissionsLauncher.launch(permissions) - } - } - else -> { - requestPermissionsLauncher.launch(permissions) - } - } - } - } - } - } - override fun onOptionsItemSelected(item: MenuItem): Boolean { if (BaresipService.activities.indexOf("contact,$newContact,$uOrI") == -1) return true @@ -265,11 +209,13 @@ class ContactActivity : AppCompatActivity() { String.format(getString(R.string.invalid_contact), newName)) return false } + val alert: Boolean = if (newContact) - ContactsActivity.nameExists(newName, true) - else - (Contact.contacts()[index].name != newName) && - ContactsActivity.nameExists(newName, false) + Contact.nameExists(newName, true) + else { + val c = Contact.contacts()[index] as Contact.BaresipContact + (c.name != newName) && Contact.nameExists(newName, false) + } if (alert) { Utils.alertView(this, getString(R.string.notice), String.format(getString(R.string.contact_already_exists), newName)) @@ -287,7 +233,8 @@ class ContactActivity : AppCompatActivity() { return false } - val contact: Contact + val contact: Contact.BaresipContact + if (newContact) { if (Contact.contacts().size >= Contact.CONTACTS_SIZE) { Utils.alertView(this, getString(R.string.notice), @@ -296,16 +243,13 @@ class ContactActivity : AppCompatActivity() { BaresipService.activities.removeAt(0) return true } else { - contact = Contact(newName, newUri, color, id) - contact.androidContact = androidCheck.isChecked - Contact.contacts().add(contact) + contact = Contact.BaresipContact(newName, newUri, color, id) } } else { - contact = Contact.contacts()[index] + contact = Contact.contacts()[index] as Contact.BaresipContact contact.uri = newUri contact.name = newName contact.color = color - contact.androidContact = androidCheck.isChecked } when (newAvatar) { @@ -323,21 +267,18 @@ class ContactActivity : AppCompatActivity() { } } - Contact.contacts().sortBy { Contact -> Contact.name } - - if (Utils.checkPermissions(this, permissions)) { - if (contact.androidContact) - addOrUpdateAndroidContact(this, contact) - else if (oldAndroid) - deleteAndroidContact(this, contact) + if (androidCheck.isChecked) { + addOrUpdateAndroidContact(this, contact) } else { - contact.androidContact = oldAndroid + if (newContact) + Contact.contacts().add(contact) + Contact.sortContacts() + Contact.generateContactNames() + Contact.saveBaresipContacts() } - Contact.save() - Utils.reloadContactNames() - BaresipService.activities.remove("contact,$newContact,$uOrI") + val i = Intent(this, MainActivity::class.java) i.putExtra("name", newName) setResult(Activity.RESULT_OK, i) @@ -407,7 +348,7 @@ class ContactActivity : AppCompatActivity() { return rotatedBitmap } - private fun addOrUpdateAndroidContact(ctx: Context, contact: Contact) { + private fun addOrUpdateAndroidContact(ctx: Context, contact: Contact.BaresipContact) { val projection = arrayOf(ContactsContract.Data.RAW_CONTACT_ID) val selection = ContactsContract.Data.MIMETYPE + "='" + CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE + "' AND " + @@ -420,10 +361,9 @@ class ContactActivity : AppCompatActivity() { addAndroidContact(ctx, contact) } c?.close() - contact.androidContact = true } - private fun addAndroidContact(ctx: Context, contact: Contact): Boolean { + private fun addAndroidContact(ctx: Context, contact: Contact.BaresipContact): Boolean { val ops = ArrayList() ops.add(ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI) .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, null) @@ -464,7 +404,7 @@ class ContactActivity : AppCompatActivity() { return true } - private fun updateAndroidContact(rawContactId: Long, contact: Contact) { + private fun updateAndroidContact(rawContactId: Long, contact: Contact.BaresipContact) { if (updateAndroidUri(rawContactId, contact.uri) == 0) addAndroidUri(rawContactId, contact.uri) if (updateAndroidPhoto(rawContactId, contact.avatarImage) == 0) @@ -561,9 +501,9 @@ class ContactActivity : AppCompatActivity() { var contact: Contact? = null - fun deleteAndroidContact(ctx: Context, contact: Contact): Int { + fun deleteAndroidContact(ctx: Context, name: String): Int { return ctx.contentResolver.delete(ContactsContract.RawContacts.CONTENT_URI, - ContactsContract.Contacts.DISPLAY_NAME + "='" + contact.name + "'", + ContactsContract.Contacts.DISPLAY_NAME + "='" + name + "'", null) } diff --git a/app/src/main/kotlin/com/tutpro/baresip/ContactListAdapter.kt b/app/src/main/kotlin/com/tutpro/baresip/ContactListAdapter.kt index 0033e630..3c5da769 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/ContactListAdapter.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/ContactListAdapter.kt @@ -1,22 +1,22 @@ package com.tutpro.baresip -import android.Manifest import android.app.Activity import android.content.Context import android.content.DialogInterface import android.content.Intent import android.os.Bundle import android.os.SystemClock -import androidx.appcompat.app.AlertDialog import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.* +import android.widget.ArrayAdapter +import android.widget.ImageButton +import android.widget.ImageView +import android.widget.TextView +import androidx.appcompat.app.AlertDialog import androidx.core.content.ContextCompat.startActivity - import java.io.File import java.io.IOException -import java.util.* class ContactListAdapter(private val ctx: Context, private val rows: ArrayList, private val aor: String) : @@ -48,82 +48,137 @@ class ContactListAdapter(private val ctx: Context, private val rows: ArrayList - when (which) { - DialogInterface.BUTTON_POSITIVE, DialogInterface.BUTTON_NEGATIVE -> { - val i = Intent(ctx, MainActivity::class.java) - i.flags = Intent.FLAG_ACTIVITY_NEW_TASK or - Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP - if (which == DialogInterface.BUTTON_NEGATIVE) - i.putExtra("action", "call") - else - i.putExtra("action", "message") - val ua = UserAgent.ofAor(aor) - if (ua == null) { - Log.w(TAG, "onClickListener did not find AoR $aor") - } else { - BaresipService.activities.clear() - i.putExtra("uap", ua.uap) - i.putExtra("peer", contact.uri) - (ctx as Activity).startActivity(i) + viewHolder.nameView.text = contact.name + viewHolder.nameView.textSize = 20f + viewHolder.nameView.setPadding(6, 6, 0, 6) + + if (aor != "") { + viewHolder.nameView.setOnClickListener { + val dialogClickListener = DialogInterface.OnClickListener { _, which -> + when (which) { + DialogInterface.BUTTON_POSITIVE, DialogInterface.BUTTON_NEGATIVE -> { + val i = Intent(ctx, MainActivity::class.java) + i.flags = Intent.FLAG_ACTIVITY_NEW_TASK or + Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP + if (which == DialogInterface.BUTTON_NEGATIVE) + i.putExtra("action", "call") + else + i.putExtra("action", "message") + val ua = UserAgent.ofAor(aor) + if (ua == null) { + Log.w(TAG, "onClickListener did not find AoR $aor") + } else { + BaresipService.activities.clear() + i.putExtra("uap", ua.uap) + i.putExtra("peer", contact.uri) + (ctx as Activity).startActivity(i) + } + } + DialogInterface.BUTTON_NEUTRAL -> { } } - DialogInterface.BUTTON_NEUTRAL -> { - } } - } - if (SystemClock.elapsedRealtime() - lastClick > 1000) { - lastClick = SystemClock.elapsedRealtime() - with (AlertDialog.Builder(ctx, R.style.Theme_AppCompat)) { - setMessage(String.format(ctx.getString(R.string.contact_action_question), - Contact.contacts()[position].name)) - setNeutralButton(ctx.getText(R.string.cancel), dialogClickListener) - setNegativeButton(ctx.getText(R.string.call), dialogClickListener) - setPositiveButton(ctx.getText(R.string.send_message), dialogClickListener) - show() + if (SystemClock.elapsedRealtime() - lastClick > 1000) { + lastClick = SystemClock.elapsedRealtime() + with(AlertDialog.Builder(ctx, R.style.Theme_AppCompat)) { + setMessage(String.format(ctx.getString(R.string.contact_action_question), + contact.name)) + setNeutralButton(ctx.getText(R.string.cancel), dialogClickListener) + setNegativeButton(ctx.getText(R.string.call), dialogClickListener) + setPositiveButton(ctx.getText(R.string.send_message), dialogClickListener) + show() + } } } } + + viewHolder.actionView.visibility = View.VISIBLE + viewHolder.actionView.setOnClickListener { + if (SystemClock.elapsedRealtime() - lastClick > 1000) { + lastClick = SystemClock.elapsedRealtime() + val i = Intent(ctx, ContactActivity::class.java) + val b = Bundle() + b.putBoolean("new", false) + b.putInt("index", position) + i.putExtras(b) + startActivity(ctx, i, null) + } + } + + } + + if (contact is Contact.AndroidContact) { + + val thumbNailUri = contact.thumbnailUri + if (thumbNailUri != null) { + viewHolder.imageAvatarView.setImageURI(thumbNailUri) + } else { + viewHolder.textAvatarView.background.setTint(contact.color) + if (contact.name.isNotEmpty()) + viewHolder.textAvatarView.text = "${contact.name[0]}" + else + viewHolder.textAvatarView.text = "" + viewHolder.imageAvatarView.setImageBitmap(Utils.bitmapFromView(viewHolder.textAvatarView)) + } + + viewHolder.nameView.text = contact.name + viewHolder.nameView.textSize = 20f + viewHolder.nameView.setPadding(6, 6, 0, 6) + + viewHolder.nameView.setOnClickListener { + if (SystemClock.elapsedRealtime() - lastClick > 1000) { + lastClick = SystemClock.elapsedRealtime() + val i = Intent(ctx, AndroidContactActivity::class.java) + val b = Bundle() + b.putString("aor", aor) + b.putInt("index", position) + i.putExtras(b) + startActivity(ctx, i, null) + } + } + + viewHolder.actionView.visibility = View.GONE + } viewHolder.nameView.setOnLongClickListener { val dialogClickListener = DialogInterface.OnClickListener { _, which -> when (which) { DialogInterface.BUTTON_POSITIVE -> { - val id = contact.id - val avatarFile = File(BaresipService.filesPath, "$id.img") - if (avatarFile.exists()) { - try { - avatarFile.delete() - } catch (e: IOException) { - Log.e(TAG, "Could not delete file '$id.img") + when (contact) { + is Contact.BaresipContact -> { + val id = contact.id + val avatarFile = File(BaresipService.filesPath, "$id.img") + if (avatarFile.exists()) { + try { + avatarFile.delete() + } catch (e: IOException) { + Log.e(TAG, "Could not delete file '$id.img") + } + } + Contact.contacts().removeAt(position) + Contact.contactNames().removeAt(position) + Contact.saveBaresipContacts() + this.notifyDataSetChanged() + } + is Contact.AndroidContact -> { + ContactActivity.deleteAndroidContact(ctx, contact.name) } } - if (contact.androidContact && - Utils.checkPermissions(ctx, arrayOf(Manifest.permission.WRITE_CONTACTS))) - ContactActivity.deleteAndroidContact(ctx, contact) - Contact.contacts().removeAt(position) - Contact.save() - Utils.reloadContactNames() - this.notifyDataSetChanged() } DialogInterface.BUTTON_NEGATIVE -> { } @@ -131,10 +186,13 @@ class ContactListAdapter(private val ctx: Context, private val rows: ArrayList contact.name + is Contact.AndroidContact -> contact.name + })) setNegativeButton(ctx.getText(R.string.cancel), dialogClickListener) setPositiveButton(ctx.getText(R.string.delete), dialogClickListener) show() @@ -142,18 +200,6 @@ class ContactListAdapter(private val ctx: Context, private val rows: ArrayList 1000) { - lastClick = SystemClock.elapsedRealtime() - val i = Intent(ctx, ContactActivity::class.java) - val b = Bundle() - b.putBoolean("new", false) - b.putInt("index", position) - i.putExtras(b) - startActivity(ctx, i, null) - } - } - return rowView } } diff --git a/app/src/main/kotlin/com/tutpro/baresip/ContactsActivity.kt b/app/src/main/kotlin/com/tutpro/baresip/ContactsActivity.kt index 1473ffa5..cc149ef3 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/ContactsActivity.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/ContactsActivity.kt @@ -4,10 +4,10 @@ import android.app.Activity import android.content.* import android.os.Bundle import android.os.SystemClock -import android.view.Menu import androidx.appcompat.app.AppCompatActivity import android.view.MenuItem import androidx.activity.result.contract.ActivityResultContracts +import androidx.lifecycle.Observer import com.tutpro.baresip.databinding.ActivityContactsBinding class ContactsActivity : AppCompatActivity() { @@ -27,10 +27,15 @@ class ContactsActivity : AppCompatActivity() { Utils.addActivity("contacts,$aor") val listView = binding.contacts - clAdapter = ContactListAdapter(this, Contact.contacts(), aor) + clAdapter = ContactListAdapter(this, BaresipService.contacts, aor) listView.adapter = clAdapter listView.isLongClickable = true + val contactObserver = Observer { + clAdapter.notifyDataSetChanged() + } + BaresipService.contactUpdate.observe(this, contactObserver) + val contactRequest = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { if (it.resultCode == RESULT_OK) @@ -58,34 +63,14 @@ class ContactsActivity : AppCompatActivity() { } - override fun onCreateOptionsMenu(menu: Menu): Boolean { - menuInflater.inflate(R.menu.swap_contacts_icon, menu) - return super.onCreateOptionsMenu(menu) - } - override fun onResume() { - super.onResume() - clAdapter.notifyDataSetChanged() } override fun onOptionsItemSelected(item: MenuItem): Boolean { when (item.itemId) { - - R.id.swapIcon -> { - if (SystemClock.elapsedRealtime() - lastClick > 1000) { - lastClick = SystemClock.elapsedRealtime() - BaresipService.activities.remove("contacts,$aor") - val intent = Intent(this, AndroidContactsActivity::class.java) - intent.putExtra("aor", aor) - startActivity(intent) - finish() - return true - } - } - android.R.id.home -> { BaresipService.activities.remove("contacts,$aor") setResult(Activity.RESULT_OK, Intent()) @@ -105,40 +90,4 @@ class ContactsActivity : AppCompatActivity() { } - companion object { - - // Return uri of contact name or null if contact is not found - fun contactUri(name: String): String? { - for (c in Contact.contacts()) - if (c.name.equals(name, ignoreCase = true)) - return c.uri.removePrefix("<") - .replaceAfter(">", "") - .replace(">", "") - return null - } - - // Return contact name of uri or null if contact with uri is not found - fun contactName(uri: String): String? { - val userPart = Utils.uriUserPart(uri) - return if (Utils.isTelNumber(userPart)) - findContact("tel:$userPart")?.name - else - findContact(uri)?.name - } - - fun findContact(uri: String): Contact? { - for (c in Contact.contacts()) { - if (Utils.uriMatch(c.uri, uri)) - return c - } - return null - } - - fun nameExists(name: String, ignoreCase: Boolean): Boolean { - for (c in Contact.contacts()) - if (c.name.equals(name, ignoreCase = ignoreCase)) return true - return false - } - - } } diff --git a/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt b/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt index 8da854c9..eba979a7 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt @@ -209,11 +209,13 @@ class MainActivity : AppCompatActivity() { // 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") - val acc = UserAgent.uas()[position].account - aorSpinner.tag = acc.aor - val ua = UserAgent.uas()[position] - showCall(ua) - updateIcons(acc) + if (position < UserAgent.uas().size) { + val acc = UserAgent.uas()[position].account + aorSpinner.tag = acc.aor + val ua = UserAgent.uas()[position] + showCall(ua) + updateIcons(acc) + } } override fun onNothingSelected(parent: AdapterView<*>) { Log.d(TAG, "Nothing selected") @@ -273,7 +275,7 @@ class MainActivity : AppCompatActivity() { } callUri.setAdapter(ArrayAdapter(this, android.R.layout.select_dialog_item, - Utils.contactNames())) + Contact.contactNames())) callUri.threshold = 2 callUri.setOnFocusChangeListener { view, b -> if (b) { @@ -456,27 +458,18 @@ class MainActivity : AppCompatActivity() { contactsRequest = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { callUri.setAdapter(ArrayAdapter(this, android.R.layout.select_dialog_item, - Utils.contactNames())) + Contact.contactNames())) } contactsButton.setOnClickListener { - if (BaresipService.preferAndroidContacts) { - val i = Intent(this@MainActivity, AndroidContactsActivity::class.java) - if (aorSpinner.selectedItemPosition >= 0) - i.putExtra("aor", aorSpinner.tag.toString()) - else - i.putExtra("aor", "") - startActivity(i) - } else { - 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) - } + 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()) { @@ -932,6 +925,13 @@ class MainActivity : AppCompatActivity() { if (event == "started") { val callActionUri = params[0] Log.d(TAG, "Handling service event 'started' with '$callActionUri'") + if (!this::uaAdapter.isInitialized) { + // Android has restarted baresip when permission has been denied in app settings + val i = Intent(this, MainActivity::class.java) + i.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + startActivity(i) + return + } uaAdapter.notifyDataSetChanged() if (callActionUri != "") { var ua = UserAgent.ofDomain(Utils.uriHostPart(callActionUri)) @@ -1105,7 +1105,7 @@ class MainActivity : AppCompatActivity() { val call = Call.ofCallp(callp)!! val titleView = View.inflate(this, R.layout.alert_title, null) as TextView titleView.text = getString(R.string.transfer_request) - val target = Utils.friendlyUri(Utils.contactName(ev[1]), Utils.aorDomain(aor)) + val target = Utils.friendlyUri(Contact.contactName(ev[1]), Utils.aorDomain(aor)) with(AlertDialog.Builder(this)) { setCustomTitle(titleView) setMessage(String.format(getString(R.string.transfer_request_query), @@ -1403,7 +1403,7 @@ class MainActivity : AppCompatActivity() { titleView.text = getString(R.string.call_transfer) val transferUri = layout.findViewById(R.id.transferUri) as AutoCompleteTextView transferUri.setAdapter(ArrayAdapter(this, android.R.layout.select_dialog_item, - Utils.contactNames())) + Contact.contactNames())) transferUri.threshold = 2 transferUri.requestFocus() val builder = AlertDialog.Builder(this) @@ -1414,7 +1414,7 @@ class MainActivity : AppCompatActivity() { dialog.dismiss() var uriText = transferUri.text.toString().trim() if (uriText.isNotEmpty()) { - uriText = Utils.contactUri(uriText) ?: uriText + uriText = Contact.contactUri(uriText) ?: uriText if (Utils.isTelNumber(uriText)) uriText = "tel:$uriText" val uri = if (Utils.isTelUri(uriText)) @@ -1702,7 +1702,7 @@ class MainActivity : AppCompatActivity() { if (Call.calls().isEmpty()) { var uriText = callUri.text.toString().trim() if (uriText.isNotEmpty()) { - uriText = Utils.contactUri(uriText) ?: uriText + uriText = Contact.contactUri(uriText) ?: uriText if (Utils.isTelNumber(uriText)) uriText = "tel:$uriText" val uri = if (Utils.isTelUri(uriText)) { @@ -1750,10 +1750,8 @@ class MainActivity : AppCompatActivity() { } else { val latest = NewCallHistory.aorLatestHistory(aor) if (latest != null) - callUri.setText( - Utils.friendlyUri(Utils.contactName(latest.peerUri), Utils.aorDomain(aor) - ) - ) + callUri.setText(Utils.friendlyUri(Contact.contactName(latest.peerUri), + Utils.aorDomain(aor))) } } } @@ -1772,7 +1770,7 @@ class MainActivity : AppCompatActivity() { callUri.isFocusableInTouchMode = true imm.hideSoftInputFromWindow(callUri.windowToken, 0) callUri.setAdapter(ArrayAdapter(this, android.R.layout.select_dialog_item, - Utils.contactNames())) + Contact.contactNames())) securityButton.visibility = View.INVISIBLE callButton.visibility = View.VISIBLE callButton.isEnabled = true @@ -1797,7 +1795,7 @@ class MainActivity : AppCompatActivity() { else getString(R.string.outgoing_call_to_dots) callTimer.visibility = View.INVISIBLE - callUri.setText(Utils.friendlyUri(Utils.contactName(call.peerUri), + callUri.setText(Utils.friendlyUri(Contact.contactName(call.peerUri), Utils.aorDomain(ua.account.aor))) securityButton.visibility = View.INVISIBLE callButton.visibility = View.INVISIBLE @@ -1812,7 +1810,7 @@ class MainActivity : AppCompatActivity() { "incoming" -> { callTitle.text = getString(R.string.incoming_call_from_dots) callTimer.visibility = View.INVISIBLE - callUri.setText(Utils.friendlyUri(Utils.contactName(call.peerUri), + callUri.setText(Utils.friendlyUri(Contact.contactName(call.peerUri), Utils.aorDomain(ua.account.aor))) callUri.setAdapter(null) securityButton.visibility = View.INVISIBLE @@ -1832,7 +1830,7 @@ class MainActivity : AppCompatActivity() { } if (call.referTo != "") { callTitle.text = getString(R.string.transferring_call_to_dots) - callUri.setText(Utils.friendlyUri(Utils.contactName(call.referTo), + callUri.setText(Utils.friendlyUri(Contact.contactName(call.referTo), Utils.aorDomain(ua.account.aor))) transferButton.isEnabled = false } else { @@ -1840,7 +1838,7 @@ class MainActivity : AppCompatActivity() { callTitle.text = getString(R.string.outgoing_call_to_dots) else callTitle.text = getString(R.string.incoming_call_from_dots) - callUri.setText(Utils.friendlyUri(Utils.contactName(call.peerUri), + callUri.setText(Utils.friendlyUri(Contact.contactName(call.peerUri), Utils.aorDomain(ua.account.aor))) transferButton.isEnabled = true } @@ -1973,21 +1971,6 @@ class MainActivity : AppCompatActivity() { i.putExtras(b) startActivity(i) } - "android contacts" -> { - val i = Intent(this, AndroidContactsActivity::class.java) - val b = Bundle() - b.putString("aor", activity[1]) - i.putExtras(b) - startActivity(i) - } - "android contact" -> { - val i = Intent(this, AndroidContactActivity::class.java) - val b = Bundle() - b.putString("aor", activity[1]) - b.putInt("index", activity[2].toInt()) - i.putExtras(b) - startActivity(i) - } "chats" -> { val i = Intent(this, ChatsActivity::class.java) val b = Bundle() diff --git a/app/src/main/kotlin/com/tutpro/baresip/MessageListAdapter.kt b/app/src/main/kotlin/com/tutpro/baresip/MessageListAdapter.kt index 619ab43f..e7038332 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/MessageListAdapter.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/MessageListAdapter.kt @@ -45,7 +45,7 @@ class MessageListAdapter(private val ctx: Context, private val rows: ArrayList Contact.name}) - BaresipService.contactNames.addAll(AndroidContact.contacts().map{AndroidContact -> AndroidContact.name}) - BaresipService.contactNames.sort() - } - - fun contactNames() : List { - return BaresipService.contactNames - } - fun setAvatar(ctx: Context, imageView: ImageView, textView: TextView, uri: String) { - val contact = ContactsActivity.findContact(uri) - if (contact != null) { - val avatarImage = contact.avatarImage - if (avatarImage != null) { - imageView.setImageBitmap(avatarImage) - } else { - textView.background.setTint(contact.color) - if (contact.name.isNotEmpty()) - textView.text = "${contact.name[0]}" - else - textView.text = "" - imageView.setImageBitmap(bitmapFromView(textView)) - } - } else { - val androidContact = AndroidContactsActivity.findContact(uri) - if (androidContact != null) { - val thumbnailUri = androidContact.thumbnailUri - if (thumbnailUri != null) { - imageView.setImageURI(thumbnailUri) + + when (val contact = Contact.findContact(uri)) { + + is Contact.BaresipContact -> { + val avatarImage = contact.avatarImage + if (avatarImage != null) { + imageView.setImageBitmap(avatarImage) } else { - textView.background.setTint(androidContact.color) - if (androidContact.name.isNotEmpty()) - textView.text = "${androidContact.name[0]}" + textView.background.setTint(contact.color) + if (contact.name.isNotEmpty()) + textView.text = "${contact.name[0]}" else textView.text = "" imageView.setImageBitmap(bitmapFromView(textView)) } - } else { + } + + is Contact.AndroidContact -> { + val thumbnailUri = contact.thumbnailUri + if (thumbnailUri != null) { + imageView.setImageURI(thumbnailUri) + } else { + textView.background.setTint(contact.color) + if (contact.name.isNotEmpty()) + textView.text = "${contact.name[0]}" + else + textView.text = "" + imageView.setImageBitmap(bitmapFromView(textView)) + } + } + + null -> { val bitmap = BitmapFactory.decodeResource(ctx.resources, R.drawable.person_image) imageView.setImageBitmap(bitmap) } + } } diff --git a/app/src/main/res/layout/activity_config.xml b/app/src/main/res/layout/activity_config.xml index c7fb3748..3ca12e2f 100644 --- a/app/src/main/res/layout/activity_config.xml +++ b/app/src/main/res/layout/activity_config.xml @@ -215,37 +215,27 @@ android:id="@+id/AudioSettingsTitle" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginBottom="16dp" + android:layout_marginBottom="12dp" android:textSize="18sp" android:textStyle="bold" android:text="@string/audio_settings" > - + + + - - - - - + android:paddingTop="8dp" > + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a92f18cb..f7bfd012 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -329,9 +329,7 @@ Size of transmitted video frames (width x height). Factory default is 800x600. Android Settings - Show Android Contacts - If checked, Android contacts are shown when Contacts - button is touched. + Chooses if baresip contacts, Android contacts, or both will be used. Debug If checked, provides debug and info level log messages to Logcat. SIP Trace @@ -356,8 +354,7 @@ Invalid contact name \'%1$s\' Contact \'%1$s\' already exists. Invalid SIP URI - Android - If checked, this contact is available also in Android contacts. + If checked, this contact is added to Android contacts. Profile image Contacts @@ -392,6 +389,9 @@ \u2022 %1$s Invalid SIP or tel URI \'%1$s\' + baresip + Android + Both Backup Restore