Added possibility to use Android contacts

This commit is contained in:
Juha Heinanen
2022-02-13 12:51:59 +02:00
parent de673b105b
commit 21714a82c4
31 changed files with 267 additions and 208 deletions
@@ -1,8 +1,9 @@
package com.tutpro.baresip package com.tutpro.baresip
import android.net.Uri
import java.util.ArrayList import java.util.ArrayList
class AndroidContact(val id: Long, var name: String, var color: Int, val thumbnailUri: String?) { class AndroidContact(val id: Long, var name: String, var color: Int, var thumbnailUri: Uri?) {
val uris = ArrayList<String>() val uris = ArrayList<String>()
@@ -1,21 +1,16 @@
package com.tutpro.baresip package com.tutpro.baresip
import android.Manifest
import android.app.Activity import android.app.Activity
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.widget.* import android.widget.ImageView
import androidx.activity.result.ActivityResultLauncher import android.widget.LinearLayout
import androidx.activity.result.contract.ActivityResultContracts import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.cardview.widget.CardView import androidx.cardview.widget.CardView
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.ActivityAndroidContactBinding import com.tutpro.baresip.databinding.ActivityAndroidContactBinding
class AndroidContactActivity : AppCompatActivity() { class AndroidContactActivity : AppCompatActivity() {
@@ -28,7 +23,6 @@ class AndroidContactActivity : AppCompatActivity() {
private lateinit var nameView: TextView private lateinit var nameView: TextView
private lateinit var ulAdapter: AndroidUriListAdapter private lateinit var ulAdapter: AndroidUriListAdapter
private lateinit var aor: String private lateinit var aor: String
private lateinit var requestPermissionsLauncher: ActivityResultLauncher<Array<String>>
private var index = 0 private var index = 0
private var color = 0 private var color = 0
@@ -49,7 +43,7 @@ class AndroidContactActivity : AppCompatActivity() {
cardImageAvatarView = binding.ImageAvatar cardImageAvatarView = binding.ImageAvatar
nameView = binding.Name nameView = binding.Name
val contact = AndroidContactsActivity.androidContacts[index] val contact = BaresipService.androidContacts[index]
val name = contact.name val name = contact.name
color = contact.color color = contact.color
id = contact.id id = contact.id
@@ -69,53 +63,6 @@ class AndroidContactActivity : AppCompatActivity() {
} }
override fun onStart() {
super.onStart()
requestPermissionsLauncher =
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) {}
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>,
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) {
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 { override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (BaresipService.activities.indexOf("android contact,$aor,$index") == -1) return true if (BaresipService.activities.indexOf("android contact,$aor,$index") == -1) return true
@@ -151,10 +98,10 @@ class AndroidContactActivity : AppCompatActivity() {
textAvatarView.text = "${name[0]}" textAvatarView.text = "${name[0]}"
} }
private fun showImageAvatar(thumbNailUri: String) { private fun showImageAvatar(thumbNailUri: Uri) {
textAvatarView.visibility = View.GONE textAvatarView.visibility = View.GONE
cardAvatarView.visibility = View.VISIBLE cardAvatarView.visibility = View.VISIBLE
cardImageAvatarView.setImageURI(thumbNailUri.toUri()) cardImageAvatarView.setImageURI(thumbNailUri)
} }
} }
@@ -11,7 +11,6 @@ import android.widget.ArrayAdapter
import android.widget.ImageView import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import androidx.core.content.ContextCompat.startActivity import androidx.core.content.ContextCompat.startActivity
import androidx.core.net.toUri
class AndroidContactListAdapter(private val ctx: Context, class AndroidContactListAdapter(private val ctx: Context,
private val rows: ArrayList<AndroidContact>, private val rows: ArrayList<AndroidContact>,
@@ -45,7 +44,7 @@ class AndroidContactListAdapter(private val ctx: Context,
val thumbNailUri = contact.thumbnailUri val thumbNailUri = contact.thumbnailUri
if (thumbNailUri != null) { if (thumbNailUri != null) {
viewHolder.imageAvatarView.setImageURI(thumbNailUri.toUri()) viewHolder.imageAvatarView.setImageURI(thumbNailUri)
} else { } else {
contact.color = Utils.randomColor() contact.color = Utils.randomColor()
viewHolder.textAvatarView.background.setTint(contact.color) viewHolder.textAvatarView.background.setTint(contact.color)
@@ -1,35 +1,64 @@
package com.tutpro.baresip package com.tutpro.baresip
import android.Manifest
import android.app.Activity import android.app.Activity
import android.content.* import android.content.*
import android.content.pm.PackageManager
import android.database.Cursor import android.database.Cursor
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.SystemClock import android.os.SystemClock
import android.provider.ContactsContract import android.provider.ContactsContract
import android.view.Menu import android.view.Menu
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import android.view.MenuItem 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 import com.tutpro.baresip.databinding.ActivityAndroidContactsBinding
class AndroidContactsActivity : AppCompatActivity() { class AndroidContactsActivity : AppCompatActivity() {
private lateinit var binding: ActivityAndroidContactsBinding private lateinit var binding: ActivityAndroidContactsBinding
private lateinit var layout: RelativeLayout
private lateinit var clAdapter: AndroidContactListAdapter private lateinit var clAdapter: AndroidContactListAdapter
private lateinit var aor: String private lateinit var aor: String
private var lastSwap: Long = 0 private var lastSwap: Long = 0
private lateinit var permissionsLauncher: ActivityResultLauncher<Array<String>>
private val permissions = arrayOf(Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS)
public override fun onCreate(savedInstanceState: Bundle?) { public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = ActivityAndroidContactsBinding.inflate(layoutInflater) binding = ActivityAndroidContactsBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
layout = binding.AndroidContactsView
aor = intent.getStringExtra("aor")!! aor = intent.getStringExtra("aor")!!
val listView = binding.androidContacts val listView = binding.androidContacts
getAndroidContacts(this) permissionsLauncher = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
clAdapter = AndroidContactListAdapter(this, androidContacts, aor) 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)
else
fetchAndroidContacts(this)
clAdapter = AndroidContactListAdapter(this, BaresipService.androidContacts, aor)
listView.adapter = clAdapter listView.adapter = clAdapter
Utils.addActivity("android contacts,$aor") Utils.addActivity("android contacts,$aor")
@@ -45,7 +74,7 @@ class AndroidContactsActivity : AppCompatActivity() {
when (item.itemId) { when (item.itemId) {
R.id.swapContactsIcon -> { R.id.swapIcon -> {
if (SystemClock.elapsedRealtime() - lastSwap > 1000) { if (SystemClock.elapsedRealtime() - lastSwap > 1000) {
lastSwap = SystemClock.elapsedRealtime() lastSwap = SystemClock.elapsedRealtime()
BaresipService.activities.remove("android contacts,$aor") BaresipService.activities.remove("android contacts,$aor")
@@ -76,46 +105,110 @@ class AndroidContactsActivity : AppCompatActivity() {
} }
private fun getAndroidContacts(ctx: Context) { override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>,
val projection = arrayOf(ContactsContract.Data.CONTACT_ID, ContactsContract.Data.DISPLAY_NAME, grandResults: IntArray) {
ContactsContract.Data.MIMETYPE, ContactsContract.Data.DATA1, super.onRequestPermissionsResult(requestCode, permissions, grandResults)
ContactsContract.Data.PHOTO_THUMBNAIL_URI) when (requestCode) {
val selection = CONTACT_PERMISSION_REQUEST_CODE -> {
ContactsContract.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE + "' OR " + var allowed = true
ContactsContract.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE + "'" for (res in grandResults)
Log.d(TAG, "******* selection = /$selection/") allowed = allowed && res == PackageManager.PERMISSION_GRANTED
val cur: Cursor? = ctx.contentResolver.query(ContactsContract.Data.CONTENT_URI, projection, if (allowed) {
selection, null, null) fetchAndroidContacts(this)
val contacts = HashMap<Long, AndroidContact>() clAdapter.notifyDataSetChanged()
while (cur != null && cur.moveToNext()) { } else {
val id = cur.getLong(0) when {
val name = cur.getString(1) // display name ActivityCompat.shouldShowRequestPermissionRationale(this,
val mime = cur.getString(2) // type of data Manifest.permission.READ_CONTACTS) -> {
val data = cur.getString(3) // data layout.showSnackBar(
val thumb = cur.getString(4) // thumbnail binding.root,
// Log.d(TAG, "****** $id - $name - $mime - $data - $thumb") getString(R.string.no_android_contacts),
val contact = if (contacts.containsKey(id)) Snackbar.LENGTH_INDEFINITE,
contacts[id]!! getString(R.string.ok)
else ) {
AndroidContact(id, name, 0, thumb) permissionsLauncher.launch(permissions)
if (mime == ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE) }
contact.uris.add("tel:$data") }
else if (mime == ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE) ActivityCompat.shouldShowRequestPermissionRationale(this,
contact.uris.add("sip:$data") Manifest.permission.WRITE_CONTACTS) -> {
else layout.showSnackBar(
continue binding.root,
if (!contacts.containsKey(id)) getString(R.string.no_android_contacts),
contacts[id] = contact Snackbar.LENGTH_INDEFINITE,
getString(R.string.ok)
) {
permissionsLauncher.launch(permissions)
}
}
else -> {
permissionsLauncher.launch(permissions)
}
}
}
}
} }
cur?.close()
androidContacts.clear()
for ((_, value) in contacts)
androidContacts.add(value)
return
} }
companion object { companion object {
val androidContacts = ArrayList<AndroidContact>()
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<Long, AndroidContact>()
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, 0, 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)
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
}
} }
} }
@@ -296,6 +296,7 @@ class BaresipService: Service() {
this.registerReceiver(bluetoothReceiver, filter) this.registerReceiver(bluetoothReceiver, filter)
} }
AndroidContactsActivity.fetchAndroidContacts(this)
} }
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
@@ -648,8 +649,7 @@ class BaresipService: Service() {
PendingIntent.getActivity(applicationContext, CALL_REQ_CODE, intent, PendingIntent.getActivity(applicationContext, CALL_REQ_CODE, intent,
PendingIntent.FLAG_UPDATE_CURRENT) PendingIntent.FLAG_UPDATE_CURRENT)
val nb = NotificationCompat.Builder(this, HIGH_CHANNEL_ID) val nb = NotificationCompat.Builder(this, HIGH_CHANNEL_ID)
val caller = Utils.friendlyUri(ContactsActivity.contactName(peerUri), val caller = Utils.friendlyUri(Utils.contactName(peerUri), Utils.aorDomain(aor))
Utils.aorDomain(aor))
nb.setSmallIcon(R.drawable.ic_stat_call) nb.setSmallIcon(R.drawable.ic_stat_call)
.setColor(ContextCompat.getColor(this, R.color.colorBaresip)) .setColor(ContextCompat.getColor(this, R.color.colorBaresip))
.setContentIntent(pi) .setContentIntent(pi)
@@ -743,8 +743,7 @@ class BaresipService: Service() {
PendingIntent.getActivity(applicationContext, TRANSFER_REQ_CODE, PendingIntent.getActivity(applicationContext, TRANSFER_REQ_CODE,
intent, PendingIntent.FLAG_UPDATE_CURRENT) intent, PendingIntent.FLAG_UPDATE_CURRENT)
val nb = NotificationCompat.Builder(this, HIGH_CHANNEL_ID) val nb = NotificationCompat.Builder(this, HIGH_CHANNEL_ID)
val target = Utils.friendlyUri(ContactsActivity.contactName(ev[1]), val target = Utils.friendlyUri(Utils.contactName(ev[1]), Utils.aorDomain(aor))
Utils.aorDomain(aor))
nb.setSmallIcon(R.drawable.ic_stat_call) nb.setSmallIcon(R.drawable.ic_stat_call)
.setColor(ContextCompat.getColor(this, R.color.colorBaresip)) .setColor(ContextCompat.getColor(this, R.color.colorBaresip))
.setContentIntent(pi) .setContentIntent(pi)
@@ -820,8 +819,7 @@ class BaresipService: Service() {
} }
if (!Utils.isVisible()) { if (!Utils.isVisible()) {
if (missed) { if (missed) {
val caller = Utils.friendlyUri(ContactsActivity.contactName(call.peerUri), val caller = Utils.friendlyUri(Utils.contactName(call.peerUri), Utils.aorDomain(aor))
Utils.aorDomain(aor))
val intent = Intent(applicationContext, MainActivity::class.java) val intent = Intent(applicationContext, MainActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or intent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or
Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK
@@ -927,8 +925,7 @@ class BaresipService: Service() {
PendingIntent.getActivity(applicationContext, MESSAGE_REQ_CODE, intent, PendingIntent.getActivity(applicationContext, MESSAGE_REQ_CODE, intent,
PendingIntent.FLAG_UPDATE_CURRENT) PendingIntent.FLAG_UPDATE_CURRENT)
val nb = NotificationCompat.Builder(this, HIGH_CHANNEL_ID) val nb = NotificationCompat.Builder(this, HIGH_CHANNEL_ID)
val sender = Utils.friendlyUri(ContactsActivity.contactName(peer), val sender = Utils.friendlyUri(Utils.contactName(peer), Utils.aorDomain(ua.account.aor))
Utils.aorDomain(ua.account.aor))
nb.setSmallIcon(R.drawable.ic_stat_message) nb.setSmallIcon(R.drawable.ic_stat_message)
.setColor(ContextCompat.getColor(this, R.color.colorBaresip)) .setColor(ContextCompat.getColor(this, R.color.colorBaresip))
.setContentIntent(pi) .setContentIntent(pi)
@@ -1111,8 +1108,12 @@ class BaresipService: Service() {
} }
private fun requestAudioFocus(type: Int) { private fun requestAudioFocus(type: Int) {
if (audioFocusRequest != null && audioFocusRequest!!.audioAttributesCompat.contentType == type) if (audioFocusRequest != null) {
return if (audioFocusRequest!!.audioAttributesCompat.contentType == type)
return
else
abandonAudioFocus()
}
val attributes = AudioAttributesCompat.Builder() val attributes = AudioAttributesCompat.Builder()
.setUsage(AudioAttributesCompat.USAGE_VOICE_COMMUNICATION) .setUsage(AudioAttributesCompat.USAGE_VOICE_COMMUNICATION)
.setContentType(type) .setContentType(type)
@@ -1418,6 +1419,7 @@ class BaresipService: Service() {
val messageUpdate = MutableLiveData<Long>() val messageUpdate = MutableLiveData<Long>()
val registrationUpdate = MutableLiveData<Long>() val registrationUpdate = MutableLiveData<Long>()
val contacts = ArrayList<Contact>() val contacts = ArrayList<Contact>()
val androidContacts = ArrayList<AndroidContact>()
val chatTexts: MutableMap<String, String> = mutableMapOf() val chatTexts: MutableMap<String, String> = mutableMapOf()
val activities = mutableListOf<String>() val activities = mutableListOf<String>()
var dnsServers = listOf<InetAddress>() var dnsServers = listOf<InetAddress>()
@@ -2,7 +2,6 @@ package com.tutpro.baresip
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.graphics.BitmapFactory
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
@@ -12,8 +11,6 @@ import android.widget.ImageView
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import java.util.*
class CallListAdapter(private val ctx: Context, private val aor: String, private val rows: ArrayList<CallRow>) : class CallListAdapter(private val ctx: Context, private val aor: String, private val rows: ArrayList<CallRow>) :
ArrayAdapter<CallRow>(ctx, R.layout.call_row, rows) { ArrayAdapter<CallRow>(ctx, R.layout.call_row, rows) {
@@ -44,23 +41,7 @@ class CallListAdapter(private val ctx: Context, private val aor: String, private
val callRow = rows[position] val callRow = rows[position]
val contact = ContactsActivity.findContact(callRow.peerUri) Utils.setAvatar(ctx, viewHolder.imageAvatarView, viewHolder.textAvatarView, callRow.peerUri)
if (contact != null) {
val avatarImage = contact.avatarImage
if (avatarImage != null) {
viewHolder.imageAvatarView.setImageBitmap(avatarImage)
} 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))
}
} else {
val bitmap = BitmapFactory.decodeResource(ctx.resources, R.drawable.person_image)
viewHolder.imageAvatarView.setImageBitmap(bitmap)
}
viewHolder.directionsView.removeAllViews() viewHolder.directionsView.removeAllViews()
var count = 1 var count = 1
@@ -81,7 +62,7 @@ class CallListAdapter(private val ctx: Context, private val aor: String, private
if (count <= 3) if (count <= 3)
viewHolder.etcView.text = "" viewHolder.etcView.text = ""
val contactName = ContactsActivity.contactName(callRow.peerUri) val contactName = Utils.contactName(callRow.peerUri)
if (contactName.startsWith("sip:")) if (contactName.startsWith("sip:"))
viewHolder.peerURIView.text = Utils.friendlyUri(contactName, Utils.aorDomain(callRow.aor)) viewHolder.peerURIView.text = Utils.friendlyUri(contactName, Utils.aorDomain(callRow.aor))
else else
@@ -50,7 +50,7 @@ class CallsActivity : AppCompatActivity() {
listView.onItemClickListener = AdapterView.OnItemClickListener { _, _, pos, _ -> listView.onItemClickListener = AdapterView.OnItemClickListener { _, _, pos, _ ->
val peerUri = uaHistory[pos].peerUri val peerUri = uaHistory[pos].peerUri
var peerName = ContactsActivity.contactName(peerUri) var peerName = Utils.contactName(peerUri)
if (peerName.startsWith("sip:")) if (peerName.startsWith("sip:"))
peerName = Utils.friendlyUri(peerName, Utils.aorDomain(aor)) peerName = Utils.friendlyUri(peerName, Utils.aorDomain(aor))
val dialogClickListener = DialogInterface.OnClickListener { _, which -> val dialogClickListener = DialogInterface.OnClickListener { _, which ->
@@ -95,7 +95,7 @@ class CallsActivity : AppCompatActivity() {
listView.onItemLongClickListener = AdapterView.OnItemLongClickListener { _, _, pos, _ -> listView.onItemLongClickListener = AdapterView.OnItemLongClickListener { _, _, pos, _ ->
val peerUri = uaHistory[pos].peerUri val peerUri = uaHistory[pos].peerUri
val peerName = ContactsActivity.contactName(peerUri) val peerName = Utils.contactName(peerUri)
val dialogClickListener = DialogInterface.OnClickListener { _, which -> val dialogClickListener = DialogInterface.OnClickListener { _, which ->
when (which) { when (which) {
DialogInterface.BUTTON_NEGATIVE -> { DialogInterface.BUTTON_NEGATIVE -> {
@@ -63,7 +63,7 @@ class ChatActivity : AppCompatActivity() {
imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
this@ChatActivity.window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN) this@ChatActivity.window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN)
var chatPeer = ContactsActivity.contactName(peerUri) var chatPeer = Utils.contactName(peerUri)
if (chatPeer.startsWith("sip:")) if (chatPeer.startsWith("sip:"))
chatPeer = Utils.friendlyUri(chatPeer, Utils.aorDomain(aor)) chatPeer = Utils.friendlyUri(chatPeer, Utils.aorDomain(aor))
@@ -112,7 +112,7 @@ class ChatActivity : AppCompatActivity() {
} }
} }
val builder = AlertDialog.Builder(this@ChatActivity, R.style.Theme_AppCompat) val builder = AlertDialog.Builder(this@ChatActivity, R.style.Theme_AppCompat)
if (ContactsActivity.contactName(peerUri) == peerUri) if (Utils.contactName(peerUri) == peerUri)
with (builder) { with (builder) {
setMessage(String.format(getString(R.string.long_message_question), setMessage(String.format(getString(R.string.long_message_question),
chatPeer)) chatPeer))
@@ -1,17 +1,18 @@
package com.tutpro.baresip package com.tutpro.baresip
import android.content.Context import android.content.Context
import android.graphics.BitmapFactory
import android.graphics.Typeface import android.graphics.Typeface
import androidx.core.content.ContextCompat
import android.text.format.DateUtils.isToday import android.text.format.DateUtils.isToday
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.* import android.widget.ArrayAdapter
import android.widget.ImageView
import java.util.* import android.widget.LinearLayout
import android.widget.TextView
import androidx.core.content.ContextCompat
import java.text.DateFormat import java.text.DateFormat
import java.util.*
class ChatListAdapter(private val ctx: Context, private val rows: ArrayList<Message>) : class ChatListAdapter(private val ctx: Context, private val rows: ArrayList<Message>) :
ArrayAdapter<Message>(ctx, R.layout.message, rows) { ArrayAdapter<Message>(ctx, R.layout.message, rows) {
@@ -43,26 +44,7 @@ class ChatListAdapter(private val ctx: Context, private val rows: ArrayList<Mess
val message = rows[position] val message = rows[position]
val contact = ContactsActivity.findContact(message.peerUri) Utils.setAvatar(ctx, viewHolder.imageAvatarView, viewHolder.textAvatarView, message.peerUri)
val peer: String
if (contact != null) {
peer = contact.name
val avatarImage = contact.avatarImage
if (avatarImage != null) {
viewHolder.imageAvatarView.setImageBitmap(avatarImage)
} else {
viewHolder.textAvatarView.background.setTint(contact.color)
if (peer.isNotEmpty())
viewHolder.textAvatarView.text = "${peer[0]}"
else
viewHolder.textAvatarView.text = ""
viewHolder.imageAvatarView.setImageBitmap(Utils.bitmapFromView(viewHolder.textAvatarView))
}
} else {
peer = Utils.friendlyUri(message.peerUri, message.aor)
val bitmap = BitmapFactory.decodeResource(ctx.resources, R.drawable.person_image)
viewHolder.imageAvatarView.setImageBitmap(bitmap)
}
if ((message.direction == R.drawable.arrow_down_green) || if ((message.direction == R.drawable.arrow_down_green) ||
(message.direction == R.drawable.arrow_down_red)) (message.direction == R.drawable.arrow_down_red))
@@ -70,7 +52,11 @@ class ChatListAdapter(private val ctx: Context, private val rows: ArrayList<Mess
else else
viewHolder.layoutView.setBackgroundResource(R.drawable.message_out_bg) viewHolder.layoutView.setBackgroundResource(R.drawable.message_out_bg)
viewHolder.peerView.text = peer val peerName = Utils.contactName(message.peerUri)
viewHolder.peerView.text = if (peerName !=message.peerUri )
peerName
else
Utils.friendlyUri(message.peerUri, message.aor)
val cal = GregorianCalendar() val cal = GregorianCalendar()
cal.timeInMillis = message.timeStamp cal.timeInMillis = message.timeStamp
@@ -95,7 +95,7 @@ class ChatsActivity: AppCompatActivity() {
} }
val builder = AlertDialog.Builder(this@ChatsActivity, R.style.Theme_AppCompat) val builder = AlertDialog.Builder(this@ChatsActivity, R.style.Theme_AppCompat)
val peer = ContactsActivity.contactName(uaMessages[pos].peerUri) val peer = Utils.contactName(uaMessages[pos].peerUri)
if (peer.startsWith("sip:")) if (peer.startsWith("sip:"))
with (builder) { with (builder) {
setMessage(String.format(getString(R.string.long_chat_question), setMessage(String.format(getString(R.string.long_chat_question),
@@ -8,10 +8,8 @@ import android.content.Intent
import android.os.Bundle import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build import android.os.Build
import android.os.PowerManager import android.os.PowerManager
import android.provider.Settings
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
@@ -528,13 +526,6 @@ class ConfigActivity : AppCompatActivity() {
binding.AudioSettingsTitle.setOnClickListener { binding.AudioSettingsTitle.setOnClickListener {
startActivity(Intent(this, AudioActivity::class.java)) startActivity(Intent(this, AudioActivity::class.java))
} }
binding.AndroidSettings.setOnClickListener {
val i = Intent()
i.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
i.data = Uri.parse("package:${applicationContext.packageName}")
i.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY)
startActivity(i)
}
binding.DarkThemeTitle.setOnClickListener { binding.DarkThemeTitle.setOnClickListener {
Utils.alertView(this, getString(R.string.dark_theme), Utils.alertView(this, getString(R.string.dark_theme),
getString(R.string.dark_theme_help)) getString(R.string.dark_theme_help))
@@ -33,7 +33,6 @@ import com.tutpro.baresip.databinding.ActivityContactBinding
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.File import java.io.File
class ContactActivity : AppCompatActivity() { class ContactActivity : AppCompatActivity() {
private lateinit var binding: ActivityContactBinding private lateinit var binding: ActivityContactBinding
@@ -74,7 +74,7 @@ class ContactsActivity : AppCompatActivity() {
when (item.itemId) { when (item.itemId) {
R.id.swapContactsIcon -> { R.id.swapIcon -> {
if (SystemClock.elapsedRealtime() - lastClick > 1000) { if (SystemClock.elapsedRealtime() - lastClick > 1000) {
lastClick = SystemClock.elapsedRealtime() lastClick = SystemClock.elapsedRealtime()
BaresipService.activities.remove("contacts,$aor") BaresipService.activities.remove("contacts,$aor")
@@ -117,9 +117,10 @@ class ContactsActivity : AppCompatActivity() {
} }
fun findContact(uri: String): Contact? { fun findContact(uri: String): Contact? {
for (c in Contact.contacts()) for (c in Contact.contacts()) {
if (Utils.uriMatch(c.uri, uri)) if (Utils.uriMatch(c.uri, uri))
return c return c
}
return null return null
} }
@@ -1105,8 +1105,7 @@ class MainActivity : AppCompatActivity() {
val call = Call.ofCallp(callp)!! val call = Call.ofCallp(callp)!!
val titleView = View.inflate(this, R.layout.alert_title, null) as TextView val titleView = View.inflate(this, R.layout.alert_title, null) as TextView
titleView.text = getString(R.string.transfer_request) titleView.text = getString(R.string.transfer_request)
val target = Utils.friendlyUri(ContactsActivity.contactName(ev[1]), val target = Utils.friendlyUri(Utils.contactName(ev[1]), Utils.aorDomain(aor))
Utils.aorDomain(aor))
with(AlertDialog.Builder(this)) { with(AlertDialog.Builder(this)) {
setCustomTitle(titleView) setCustomTitle(titleView)
setMessage(String.format(getString(R.string.transfer_request_query), setMessage(String.format(getString(R.string.transfer_request_query),
@@ -1752,8 +1751,7 @@ class MainActivity : AppCompatActivity() {
val latest = NewCallHistory.aorLatestHistory(aor) val latest = NewCallHistory.aorLatestHistory(aor)
if (latest != null) if (latest != null)
callUri.setText( callUri.setText(
Utils.friendlyUri( Utils.friendlyUri(Utils.contactName(latest.peerUri), Utils.aorDomain(aor)
ContactsActivity.contactName(latest.peerUri), Utils.aorDomain(aor)
) )
) )
} }
@@ -1799,7 +1797,7 @@ class MainActivity : AppCompatActivity() {
else else
getString(R.string.outgoing_call_to_dots) getString(R.string.outgoing_call_to_dots)
callTimer.visibility = View.INVISIBLE callTimer.visibility = View.INVISIBLE
callUri.setText(Utils.friendlyUri(ContactsActivity.contactName(call.peerUri), callUri.setText(Utils.friendlyUri(Utils.contactName(call.peerUri),
Utils.aorDomain(ua.account.aor))) Utils.aorDomain(ua.account.aor)))
securityButton.visibility = View.INVISIBLE securityButton.visibility = View.INVISIBLE
callButton.visibility = View.INVISIBLE callButton.visibility = View.INVISIBLE
@@ -1814,7 +1812,7 @@ class MainActivity : AppCompatActivity() {
"incoming" -> { "incoming" -> {
callTitle.text = getString(R.string.incoming_call_from_dots) callTitle.text = getString(R.string.incoming_call_from_dots)
callTimer.visibility = View.INVISIBLE callTimer.visibility = View.INVISIBLE
callUri.setText(Utils.friendlyUri(ContactsActivity.contactName(call.peerUri), callUri.setText(Utils.friendlyUri(Utils.contactName(call.peerUri),
Utils.aorDomain(ua.account.aor))) Utils.aorDomain(ua.account.aor)))
callUri.setAdapter(null) callUri.setAdapter(null)
securityButton.visibility = View.INVISIBLE securityButton.visibility = View.INVISIBLE
@@ -1834,7 +1832,7 @@ class MainActivity : AppCompatActivity() {
} }
if (call.referTo != "") { if (call.referTo != "") {
callTitle.text = getString(R.string.transferring_call_to_dots) callTitle.text = getString(R.string.transferring_call_to_dots)
callUri.setText(Utils.friendlyUri(ContactsActivity.contactName(call.referTo), callUri.setText(Utils.friendlyUri(Utils.contactName(call.referTo),
Utils.aorDomain(ua.account.aor))) Utils.aorDomain(ua.account.aor)))
transferButton.isEnabled = false transferButton.isEnabled = false
} else { } else {
@@ -1842,7 +1840,7 @@ class MainActivity : AppCompatActivity() {
callTitle.text = getString(R.string.outgoing_call_to_dots) callTitle.text = getString(R.string.outgoing_call_to_dots)
else else
callTitle.text = getString(R.string.incoming_call_from_dots) callTitle.text = getString(R.string.incoming_call_from_dots)
callUri.setText(Utils.friendlyUri(ContactsActivity.contactName(call.peerUri), callUri.setText(Utils.friendlyUri(Utils.contactName(call.peerUri),
Utils.aorDomain(ua.account.aor))) Utils.aorDomain(ua.account.aor)))
transferButton.isEnabled = true transferButton.isEnabled = true
} }
@@ -45,7 +45,7 @@ class MessageListAdapter(private val ctx: Context, private val rows: ArrayList<M
val peer: String = if (down) { val peer: String = if (down) {
lp.setMargins(0, 10, 75, 10) lp.setMargins(0, 10, 75, 10)
viewHolder.layoutView.setBackgroundResource(R.drawable.message_in_bg) viewHolder.layoutView.setBackgroundResource(R.drawable.message_in_bg)
val contactName = ContactsActivity.contactName(message.peerUri) val contactName = Utils.contactName(message.peerUri)
if (contactName.startsWith("sip:") && if (contactName.startsWith("sip:") &&
(Utils.uriHostPart(message.peerUri) == Utils.uriHostPart(message.aor))) (Utils.uriHostPart(message.peerUri) == Utils.uriHostPart(message.aor)))
Utils.uriUserPart(message.peerUri) Utils.uriUserPart(message.peerUri)
@@ -7,6 +7,7 @@ import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.Bitmap.createScaledBitmap import android.graphics.Bitmap.createScaledBitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas import android.graphics.Canvas
import android.graphics.Color import android.graphics.Color
import android.net.* import android.net.*
@@ -22,6 +23,7 @@ import android.text.TextWatcher
import android.text.format.DateUtils import android.text.format.DateUtils
import android.view.View import android.view.View
import android.view.WindowManager import android.view.WindowManager
import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.ActivityResultLauncher
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
@@ -107,7 +109,7 @@ object Utils {
fun uriMatch(firstUri: String, secondUri: String): Boolean { fun uriMatch(firstUri: String, secondUri: String): Boolean {
if (firstUri.startsWith("tel:")) if (firstUri.startsWith("tel:"))
return firstUri == secondUri return firstUri == secondUri || firstUri.substringAfter(":") == uriUserPart(secondUri)
if (firstUri.startsWith("sip:")) if (firstUri.startsWith("sip:"))
return uriUserPart(firstUri) == uriUserPart(secondUri) && return uriUserPart(firstUri) == uriUserPart(secondUri) &&
uriHostPart(firstUri) == uriHostPart(secondUri) uriHostPart(firstUri) == uriHostPart(secondUri)
@@ -318,6 +320,49 @@ object Utils {
name.lines().size == 1 && !name.contains('"') name.lines().size == 1 && !name.contains('"')
} }
fun contactName(uri: String) : String {
val name = ContactsActivity.contactName(uri)
return if (name != uri)
name
else
AndroidContactsActivity.contactName(uri)
}
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)
} else {
textView.background.setTint(androidContact.color)
if (androidContact.name.isNotEmpty())
textView.text = "${androidContact.name[0]}"
else
textView.text = ""
imageView.setImageBitmap(bitmapFromView(textView))
}
} else {
val bitmap = BitmapFactory.decodeResource(ctx.resources, R.drawable.person_image)
imageView.setImageBitmap(bitmap)
}
}
}
@Suppress("unused") @Suppress("unused")
fun checkIfName(name: String): Boolean { fun checkIfName(name: String): Boolean {
if ((name.length < 2) || !name.first().isLetter()) return false if ((name.length < 2) || !name.first().isLetter()) return false
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M6.54,5c0.06,0.89 0.21,1.76 0.45,2.59l-1.2,1.2c-0.41,-1.2 -0.67,-2.47 -0.76,-3.79h1.51m9.86,12.02c0.85,0.24 1.72,0.39 2.6,0.45v1.49c-1.32,-0.09 -2.59,-0.35 -3.8,-0.75l1.2,-1.19M7.5,3H4c-0.55,0 -1,0.45 -1,1 0,9.39 7.61,17 17,17 0.55,0 1,-0.45 1,-1v-3.49c0,-0.55 -0.45,-1 -1,-1 -1.24,0 -2.45,-0.2 -3.57,-0.57 -0.1,-0.04 -0.21,-0.05 -0.31,-0.05 -0.26,0 -0.51,0.1 -0.71,0.29l-2.2,2.2c-2.83,-1.45 -5.15,-3.76 -6.59,-6.59l2.2,-2.2c0.28,-0.28 0.36,-0.67 0.25,-1.02C8.7,6.45 8.5,5.25 8.5,4c0,-0.55 -0.45,-1 -1,-1z"/>
</vector>
@@ -0,0 +1,20 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal"
android:autoMirrored="true">
<path
android:fillColor="@android:color/white"
android:pathData="M21,5c-1.11,-0.35 -2.33,-0.5 -3.5,-0.5c-1.95,0 -4.05,0.4 -5.5,1.5c-1.45,-1.1 -3.55,-1.5 -5.5,-1.5S2.45,4.9 1,6v14.65c0,0.25 0.25,0.5 0.5,0.5c0.1,0 0.15,-0.05 0.25,-0.05C3.1,20.45 5.05,20 6.5,20c1.95,0 4.05,0.4 5.5,1.5c1.35,-0.85 3.8,-1.5 5.5,-1.5c1.65,0 3.35,0.3 4.75,1.05c0.1,0.05 0.15,0.05 0.25,0.05c0.25,0 0.5,-0.25 0.5,-0.5V6C22.4,5.55 21.75,5.25 21,5zM21,18.5c-1.1,-0.35 -2.3,-0.5 -3.5,-0.5c-1.7,0 -4.15,0.65 -5.5,1.5V8c1.35,-0.85 3.8,-1.5 5.5,-1.5c1.2,0 2.4,0.15 3.5,0.5V18.5z"/>
<path
android:fillColor="@android:color/white"
android:pathData="M17.5,10.5c0.88,0 1.73,0.09 2.5,0.26V9.24C19.21,9.09 18.36,9 17.5,9c-1.7,0 -3.24,0.29 -4.5,0.83v1.66C14.13,10.85 15.7,10.5 17.5,10.5z"/>
<path
android:fillColor="@android:color/white"
android:pathData="M13,12.49v1.66c1.13,-0.64 2.7,-0.99 4.5,-0.99c0.88,0 1.73,0.09 2.5,0.26V11.9c-0.79,-0.15 -1.64,-0.24 -2.5,-0.24C15.8,11.66 14.26,11.96 13,12.49z"/>
<path
android:fillColor="@android:color/white"
android:pathData="M17.5,14.33c-1.7,0 -3.24,0.29 -4.5,0.83v1.66c1.13,-0.64 2.7,-0.99 4.5,-0.99c0.88,0 1.73,0.09 2.5,0.26v-1.52C19.21,14.41 18.36,14.33 17.5,14.33z"/>
</vector>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 402 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 278 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 522 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 773 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

+2 -2
View File
@@ -1,5 +1,5 @@
<vector android:autoMirrored="true" android:height="24dp" <vector android:autoMirrored="true" android:height="24dp"
android:tint="?attr/colorControlNormal" android:viewportHeight="24" android:tint="@android:color/white" android:viewportHeight="24"
android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#000" android:pathData="M20,15.5C18.8,15.5 17.5,15.3 16.4,14.9C16.3,14.9 16.2,14.9 16.1,14.9C15.8,14.9 15.6,15 15.4,15.2L13.2,17.4C10.4,15.9 8,13.6 6.6,10.8L8.8,8.6C9.1,8.3 9.2,7.9 9,7.6C8.7,6.5 8.5,5.2 8.5,4C8.5,3.5 8,3 7.5,3H4C3.5,3 3,3.5 3,4C3,13.4 10.6,21 20,21C20.5,21 21,20.5 21,20V16.5C21,16 20.5,15.5 20,15.5M5,5H6.5C6.6,5.9 6.8,6.8 7,7.6L5.8,8.8C5.4,7.6 5.1,6.3 5,5M19,19C17.7,18.9 16.4,18.6 15.2,18.2L16.4,17C17.2,17.2 18.1,17.4 19,17.4V19Z"/> <path android:fillColor="#000000" android:pathData="M20,15.5C18.8,15.5 17.5,15.3 16.4,14.9C16.3,14.9 16.2,14.9 16.1,14.9C15.8,14.9 15.6,15 15.4,15.2L13.2,17.4C10.4,15.9 8,13.6 6.6,10.8L8.8,8.6C9.1,8.3 9.2,7.9 9,7.6C8.7,6.5 8.5,5.2 8.5,4C8.5,3.5 8,3 7.5,3H4C3.5,3 3,3.5 3,4C3,13.4 10.6,21 20,21C20.5,21 21,20.5 21,20V16.5C21,16 20.5,15.5 20,15.5M5,5H6.5C6.6,5.9 6.8,6.8 7,7.6L5.8,8.8C5.4,7.6 5.1,6.3 5,5M19,19C17.7,18.9 16.4,18.6 15.2,18.2L16.4,17C17.2,17.2 18.1,17.4 19,17.4V19Z"/>
</vector> </vector>
@@ -1,5 +0,0 @@
<vector android:autoMirrored="true" android:height="48dp"
android:tint="?attr/colorControlNormal" android:viewportHeight="24"
android:viewportWidth="24" android:width="48dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M17.5,4.5c-1.95,0 -4.05,0.4 -5.5,1.5c-1.45,-1.1 -3.55,-1.5 -5.5,-1.5S2.45,4.9 1,6v14.65c0,0.65 0.73,0.45 0.75,0.45C3.1,20.45 5.05,20 6.5,20c1.95,0 4.05,0.4 5.5,1.5c1.35,-0.85 3.8,-1.5 5.5,-1.5c1.65,0 3.35,0.3 4.75,1.05C22.66,21.26 23,20.86 23,20.6V6C21.51,4.88 19.37,4.5 17.5,4.5zM21,18.5c-1.1,-0.35 -2.3,-0.5 -3.5,-0.5c-1.7,0 -4.15,0.65 -5.5,1.5V8c1.35,-0.85 3.8,-1.5 5.5,-1.5c1.2,0 2.4,0.15 3.5,0.5V18.5z"/>
</vector>
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/AndroidContactsView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin"
@@ -221,16 +221,6 @@
android:text="@string/audio_settings" > android:text="@string/audio_settings" >
</TextView> </TextView>
<TextView
android:id="@+id/AndroidSettings"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="12dp"
android:text="@string/android_settings"
android:textStyle="bold"
android:textSize="18sp">
</TextView>
<RelativeLayout <RelativeLayout
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
+1 -1
View File
@@ -52,7 +52,7 @@
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@id/aorSpinner" android:layout_below="@id/aorSpinner"
android:layout_marginTop="31dp" android:layout_marginTop="12dp"
android:orientation="horizontal" > android:orientation="horizontal" >
<TextView <TextView
+2 -2
View File
@@ -3,9 +3,9 @@
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<item <item
android:id="@+id/swapContactsIcon" android:id="@+id/swapIcon"
android:title="" android:title=""
app:showAsAction="always" app:showAsAction="always"
android:icon="@drawable/swap_contacts" /> android:icon="@drawable/ic_test_swap" />
</menu> </menu>
+1 -1
View File
@@ -520,7 +520,7 @@
Tallennustila-käyttöoikeutta.</string> Tallennustila-käyttöoikeutta.</string>
<string name="no_restore">Et voi palauttaa sovelluksen tietoja ilman <string name="no_restore">Et voi palauttaa sovelluksen tietoja ilman
Tallennustila-käyttöoikeutta.</string> Tallennustila-käyttöoikeutta.</string>
<string name="no_android_contacts">Et voi lisätä tai poistaa Android-yhteystietoja ilman <string name="no_android_contacts">Et voi käsitellä Androidin yhteystietoja ilman
Kontaktit-käyttöoikeutta.</string> Kontaktit-käyttöoikeutta.</string>
<string name="no_cameras">Sinulla ei ole yhtään tuettua video-kameraa.</string> <string name="no_cameras">Sinulla ei ole yhtään tuettua video-kameraa.</string>
<string name="show_password">Näytä salasana</string> <string name="show_password">Näytä salasana</string>
+1 -1
View File
@@ -475,7 +475,7 @@
<string name="no_video_calls">Grant \"Camera\" permission to make or answer video calls.</string> <string name="no_video_calls">Grant \"Camera\" permission to make or answer video calls.</string>
<string name="no_backup">You are not able create backup without \"Storage\" permission.</string> <string name="no_backup">You are not able create backup without \"Storage\" permission.</string>
<string name="no_restore">You are not able restore backup without \"Storage\" permission.</string> <string name="no_restore">You are not able restore backup without \"Storage\" permission.</string>
<string name="no_android_contacts">You are not able add or remove Android contacts without <string name="no_android_contacts">You are not able to access Android contacts without
\"Contacts\" permission.</string> \"Contacts\" permission.</string>
<string name="no_cameras">You don\'t have any supported video cameras.</string> <string name="no_cameras">You don\'t have any supported video cameras.</string>
<string name="show_password">Show Password</string> <string name="show_password">Show Password</string>