- Started work on image avatars (only Contact Activity done so far).
- Do not allow " character in contact's name, since it can be used as SIP diaplay name.
This commit is contained in:
@ -48,6 +48,7 @@ dependencies {
|
||||
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
implementation 'com.android.support:design:28.0.0'
|
||||
implementation 'com.android.support:cardview-v7:28.0.0'
|
||||
}
|
||||
|
||||
repositories {
|
||||
|
||||
@ -1,8 +1,14 @@
|
||||
package com.tutpro.baresip
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
|
||||
import java.io.File
|
||||
import java.util.ArrayList
|
||||
|
||||
class Contact(var name: String, var uri: String, var color: Int) {
|
||||
class Contact(var name: String, var uri: String, var color: Int, val id: Long) {
|
||||
|
||||
var avatarImage: Bitmap? = null
|
||||
|
||||
companion object {
|
||||
|
||||
@ -14,7 +20,8 @@ class Contact(var name: String, var uri: String, var color: Int) {
|
||||
|
||||
fun save(): Boolean {
|
||||
var contents = ""
|
||||
for (c in BaresipService.contacts) contents += "\"${c.name}\" <${c.uri}>\n"
|
||||
for (c in BaresipService.contacts) contents +=
|
||||
"\"${c.name}\" <${c.uri}>;id=${c.id};color=${c.color}\n"
|
||||
return Utils.putFileContents(BaresipService.filesPath + "/contacts",
|
||||
contents.toByteArray())
|
||||
}
|
||||
@ -25,16 +32,44 @@ class Contact(var name: String, var uri: String, var color: Int) {
|
||||
val contacts = String(content)
|
||||
Api.contacts_remove()
|
||||
BaresipService.contacts.clear()
|
||||
var contactNo = 0
|
||||
val baseId = System.currentTimeMillis()
|
||||
contacts.lines().forEach {
|
||||
val parts = it.split("\"")
|
||||
if (parts.size == 3) {
|
||||
contactNo++
|
||||
val name = parts[1]
|
||||
var uri = parts[2].trim()
|
||||
if (uri.startsWith("<"))
|
||||
uri = uri.substringAfter("<").substringBefore(">")
|
||||
// Currently no need to make baresip aware of the contact
|
||||
// Api.contact_add("\"$name\" $uri")
|
||||
BaresipService.contacts.add(Contact(name, uri, Utils.randomColor()))
|
||||
val uriParams = parts[2].trim()
|
||||
val uri = uriParams.substringAfter("<").substringBefore(">")
|
||||
val params = uriParams.substringAfter(">;")
|
||||
val colorValue = Utils.paramValue(params, "color" )
|
||||
var color = 0
|
||||
if (colorValue != "")
|
||||
color = colorValue.toInt()
|
||||
else
|
||||
color = Utils.randomColor()
|
||||
val idValue = Utils.paramValue(params, "id" )
|
||||
val id: Long
|
||||
if (idValue != "")
|
||||
id = idValue.toLong()
|
||||
else
|
||||
id = baseId + contactNo
|
||||
Log.d("Baresip", "Restoring contact $name, $uri, $color, $id")
|
||||
val contact = Contact(name, uri, color, id)
|
||||
val avatarFilePath = BaresipService.filesPath + "/$id.png"
|
||||
if (File(avatarFilePath).exists()) {
|
||||
try {
|
||||
contact.avatarImage = BitmapFactory.decodeFile(avatarFilePath)
|
||||
Log.d("Baresip", "Set avatarImage")
|
||||
if (contact.avatarImage == null)
|
||||
Log.d("Baresip", "Contact $id avatarImage is null")
|
||||
} catch (e: Exception) {
|
||||
Log.e("Baresip", "Could not read avatar image from '$id.img")
|
||||
}
|
||||
} else {
|
||||
Log.d("Baresip", "Contact $id does not have avatarImage")
|
||||
}
|
||||
BaresipService.contacts.add(contact)
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
||||
@ -2,41 +2,56 @@ package com.tutpro.baresip
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import android.os.Bundle
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.widget.*
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.support.v7.widget.CardView
|
||||
import android.view.View
|
||||
|
||||
import java.io.File
|
||||
|
||||
private const val READ_REQUEST_CODE = 42
|
||||
|
||||
class ContactActivity : AppCompatActivity() {
|
||||
|
||||
lateinit var avatarView: TextView
|
||||
lateinit var textAvatarView: TextView
|
||||
lateinit var cardAvatarView: CardView
|
||||
lateinit var imageAvatarView: ImageView
|
||||
lateinit var nameView: EditText
|
||||
lateinit var uriView: EditText
|
||||
|
||||
internal var new = false
|
||||
internal var newContact = false
|
||||
internal var newAvatar = ""
|
||||
|
||||
private var index = 0
|
||||
private var color = 0
|
||||
private var id: Long = 0
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_contact)
|
||||
|
||||
avatarView = findViewById(R.id.Avatar) as TextView
|
||||
textAvatarView = findViewById(R.id.Avatar) as TextView
|
||||
cardAvatarView = findViewById(R.id.CardAvatar) as CardView
|
||||
imageAvatarView = findViewById(R.id.ImageAvatar) as ImageView
|
||||
nameView = findViewById(R.id.Name) as EditText
|
||||
uriView = findViewById(R.id.Uri) as EditText
|
||||
|
||||
new = intent.getBooleanExtra("new", false)
|
||||
newContact = intent.getBooleanExtra("new", false)
|
||||
val uOrI: String
|
||||
|
||||
if (new) {
|
||||
if (newContact) {
|
||||
title = getString(R.string.new_contact)
|
||||
color = Utils.randomColor()
|
||||
val background = avatarView.background as GradientDrawable
|
||||
background.setColor(color)
|
||||
id = System.currentTimeMillis()
|
||||
showTextAvatar("?", color)
|
||||
nameView.setText("")
|
||||
nameView.hint = getString(R.string.contact_name)
|
||||
nameView.setSelection(nameView.text.length)
|
||||
@ -50,23 +65,83 @@ class ContactActivity : AppCompatActivity() {
|
||||
uOrI = uri
|
||||
} else {
|
||||
index = intent.getIntExtra("index", 0)
|
||||
val name = Contact.contacts()[index].name
|
||||
color = Contact.contacts()[index].color
|
||||
(avatarView.background as GradientDrawable).setColor(color)
|
||||
if (name.length > 0)
|
||||
avatarView.text = "${name[0]}"
|
||||
setTitle(name)
|
||||
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.contacts()[index].uri)
|
||||
uriView.setText(contact.uri)
|
||||
uOrI = index.toString()
|
||||
}
|
||||
|
||||
avatarView.setOnClickListener { view ->
|
||||
textAvatarView.setOnClickListener { _ ->
|
||||
|
||||
color = Utils.randomColor()
|
||||
(avatarView.background as GradientDrawable).setColor(color)
|
||||
showTextAvatar(textAvatarView.text.toString(), color)
|
||||
newAvatar = "text"
|
||||
|
||||
}
|
||||
|
||||
BaresipService.activities.add(0, "contact,$new,$uOrI")
|
||||
textAvatarView.setOnLongClickListener { _ ->
|
||||
|
||||
val intent = Intent(Intent.ACTION_GET_CONTENT).apply {
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
type = "image/*"
|
||||
}
|
||||
|
||||
startActivityForResult(intent, READ_REQUEST_CODE)
|
||||
|
||||
true
|
||||
|
||||
}
|
||||
|
||||
cardAvatarView.setOnClickListener { _ ->
|
||||
|
||||
color = Utils.randomColor()
|
||||
showTextAvatar(nameView.text.toString(), color)
|
||||
newAvatar = "text"
|
||||
|
||||
}
|
||||
|
||||
cardAvatarView.setOnLongClickListener { _ ->
|
||||
|
||||
val intent = Intent(Intent.ACTION_GET_CONTENT).apply {
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
type = "image/*"
|
||||
}
|
||||
|
||||
startActivityForResult(intent, READ_REQUEST_CODE)
|
||||
|
||||
true
|
||||
|
||||
}
|
||||
|
||||
BaresipService.activities.add(0, "contact,$newContact,$uOrI")
|
||||
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
|
||||
|
||||
if (requestCode == READ_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
|
||||
resultData?.data?.also { uri ->
|
||||
Log.d("Baresip", "Uri: $uri")
|
||||
try {
|
||||
val inputStream = baseContext.contentResolver.openInputStream(uri)
|
||||
val avatarImage = BitmapFactory.decodeStream(inputStream)
|
||||
showImageAvatar(avatarImage)
|
||||
if (Utils.saveBitmap(avatarImage, File(BaresipService.filesPath, "tmp.png")))
|
||||
newAvatar = "image"
|
||||
} catch (e: Exception) {
|
||||
Log.e("Baresip", "Could not read avatar image")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -99,7 +174,7 @@ class ContactActivity : AppCompatActivity() {
|
||||
return false
|
||||
}
|
||||
val alert: Boolean
|
||||
if (new)
|
||||
if (newContact)
|
||||
alert = ContactsActivity.nameExists(newName, true)
|
||||
else
|
||||
alert = (Contact.contacts()[index].name != newName) &&
|
||||
@ -123,7 +198,8 @@ class ContactActivity : AppCompatActivity() {
|
||||
return false
|
||||
}
|
||||
|
||||
if (new) {
|
||||
val contact: Contact
|
||||
if (newContact) {
|
||||
if (Contact.contacts().size >= Contact.CONTACTS_SIZE) {
|
||||
Utils.alertView(this, getString(R.string.notice),
|
||||
String.format(getString(R.string.contacts_exceeded),
|
||||
@ -131,15 +207,33 @@ class ContactActivity : AppCompatActivity() {
|
||||
BaresipService.activities.removeAt(0)
|
||||
return true
|
||||
} else {
|
||||
Contact.contacts().add(Contact(newName, newUri, color))
|
||||
contact = Contact(newName, newUri, color, id)
|
||||
Contact.contacts().add(contact)
|
||||
}
|
||||
} else {
|
||||
Contact.contacts()[index].uri = newUri
|
||||
Contact.contacts()[index].name = newName
|
||||
Contact.contacts()[index].color = color
|
||||
contact = Contact.contacts()[index]
|
||||
contact.uri = newUri
|
||||
contact.name = newName
|
||||
contact.color = color
|
||||
}
|
||||
|
||||
when (newAvatar) {
|
||||
"text" -> {
|
||||
if (contact.avatarImage != null) {
|
||||
contact.avatarImage = null
|
||||
Utils.deleteFile(File(BaresipService.filesPath, "${contact.id}.png"))
|
||||
}
|
||||
}
|
||||
"image" -> {
|
||||
contact.avatarImage = (imageAvatarView.drawable as BitmapDrawable).bitmap
|
||||
Utils.deleteFile(File(BaresipService.filesPath, "${contact.id}.png"))
|
||||
File(BaresipService.filesPath, "tmp.png")
|
||||
.renameTo(File(BaresipService.filesPath, "${contact.id}.png"))
|
||||
}
|
||||
}
|
||||
|
||||
Contact.contacts().sortBy { Contact -> Contact.name }
|
||||
|
||||
Contact.save()
|
||||
|
||||
i.putExtra("name", newName)
|
||||
@ -167,4 +261,21 @@ class ContactActivity : AppCompatActivity() {
|
||||
|
||||
}
|
||||
|
||||
private fun showTextAvatar(name: String, color: Int) {
|
||||
textAvatarView.visibility = View.VISIBLE
|
||||
cardAvatarView.visibility = View.GONE
|
||||
imageAvatarView.visibility = View.GONE
|
||||
(textAvatarView.background as GradientDrawable).setColor(color)
|
||||
if (name.isNotEmpty())
|
||||
textAvatarView.text = "${name[0]}"
|
||||
else
|
||||
textAvatarView.text = ""
|
||||
}
|
||||
|
||||
private fun showImageAvatar(image: Bitmap) {
|
||||
textAvatarView.visibility = View.GONE
|
||||
cardAvatarView.visibility = View.VISIBLE
|
||||
imageAvatarView.visibility = View.VISIBLE
|
||||
imageAvatarView.setImageBitmap(image)
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,6 +14,9 @@ import android.widget.ArrayAdapter
|
||||
import android.widget.ImageButton
|
||||
import android.widget.TextView
|
||||
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
|
||||
import java.util.*
|
||||
|
||||
class ContactListAdapter(private val cxt: Context, private val rows: ArrayList<Contact>,
|
||||
@ -72,6 +75,16 @@ class ContactListAdapter(private val cxt: Context, private val rows: ArrayList<C
|
||||
val dialogClickListener = DialogInterface.OnClickListener { _, which ->
|
||||
when (which) {
|
||||
DialogInterface.BUTTON_POSITIVE -> {
|
||||
val contact = Contact.contacts()[position]
|
||||
val id = contact.id
|
||||
val avatarFile = File(BaresipService.filesPath, "$id.img")
|
||||
if (avatarFile.exists()) {
|
||||
try {
|
||||
avatarFile.delete()
|
||||
} catch (e: IOException) {
|
||||
Log.e("Baresip", "Could not delete file '$id.img")
|
||||
}
|
||||
}
|
||||
Contact.contacts().removeAt(position)
|
||||
Contact.save()
|
||||
this.notifyDataSetChanged()
|
||||
|
||||
@ -18,6 +18,7 @@ import android.text.TextWatcher
|
||||
import android.widget.*
|
||||
import android.view.*
|
||||
|
||||
import java.io.File
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
@ -966,8 +967,12 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
private fun backup(password: String) {
|
||||
val files = arrayOf("accounts", "calls", "config", "contacts", "messages", "uuid",
|
||||
val files = arrayListOf("accounts", "calls", "config", "contacts", "messages", "uuid",
|
||||
"zrtp_cache.dat", "zrtp_zid", "cert.pem", "ca_cert", "ca_certs.crt")
|
||||
File(BaresipService.filesPath).walk().forEach {
|
||||
if (it.name.endsWith(".png")) files.add(it.name)
|
||||
}
|
||||
Log.d("Baresip", "Backing up files $files")
|
||||
val backupFilePath = BaresipService.downloadsPath + "/baresip.bs"
|
||||
val zipFilePath = BaresipService.filesPath + "/baresip.zip"
|
||||
if (!Utils.zip(files, "baresip.zip")) {
|
||||
@ -984,7 +989,7 @@ class MainActivity : AppCompatActivity() {
|
||||
return
|
||||
}
|
||||
Utils.alertView(this, getString(R.string.info), getString(R.string.backed_up))
|
||||
Utils.deleteFile(zipFilePath)
|
||||
Utils.deleteFile(File(zipFilePath))
|
||||
}
|
||||
|
||||
private fun restore(password: String) {
|
||||
@ -1005,7 +1010,7 @@ class MainActivity : AppCompatActivity() {
|
||||
"Failed to unzip file 'baresip.zip'")
|
||||
return
|
||||
}
|
||||
Utils.deleteFile(zipFilePath)
|
||||
Utils.deleteFile(File(zipFilePath))
|
||||
Log.d("Baresip", "Showing restart dialog")
|
||||
val restartDialog = AlertDialog.Builder(this)
|
||||
restartDialog.setMessage(getString(R.string.restored))
|
||||
|
||||
@ -6,6 +6,8 @@ import android.content.Context
|
||||
import android.support.v7.app.AlertDialog
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Bitmap.createScaledBitmap
|
||||
import android.graphics.Color
|
||||
import android.net.LinkAddress
|
||||
import android.os.Bundle
|
||||
@ -104,7 +106,7 @@ object Utils {
|
||||
checkPort(parts[1])
|
||||
}
|
||||
|
||||
fun checkE164Number(no: String): Boolean {
|
||||
private fun checkE164Number(no: String): Boolean {
|
||||
return Regex("^[+][1-9][0-9]{0,14}\$").matches(no)
|
||||
}
|
||||
|
||||
@ -121,7 +123,7 @@ object Utils {
|
||||
checkIpV6(bracketedIp.substring(1, bracketedIp.length - 2))
|
||||
}
|
||||
|
||||
fun checkIp(ip: String): Boolean {
|
||||
private fun checkIp(ip: String): Boolean {
|
||||
return checkIpV4(ip) || checkIpV6(ip)
|
||||
}
|
||||
|
||||
@ -220,7 +222,7 @@ object Utils {
|
||||
|
||||
fun checkName(name: String): Boolean {
|
||||
return name.isNotEmpty() && name == String(name.toByteArray(), Charsets.UTF_8) &&
|
||||
name.lines().size == 1
|
||||
name.lines().size == 1 && !name.contains('"')
|
||||
}
|
||||
|
||||
fun checkIfName(name: String): Boolean {
|
||||
@ -312,7 +314,6 @@ object Utils {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun checkPermission(ctx: Context, permission: String) : Boolean {
|
||||
return ContextCompat.checkSelfPermission(ctx, permission) == PackageManager.PERMISSION_GRANTED
|
||||
}
|
||||
@ -343,11 +344,13 @@ object Utils {
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteFile(filePath: String) {
|
||||
val file = File(filePath)
|
||||
fun deleteFile(file: File) {
|
||||
if (file.exists()) {
|
||||
Log.d("Baresip", "Deleting file '$filePath'")
|
||||
file.delete()
|
||||
try {
|
||||
file.delete()
|
||||
} catch (e: IOException) {
|
||||
Log.e("Baresip", "Could not delete file ${file.absolutePath}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -374,6 +377,22 @@ object Utils {
|
||||
return true
|
||||
}
|
||||
|
||||
fun saveBitmap(bitmap: Bitmap, file: File): Boolean {
|
||||
if (file.exists()) file.delete()
|
||||
try {
|
||||
val out = FileOutputStream(file)
|
||||
val scaledBitmap = createScaledBitmap (bitmap, 96, 96, true)
|
||||
scaledBitmap.compress(Bitmap.CompressFormat.PNG, 100, out)
|
||||
out.flush()
|
||||
out.close()
|
||||
Log.e("Baresip", "Saved bitmap to ${file.absolutePath} of length ${file.length()}")
|
||||
} catch (e: Exception) {
|
||||
Log.e("Baresip", "Failed to save bitmap to ${file.absolutePath}")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
class Crypto(val salt: ByteArray, val iter: Int, val iv: ByteArray, val data: ByteArray):
|
||||
Serializable {
|
||||
val serialVersionUID = -29238082928391L
|
||||
@ -449,7 +468,7 @@ object Utils {
|
||||
return plainData
|
||||
}
|
||||
|
||||
fun zip(fileNames: Array<String>, zipFileName: String): Boolean {
|
||||
fun zip(fileNames: ArrayList<String>, zipFileName: String): Boolean {
|
||||
val zipFilePath = BaresipService.filesPath + "/" + zipFileName
|
||||
try {
|
||||
ZipOutputStream(BufferedOutputStream(FileOutputStream(zipFilePath))).use { out ->
|
||||
@ -498,7 +517,7 @@ object Utils {
|
||||
return false
|
||||
}
|
||||
(allFiles - zipFiles).iterator().forEach {
|
||||
deleteFile(BaresipService.filesPath + "/$it")
|
||||
deleteFile(File(BaresipService.filesPath, "$it"))
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/ContactView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@ -8,21 +8,40 @@
|
||||
android:paddingRight="16dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingBottom="24dp"
|
||||
android:orientation="vertical" >
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/Avatar"
|
||||
android:layout_width="96dp"
|
||||
android:layout_height="96dp"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:scaleType="centerInside"
|
||||
android:background="@drawable/circle"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="36dp"
|
||||
android:textSize="24sp"
|
||||
android:textSize="72sp"
|
||||
android:textStyle="bold"
|
||||
android:textAllCaps="true"
|
||||
android:textColor="@android:color/white"
|
||||
android:gravity="center" >
|
||||
</TextView>
|
||||
|
||||
<android.support.v7.widget.CardView
|
||||
android:id="@+id/CardAvatar"
|
||||
android:layout_width="96dp"
|
||||
android:layout_height="96dp"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:elevation="12dp"
|
||||
app:cardCornerRadius="48dp" >
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ImageAvatar"
|
||||
android:layout_height="90dp"
|
||||
android:layout_width="96dp"
|
||||
android:adjustViewBounds="true"
|
||||
android:scaleType="centerCrop" >
|
||||
</ImageView>
|
||||
|
||||
</android.support.v7.widget.CardView>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/NameTitle"
|
||||
android:layout_width="wrap_content"
|
||||
|
||||
Reference in New Issue
Block a user