diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3c6a8c2c..ae86dd8c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,29 @@
Changelog
==========
+Version 4.5.0 *(2018-09-28)*
+----------------------------
+
+ * Added a simple dialpad
+ * Do not allow creating contacts under Signal or Telegram contact source
+ * Allow copying phone numbers into clipboard by long pressing them on the View screen
+ * Properly handle intents adding numbers to existing contacts
+ * Many other smaller improvements and bugfixes
+
+Version 4.4.0 *(2018-09-04)*
+----------------------------
+
+ * Added support for custom phone number/email/address types
+ * Added IM field
+ * Fixed some exporting/importing issues
+ * Improved duplicate filtering
+
+Version 4.3.0 *(2018-08-24)*
+----------------------------
+
+ * Reworked contact exporting/importing from scratch, use ez-vcard for parsing files
+ * Couple stability and translation improvements
+
Version 4.2.2 *(2018-08-13)*
----------------------------
diff --git a/app/build.gradle b/app/build.gradle
index dd3e3290..63ebf515 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -4,14 +4,14 @@ apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 28
- buildToolsVersion "28.0.2"
+ buildToolsVersion "28.0.3"
defaultConfig {
applicationId "com.simplemobiletools.contacts"
minSdkVersion 16
targetSdkVersion 28
- versionCode 29
- versionName "4.2.2"
+ versionCode 32
+ versionName "4.5.0"
setProperty("archivesBaseName", "contacts")
}
@@ -40,19 +40,12 @@ android {
}
}
-ext {
- leakCanaryVersion = '1.5.4'
-}
-
dependencies {
- implementation 'com.simplemobiletools:commons:4.6.15'
+ implementation 'com.simplemobiletools:commons:4.8.0'
implementation 'joda-time:joda-time:2.9.9'
implementation 'com.facebook.stetho:stetho:1.5.0'
- implementation 'com.android.support.constraint:constraint-layout:1.1.2'
+ implementation 'com.android.support.constraint:constraint-layout:1.1.3'
compile 'com.googlecode.ez-vcard:ez-vcard:0.10.4'
-
- debugImplementation "com.squareup.leakcanary:leakcanary-android:$leakCanaryVersion"
- releaseImplementation "com.squareup.leakcanary:leakcanary-android-no-op:$leakCanaryVersion"
}
Properties props = new Properties()
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 4422660d..998d518a 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -14,6 +14,10 @@
+
+
@@ -114,15 +118,6 @@
android:name=".activities.EditContactActivity"
android:parentActivityName=".activities.MainActivity">
-
-
-
-
-
-
-
-
-
@@ -159,6 +154,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
R.string.mobile
- ContactsContract.CommonDataKinds.Phone.TYPE_HOME -> R.string.home
- ContactsContract.CommonDataKinds.Phone.TYPE_WORK -> R.string.work
- ContactsContract.CommonDataKinds.Phone.TYPE_MAIN -> R.string.main_number
- ContactsContract.CommonDataKinds.Phone.TYPE_FAX_WORK -> R.string.work_fax
- ContactsContract.CommonDataKinds.Phone.TYPE_FAX_HOME -> R.string.home_fax
- ContactsContract.CommonDataKinds.Phone.TYPE_PAGER -> R.string.pager
- else -> R.string.other
+ fun getPhoneNumberTypeText(type: Int, label: String): String {
+ return if (type == ContactsContract.CommonDataKinds.BaseTypes.TYPE_CUSTOM) {
+ label
+ } else {
+ getString(when (type) {
+ ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE -> R.string.mobile
+ ContactsContract.CommonDataKinds.Phone.TYPE_HOME -> R.string.home
+ ContactsContract.CommonDataKinds.Phone.TYPE_WORK -> R.string.work
+ ContactsContract.CommonDataKinds.Phone.TYPE_MAIN -> R.string.main_number
+ ContactsContract.CommonDataKinds.Phone.TYPE_FAX_WORK -> R.string.work_fax
+ ContactsContract.CommonDataKinds.Phone.TYPE_FAX_HOME -> R.string.home_fax
+ ContactsContract.CommonDataKinds.Phone.TYPE_PAGER -> R.string.pager
+ else -> R.string.other
+ })
+ }
}
- fun getEmailTextId(type: Int) = when (type) {
- ContactsContract.CommonDataKinds.Email.TYPE_HOME -> R.string.home
- ContactsContract.CommonDataKinds.Email.TYPE_WORK -> R.string.work
- ContactsContract.CommonDataKinds.Email.TYPE_MOBILE -> R.string.mobile
- else -> R.string.other
+ fun getEmailTypeText(type: Int, label: String): String {
+ return if (type == ContactsContract.CommonDataKinds.BaseTypes.TYPE_CUSTOM) {
+ label
+ } else {
+ getString(when (type) {
+ ContactsContract.CommonDataKinds.Email.TYPE_HOME -> R.string.home
+ ContactsContract.CommonDataKinds.Email.TYPE_WORK -> R.string.work
+ ContactsContract.CommonDataKinds.Email.TYPE_MOBILE -> R.string.mobile
+ else -> R.string.other
+ })
+ }
}
- fun getAddressTextId(type: Int) = when (type) {
- ContactsContract.CommonDataKinds.StructuredPostal.TYPE_HOME -> R.string.home
- ContactsContract.CommonDataKinds.StructuredPostal.TYPE_WORK -> R.string.work
- else -> R.string.other
+ fun getAddressTypeText(type: Int, label: String): String {
+ return if (type == ContactsContract.CommonDataKinds.BaseTypes.TYPE_CUSTOM) {
+ label
+ } else {
+ getString(when (type) {
+ ContactsContract.CommonDataKinds.StructuredPostal.TYPE_HOME -> R.string.home
+ ContactsContract.CommonDataKinds.StructuredPostal.TYPE_WORK -> R.string.work
+ else -> R.string.other
+ })
+ }
+ }
+
+ fun getIMTypeText(type: Int, label: String): String {
+ return if (type == ContactsContract.CommonDataKinds.Im.PROTOCOL_CUSTOM) {
+ label
+ } else {
+ getString(when (type) {
+ ContactsContract.CommonDataKinds.Im.PROTOCOL_AIM -> R.string.aim
+ ContactsContract.CommonDataKinds.Im.PROTOCOL_MSN -> R.string.windows_live
+ ContactsContract.CommonDataKinds.Im.PROTOCOL_YAHOO -> R.string.yahoo
+ ContactsContract.CommonDataKinds.Im.PROTOCOL_SKYPE -> R.string.skype
+ ContactsContract.CommonDataKinds.Im.PROTOCOL_QQ -> R.string.qq
+ ContactsContract.CommonDataKinds.Im.PROTOCOL_GOOGLE_TALK -> R.string.hangouts
+ ContactsContract.CommonDataKinds.Im.PROTOCOL_ICQ -> R.string.icq
+ else -> R.string.jabber
+ })
+ }
}
fun getEventTextId(type: Int) = when (type) {
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/activities/DialpadActivity.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/activities/DialpadActivity.kt
new file mode 100644
index 00000000..11a45b07
--- /dev/null
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/activities/DialpadActivity.kt
@@ -0,0 +1,169 @@
+package com.simplemobiletools.contacts.activities
+
+import android.annotation.TargetApi
+import android.content.Intent
+import android.graphics.Color
+import android.os.Build
+import android.os.Bundle
+import android.text.InputType
+import android.view.KeyEvent
+import android.view.Menu
+import android.view.MenuItem
+import android.view.View
+import com.simplemobiletools.commons.extensions.*
+import com.simplemobiletools.commons.helpers.isLollipopPlus
+import com.simplemobiletools.contacts.R
+import com.simplemobiletools.contacts.adapters.ContactsAdapter
+import com.simplemobiletools.contacts.dialogs.CallConfirmationDialog
+import com.simplemobiletools.contacts.extensions.afterTextChanged
+import com.simplemobiletools.contacts.extensions.callContact
+import com.simplemobiletools.contacts.extensions.config
+import com.simplemobiletools.contacts.extensions.startCallIntent
+import com.simplemobiletools.contacts.helpers.ContactsHelper
+import com.simplemobiletools.contacts.helpers.KEY_PHONE
+import com.simplemobiletools.contacts.helpers.LOCATION_DIALPAD
+import com.simplemobiletools.contacts.models.Contact
+import kotlinx.android.synthetic.main.activity_dialpad.*
+
+class DialpadActivity : SimpleActivity() {
+ var contacts = ArrayList()
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_dialpad)
+
+ dialpad_0_holder.setOnClickListener { dialpadPressed("0", it) }
+ dialpad_1.setOnClickListener { dialpadPressed("1", it) }
+ dialpad_2.setOnClickListener { dialpadPressed("2", it) }
+ dialpad_3.setOnClickListener { dialpadPressed("3", it) }
+ dialpad_4.setOnClickListener { dialpadPressed("4", it) }
+ dialpad_5.setOnClickListener { dialpadPressed("5", it) }
+ dialpad_6.setOnClickListener { dialpadPressed("6", it) }
+ dialpad_7.setOnClickListener { dialpadPressed("7", it) }
+ dialpad_8.setOnClickListener { dialpadPressed("8", it) }
+ dialpad_9.setOnClickListener { dialpadPressed("9", it) }
+ dialpad_0_holder.setOnLongClickListener { dialpadPressed("+", null); true }
+ dialpad_asterisk.setOnClickListener { dialpadPressed("*", it) }
+ dialpad_hashtag.setOnClickListener { dialpadPressed("#", it) }
+ dialpad_clear_char.setOnClickListener { clearChar(it) }
+ dialpad_clear_char.setOnLongClickListener { clearInput(); true }
+ dialpad_call_button.setOnClickListener { initCall() }
+ dialpad_input.afterTextChanged { dialpadValueChanged(it) }
+ ContactsHelper(this).getContacts { gotContacts(it) }
+ disableKeyboardPopping()
+
+ val callIcon = resources.getColoredDrawableWithColor(R.drawable.ic_phone_big, if (isBlackAndWhiteTheme()) Color.BLACK else Color.WHITE)
+ dialpad_call_button.setImageDrawable(callIcon)
+ dialpad_call_button.background.applyColorFilter(getAdjustedPrimaryColor())
+ }
+
+ override fun onResume() {
+ super.onResume()
+ updateTextColors(dialpad_holder)
+ dialpad_clear_char.applyColorFilter(config.textColor)
+ }
+
+ override fun onCreateOptionsMenu(menu: Menu): Boolean {
+ menuInflater.inflate(R.menu.menu_dialpad, menu)
+ return true
+ }
+
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+ when (item.itemId) {
+ R.id.add_number_to_contact -> addNumberToContact()
+ else -> return super.onOptionsItemSelected(item)
+ }
+ return true
+ }
+
+ private fun addNumberToContact() {
+ Intent().apply {
+ action = Intent.ACTION_INSERT_OR_EDIT
+ type = "vnd.android.cursor.item/contact"
+ putExtra(KEY_PHONE, dialpad_input.value)
+ if (resolveActivity(packageManager) != null) {
+ startActivity(this)
+ } else {
+ toast(R.string.no_app_found)
+ }
+ }
+ }
+
+ private fun dialpadPressed(char: String, view: View?) {
+ dialpad_input.dispatchKeyEvent(getKeyEvent(getCharKeyCode(char)))
+ view?.performHapticFeedback()
+ }
+
+ private fun clearChar(view: View) {
+ dialpad_input.dispatchKeyEvent(getKeyEvent(KeyEvent.KEYCODE_DEL))
+ view.performHapticFeedback()
+ }
+
+ private fun clearInput() {
+ dialpad_input.setText("")
+ }
+
+ private fun getKeyEvent(keyCode: Int) = KeyEvent(0, 0, KeyEvent.ACTION_DOWN, keyCode, 0)
+
+ private fun getCharKeyCode(char: String) = when (char) {
+ "0" -> KeyEvent.KEYCODE_0
+ "1" -> KeyEvent.KEYCODE_1
+ "2" -> KeyEvent.KEYCODE_2
+ "3" -> KeyEvent.KEYCODE_3
+ "4" -> KeyEvent.KEYCODE_4
+ "5" -> KeyEvent.KEYCODE_5
+ "6" -> KeyEvent.KEYCODE_6
+ "7" -> KeyEvent.KEYCODE_7
+ "8" -> KeyEvent.KEYCODE_8
+ "9" -> KeyEvent.KEYCODE_9
+ "*" -> KeyEvent.KEYCODE_STAR
+ "+" -> KeyEvent.KEYCODE_PLUS
+ else -> KeyEvent.KEYCODE_POUND
+ }
+
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ private fun disableKeyboardPopping() {
+ if (isLollipopPlus()) {
+ dialpad_input.showSoftInputOnFocus = false
+ } else {
+ dialpad_input.inputType = InputType.TYPE_NULL
+ }
+ }
+
+ private fun gotContacts(newContacts: ArrayList) {
+ contacts = newContacts
+ Contact.sorting = config.sorting
+ Contact.startWithSurname = config.startNameWithSurname
+ contacts.sort()
+ }
+
+ private fun dialpadValueChanged(text: String) {
+ (dialpad_list.adapter as? ContactsAdapter)?.finishActMode()
+ val filtered = contacts.filter { it.doesContainPhoneNumber(text) } as ArrayList
+
+ ContactsAdapter(this, filtered, null, LOCATION_DIALPAD, null, dialpad_list, dialpad_fastscroller, text) {
+ callContact(it as Contact)
+ }.apply {
+ addVerticalDividers(true)
+ dialpad_list.adapter = this
+ }
+
+ dialpad_fastscroller.setScrollToY(0)
+ dialpad_fastscroller.setViews(dialpad_list) {
+ val item = (dialpad_list.adapter as ContactsAdapter).contactItems.getOrNull(it)
+ dialpad_fastscroller.updateBubbleText(item?.getBubbleText() ?: "")
+ }
+ }
+
+ private fun initCall() {
+ val number = dialpad_input.value
+ if (number.isNotEmpty()) {
+ if (config.showCallConfirmation) {
+ CallConfirmationDialog(this, number) {
+ startCallIntent(number)
+ }
+ } else {
+ startCallIntent(number)
+ }
+ }
+ }
+}
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/activities/EditContactActivity.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/activities/EditContactActivity.kt
index 5acf55af..385f155a 100644
--- a/app/src/main/kotlin/com/simplemobiletools/contacts/activities/EditContactActivity.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/activities/EditContactActivity.kt
@@ -1,5 +1,6 @@
package com.simplemobiletools.contacts.activities
+import android.app.Activity
import android.app.DatePickerDialog
import android.content.ClipData
import android.content.ContentValues
@@ -13,6 +14,7 @@ import android.view.Menu
import android.view.MenuItem
import android.view.ViewGroup
import android.view.WindowManager
+import android.widget.EditText
import android.widget.ImageView
import android.widget.TextView
import com.simplemobiletools.commons.dialogs.RadioGroupDialog
@@ -21,6 +23,7 @@ import com.simplemobiletools.commons.helpers.PERMISSION_READ_CONTACTS
import com.simplemobiletools.commons.helpers.PERMISSION_WRITE_CONTACTS
import com.simplemobiletools.commons.models.RadioItem
import com.simplemobiletools.contacts.R
+import com.simplemobiletools.contacts.dialogs.CustomLabelDialog
import com.simplemobiletools.contacts.dialogs.SelectGroupsDialog
import com.simplemobiletools.contacts.extensions.*
import com.simplemobiletools.contacts.helpers.*
@@ -29,12 +32,12 @@ import kotlinx.android.synthetic.main.activity_edit_contact.*
import kotlinx.android.synthetic.main.item_edit_address.view.*
import kotlinx.android.synthetic.main.item_edit_email.view.*
import kotlinx.android.synthetic.main.item_edit_group.view.*
+import kotlinx.android.synthetic.main.item_edit_im.view.*
import kotlinx.android.synthetic.main.item_edit_phone_number.view.*
import kotlinx.android.synthetic.main.item_edit_website.view.*
import kotlinx.android.synthetic.main.item_event.view.*
import org.joda.time.DateTime
import org.joda.time.format.DateTimeFormat
-import java.util.*
class EditContactActivity : ContactActivity() {
private val INTENT_TAKE_PHOTO = 1
@@ -49,6 +52,8 @@ class EditContactActivity : ContactActivity() {
private var lastPhotoIntentUri: Uri? = null
private var isSaving = false
private var isThirdPartyIntent = false
+ private var highlightLastPhoneNumber = false
+ private var numberViewToColor: EditText? = null
private var originalContactSource = ""
override fun onCreate(savedInstanceState: Bundle?) {
@@ -57,7 +62,7 @@ class EditContactActivity : ContactActivity() {
supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_cross)
val action = intent.action
- isThirdPartyIntent = action == Intent.ACTION_EDIT || action == Intent.ACTION_INSERT_OR_EDIT || action == Intent.ACTION_INSERT
+ isThirdPartyIntent = action == Intent.ACTION_EDIT || action == Intent.ACTION_INSERT || action == ADD_NEW_CONTACT_NUMBER
val isFromSimpleContacts = intent.getBooleanExtra(IS_FROM_SIMPLE_CONTACTS, false)
if (isThirdPartyIntent && !isFromSimpleContacts) {
handlePermission(PERMISSION_READ_CONTACTS) {
@@ -118,7 +123,7 @@ class EditContactActivity : ContactActivity() {
private fun initContact() {
var contactId = intent.getIntExtra(CONTACT_ID, 0)
val action = intent.action
- if (contactId == 0 && action == Intent.ACTION_EDIT) {
+ if (contactId == 0 && (action == Intent.ACTION_EDIT || action == ADD_NEW_CONTACT_NUMBER)) {
val data = intent.data
if (data != null) {
val rawId = if (data.path.contains("lookup")) {
@@ -148,11 +153,20 @@ class EditContactActivity : ContactActivity() {
setupEditContact()
}
- if (contact!!.id == 0 && intent.extras?.containsKey(KEY_PHONE) == true && (action == Intent.ACTION_INSERT_OR_EDIT || action == Intent.ACTION_INSERT)) {
- val phoneNumber = intent.extras.get(KEY_PHONE)?.toString() ?: ""
- contact!!.phoneNumbers.add(PhoneNumber(phoneNumber, DEFAULT_PHONE_NUMBER_TYPE))
+ if ((contact!!.id == 0 && intent.extras != null && intent.extras.containsKey(KEY_PHONE) && action == Intent.ACTION_INSERT) || action == ADD_NEW_CONTACT_NUMBER) {
+ val phone = intent.extras.get(KEY_PHONE)
+ if (phone != null) {
+ val phoneNumber = phone.toString()
+ contact!!.phoneNumbers.add(PhoneNumber(phoneNumber, DEFAULT_PHONE_NUMBER_TYPE, ""))
+ if (phoneNumber.isNotEmpty() && action == ADD_NEW_CONTACT_NUMBER) {
+ highlightLastPhoneNumber = true
+ }
+ }
- contact!!.firstName = intent.extras.get(KEY_NAME)?.toString() ?: ""
+ val firstName = intent.extras.get(KEY_NAME)
+ if (firstName != null) {
+ contact!!.firstName = firstName.toString()
+ }
val data = intent.extras.getParcelableArrayList("data")
if (data != null) {
@@ -182,6 +196,7 @@ class EditContactActivity : ContactActivity() {
contact_numbers_image.applyColorFilter(textColor)
contact_emails_image.applyColorFilter(textColor)
contact_addresses_image.applyColorFilter(textColor)
+ contact_ims_image.applyColorFilter(textColor)
contact_events_image.applyColorFilter(textColor)
contact_notes_image.applyColorFilter(textColor)
contact_organization_image.applyColorFilter(textColor)
@@ -196,6 +211,8 @@ class EditContactActivity : ContactActivity() {
contact_emails_add_new.background.applyColorFilter(textColor)
contact_addresses_add_new.applyColorFilter(adjustedPrimaryColor)
contact_addresses_add_new.background.applyColorFilter(textColor)
+ contact_ims_add_new.applyColorFilter(adjustedPrimaryColor)
+ contact_ims_add_new.background.applyColorFilter(textColor)
contact_events_add_new.applyColorFilter(adjustedPrimaryColor)
contact_events_add_new.background.applyColorFilter(textColor)
contact_websites_add_new.applyColorFilter(adjustedPrimaryColor)
@@ -211,6 +228,7 @@ class EditContactActivity : ContactActivity() {
contact_numbers_add_new.setOnClickListener { addNewPhoneNumberField() }
contact_emails_add_new.setOnClickListener { addNewEmailField() }
contact_addresses_add_new.setOnClickListener { addNewAddressField() }
+ contact_ims_add_new.setOnClickListener { addNewIMField() }
contact_events_add_new.setOnClickListener { addNewEventField() }
contact_websites_add_new.setOnClickListener { addNewWebsiteField() }
contact_groups_add_new.setOnClickListener { showSelectGroupsDialog() }
@@ -225,6 +243,7 @@ class EditContactActivity : ContactActivity() {
}
updateTextColors(contact_scrollview)
+ numberViewToColor?.setTextColor(getAdjustedPrimaryColor())
wasActivityInitialized = true
invalidateOptionsMenu()
}
@@ -299,6 +318,11 @@ class EditContactActivity : ContactActivity() {
contact_addresses_holder.beVisibleIf(areAddressesVisible)
contact_addresses_add_new.beVisibleIf(areAddressesVisible)
+ val areIMsVisible = showFields and SHOW_IMS_FIELD != 0
+ contact_ims_image.beVisibleIf(areIMsVisible)
+ contact_ims_holder.beVisibleIf(areIMsVisible)
+ contact_ims_add_new.beVisibleIf(areIMsVisible)
+
val isOrganizationVisible = showFields and SHOW_ORGANIZATION_FIELD != 0
contact_organization_company.beVisibleIf(isOrganizationVisible)
contact_organization_job_position.beVisibleIf(isOrganizationVisible)
@@ -332,6 +356,7 @@ class EditContactActivity : ContactActivity() {
setupPhoneNumbers()
setupEmails()
setupAddresses()
+ setupIMs()
setupNotes()
setupOrganization()
setupWebsites()
@@ -361,7 +386,10 @@ class EditContactActivity : ContactActivity() {
numberHolder!!.apply {
contact_number.setText(number.value)
- setupPhoneNumberTypePicker(contact_number_type, number.type)
+ setupPhoneNumberTypePicker(contact_number_type, number.type, number.label)
+ if (highlightLastPhoneNumber && index == contact!!.phoneNumbers.size - 1) {
+ numberViewToColor = contact_number
+ }
}
}
}
@@ -376,7 +404,7 @@ class EditContactActivity : ContactActivity() {
emailHolder!!.apply {
contact_email.setText(email.value)
- setupEmailTypePicker(contact_email_type, email.type)
+ setupEmailTypePicker(contact_email_type, email.type, email.label)
}
}
}
@@ -391,7 +419,22 @@ class EditContactActivity : ContactActivity() {
addressHolder!!.apply {
contact_address.setText(address.value)
- setupAddressTypePicker(contact_address_type, address.type)
+ setupAddressTypePicker(contact_address_type, address.type, address.label)
+ }
+ }
+ }
+
+ private fun setupIMs() {
+ contact!!.IMs.forEachIndexed { index, IM ->
+ var imHolder = contact_ims_holder.getChildAt(index)
+ if (imHolder == null) {
+ imHolder = layoutInflater.inflate(R.layout.item_edit_im, contact_ims_holder, false)
+ contact_ims_holder.addView(imHolder)
+ }
+
+ imHolder!!.apply {
+ contact_im.setText(IM.value)
+ setupIMTypePicker(contact_im_type, IM.type, IM.label)
}
}
}
@@ -506,7 +549,7 @@ class EditContactActivity : ContactActivity() {
originalContactSource = if (hasContactPermissions()) config.lastUsedContactSource else SMT_PRIVATE
val organization = Organization("", "")
contact = Contact(0, "", "", "", "", "", "", "", ArrayList(), ArrayList(), ArrayList(), ArrayList(), originalContactSource, 0, 0, "",
- null, "", ArrayList(), organization, ArrayList())
+ null, "", ArrayList(), organization, ArrayList(), ArrayList(), ArrayList())
contact_source.text = getPublicContactSource(contact!!.source)
}
@@ -514,21 +557,28 @@ class EditContactActivity : ContactActivity() {
if (contact!!.phoneNumbers.isEmpty()) {
val numberHolder = contact_numbers_holder.getChildAt(0)
(numberHolder as? ViewGroup)?.contact_number_type?.apply {
- setupPhoneNumberTypePicker(this)
+ setupPhoneNumberTypePicker(this, DEFAULT_PHONE_NUMBER_TYPE, "")
}
}
if (contact!!.emails.isEmpty()) {
val emailHolder = contact_emails_holder.getChildAt(0)
(emailHolder as? ViewGroup)?.contact_email_type?.apply {
- setupEmailTypePicker(this)
+ setupEmailTypePicker(this, DEFAULT_EMAIL_TYPE, "")
}
}
if (contact!!.addresses.isEmpty()) {
val addressHolder = contact_addresses_holder.getChildAt(0)
(addressHolder as? ViewGroup)?.contact_address_type?.apply {
- setupAddressTypePicker(this)
+ setupAddressTypePicker(this, DEFAULT_ADDRESS_TYPE, "")
+ }
+ }
+
+ if (contact!!.IMs.isEmpty()) {
+ val IMHolder = contact_ims_holder.getChildAt(0)
+ (IMHolder as? ViewGroup)?.contact_im_type?.apply {
+ setupIMTypePicker(this, DEFAULT_IM_TYPE, "")
}
}
@@ -547,33 +597,42 @@ class EditContactActivity : ContactActivity() {
}
}
- private fun setupPhoneNumberTypePicker(numberTypeField: TextView, type: Int = DEFAULT_PHONE_NUMBER_TYPE) {
+ private fun setupPhoneNumberTypePicker(numberTypeField: TextView, type: Int, label: String) {
numberTypeField.apply {
- setText(getPhoneNumberTextId(type))
+ text = getPhoneNumberTypeText(type, label)
setOnClickListener {
showNumberTypePicker(it as TextView)
}
}
}
- private fun setupEmailTypePicker(emailTypeField: TextView, type: Int = DEFAULT_EMAIL_TYPE) {
+ private fun setupEmailTypePicker(emailTypeField: TextView, type: Int, label: String) {
emailTypeField.apply {
- setText(getEmailTextId(type))
+ text = getEmailTypeText(type, label)
setOnClickListener {
showEmailTypePicker(it as TextView)
}
}
}
- private fun setupAddressTypePicker(addressTypeField: TextView, type: Int = DEFAULT_ADDRESS_TYPE) {
+ private fun setupAddressTypePicker(addressTypeField: TextView, type: Int, label: String) {
addressTypeField.apply {
- setText(getAddressTextId(type))
+ text = getAddressTypeText(type, label)
setOnClickListener {
showAddressTypePicker(it as TextView)
}
}
}
+ private fun setupIMTypePicker(imTypeField: TextView, type: Int, label: String) {
+ imTypeField.apply {
+ text = getIMTypeText(type, label)
+ setOnClickListener {
+ showIMTypePicker(it as TextView)
+ }
+ }
+ }
+
private fun setupEventTypePicker(eventHolder: ViewGroup, type: Int = DEFAULT_EVENT_TYPE) {
eventHolder.contact_event_type.apply {
setText(getEventTextId(type))
@@ -641,12 +700,19 @@ class EditContactActivity : ContactActivity() {
RadioItem(CommonDataKinds.Phone.TYPE_FAX_WORK, getString(R.string.work_fax)),
RadioItem(CommonDataKinds.Phone.TYPE_FAX_HOME, getString(R.string.home_fax)),
RadioItem(CommonDataKinds.Phone.TYPE_PAGER, getString(R.string.pager)),
- RadioItem(CommonDataKinds.Phone.TYPE_OTHER, getString(R.string.other))
+ RadioItem(CommonDataKinds.Phone.TYPE_OTHER, getString(R.string.other)),
+ RadioItem(CommonDataKinds.Phone.TYPE_CUSTOM, getString(R.string.custom))
)
val currentNumberTypeId = getPhoneNumberTypeId(numberTypeField.value)
RadioGroupDialog(this, items, currentNumberTypeId) {
- numberTypeField.setText(getPhoneNumberTextId(it as Int))
+ if (it as Int == CommonDataKinds.Phone.TYPE_CUSTOM) {
+ CustomLabelDialog(this) {
+ numberTypeField.text = it
+ }
+ } else {
+ numberTypeField.text = getPhoneNumberTypeText(it, "")
+ }
}
}
@@ -655,12 +721,19 @@ class EditContactActivity : ContactActivity() {
RadioItem(CommonDataKinds.Email.TYPE_HOME, getString(R.string.home)),
RadioItem(CommonDataKinds.Email.TYPE_WORK, getString(R.string.work)),
RadioItem(CommonDataKinds.Email.TYPE_MOBILE, getString(R.string.mobile)),
- RadioItem(CommonDataKinds.Email.TYPE_OTHER, getString(R.string.other))
+ RadioItem(CommonDataKinds.Email.TYPE_OTHER, getString(R.string.other)),
+ RadioItem(CommonDataKinds.Email.TYPE_CUSTOM, getString(R.string.custom))
)
val currentEmailTypeId = getEmailTypeId(emailTypeField.value)
RadioGroupDialog(this, items, currentEmailTypeId) {
- emailTypeField.setText(getEmailTextId(it as Int))
+ if (it as Int == CommonDataKinds.Email.TYPE_CUSTOM) {
+ CustomLabelDialog(this) {
+ emailTypeField.text = it
+ }
+ } else {
+ emailTypeField.text = getEmailTypeText(it, "")
+ }
}
}
@@ -668,12 +741,44 @@ class EditContactActivity : ContactActivity() {
val items = arrayListOf(
RadioItem(CommonDataKinds.StructuredPostal.TYPE_HOME, getString(R.string.home)),
RadioItem(CommonDataKinds.StructuredPostal.TYPE_WORK, getString(R.string.work)),
- RadioItem(CommonDataKinds.StructuredPostal.TYPE_OTHER, getString(R.string.other))
+ RadioItem(CommonDataKinds.StructuredPostal.TYPE_OTHER, getString(R.string.other)),
+ RadioItem(CommonDataKinds.StructuredPostal.TYPE_CUSTOM, getString(R.string.custom))
)
val currentAddressTypeId = getAddressTypeId(addressTypeField.value)
RadioGroupDialog(this, items, currentAddressTypeId) {
- addressTypeField.setText(getAddressTextId(it as Int))
+ if (it as Int == CommonDataKinds.StructuredPostal.TYPE_CUSTOM) {
+ CustomLabelDialog(this) {
+ addressTypeField.text = it
+ }
+ } else {
+ addressTypeField.text = getAddressTypeText(it, "")
+ }
+ }
+ }
+
+ private fun showIMTypePicker(imTypeField: TextView) {
+ val items = arrayListOf(
+ RadioItem(CommonDataKinds.Im.PROTOCOL_AIM, getString(R.string.aim)),
+ RadioItem(CommonDataKinds.Im.PROTOCOL_MSN, getString(R.string.windows_live)),
+ RadioItem(CommonDataKinds.Im.PROTOCOL_YAHOO, getString(R.string.yahoo)),
+ RadioItem(CommonDataKinds.Im.PROTOCOL_SKYPE, getString(R.string.skype)),
+ RadioItem(CommonDataKinds.Im.PROTOCOL_QQ, getString(R.string.qq)),
+ RadioItem(CommonDataKinds.Im.PROTOCOL_GOOGLE_TALK, getString(R.string.hangouts)),
+ RadioItem(CommonDataKinds.Im.PROTOCOL_ICQ, getString(R.string.icq)),
+ RadioItem(CommonDataKinds.Im.PROTOCOL_JABBER, getString(R.string.jabber)),
+ RadioItem(CommonDataKinds.Im.PROTOCOL_CUSTOM, getString(R.string.custom))
+ )
+
+ val currentIMTypeId = getIMTypeId(imTypeField.value)
+ RadioGroupDialog(this, items, currentIMTypeId) {
+ if (it as Int == CommonDataKinds.Im.PROTOCOL_CUSTOM) {
+ CustomLabelDialog(this) {
+ imTypeField.text = it
+ }
+ } else {
+ imTypeField.text = getIMTypeText(it, "")
+ }
}
}
@@ -722,6 +827,7 @@ class EditContactActivity : ContactActivity() {
phoneNumbers = getFilledPhoneNumbers()
emails = getFilledEmails()
addresses = getFilledAddresses()
+ IMs = getFilledIMs()
events = getFilledEvents()
starred = if (isContactStarred()) 1 else 0
notes = contact_notes.value
@@ -752,9 +858,10 @@ class EditContactActivity : ContactActivity() {
val numberHolder = contact_numbers_holder.getChildAt(i)
val number = numberHolder.contact_number.value
val numberType = getPhoneNumberTypeId(numberHolder.contact_number_type.value)
+ val numberLabel = if (numberType == CommonDataKinds.Phone.TYPE_CUSTOM) numberHolder.contact_number_type.value else ""
if (number.isNotEmpty()) {
- phoneNumbers.add(PhoneNumber(number, numberType))
+ phoneNumbers.add(PhoneNumber(number, numberType, numberLabel))
}
}
return phoneNumbers
@@ -767,9 +874,10 @@ class EditContactActivity : ContactActivity() {
val emailHolder = contact_emails_holder.getChildAt(i)
val email = emailHolder.contact_email.value
val emailType = getEmailTypeId(emailHolder.contact_email_type.value)
+ val emailLabel = if (emailType == CommonDataKinds.Email.TYPE_CUSTOM) emailHolder.contact_email_type.value else ""
if (email.isNotEmpty()) {
- emails.add(Email(email, emailType))
+ emails.add(Email(email, emailType, emailLabel))
}
}
return emails
@@ -782,14 +890,31 @@ class EditContactActivity : ContactActivity() {
val addressHolder = contact_addresses_holder.getChildAt(i)
val address = addressHolder.contact_address.value
val addressType = getAddressTypeId(addressHolder.contact_address_type.value)
+ val addressLabel = if (addressType == CommonDataKinds.StructuredPostal.TYPE_CUSTOM) addressHolder.contact_address_type.value else ""
if (address.isNotEmpty()) {
- addresses.add(Address(address, addressType))
+ addresses.add(Address(address, addressType, addressLabel))
}
}
return addresses
}
+ private fun getFilledIMs(): ArrayList {
+ val IMs = ArrayList()
+ val IMsCount = contact_ims_holder.childCount
+ for (i in 0 until IMsCount) {
+ val IMsHolder = contact_ims_holder.getChildAt(i)
+ val IM = IMsHolder.contact_im.value
+ val IMType = getIMTypeId(IMsHolder.contact_im_type.value)
+ val IMLabel = if (IMType == CommonDataKinds.Im.PROTOCOL_CUSTOM) IMsHolder.contact_im_type.value else ""
+
+ if (IM.isNotEmpty()) {
+ IMs.add(IM(IM, IMType, IMLabel))
+ }
+ }
+ return IMs
+ }
+
private fun getFilledEvents(): ArrayList {
val unknown = getString(R.string.unknown)
val events = ArrayList()
@@ -830,6 +955,7 @@ class EditContactActivity : ContactActivity() {
contact!!.source = originalContactSource
ContactsHelper(this).deleteContact(contact!!)
}
+ setResult(Activity.RESULT_OK)
finish()
} else {
toast(R.string.unknown_error_occurred)
@@ -839,6 +965,7 @@ class EditContactActivity : ContactActivity() {
private fun updateContact(photoUpdateStatus: Int) {
isSaving = true
if (ContactsHelper(this@EditContactActivity).updateContact(contact!!, photoUpdateStatus)) {
+ setResult(Activity.RESULT_OK)
finish()
} else {
toast(R.string.unknown_error_occurred)
@@ -860,7 +987,7 @@ class EditContactActivity : ContactActivity() {
private fun addNewPhoneNumberField() {
val numberHolder = layoutInflater.inflate(R.layout.item_edit_phone_number, contact_numbers_holder, false) as ViewGroup
updateTextColors(numberHolder)
- setupPhoneNumberTypePicker(numberHolder.contact_number_type)
+ setupPhoneNumberTypePicker(numberHolder.contact_number_type, DEFAULT_PHONE_NUMBER_TYPE, "")
contact_numbers_holder.addView(numberHolder)
contact_numbers_holder.onGlobalLayout {
numberHolder.contact_number.requestFocus()
@@ -871,7 +998,7 @@ class EditContactActivity : ContactActivity() {
private fun addNewEmailField() {
val emailHolder = layoutInflater.inflate(R.layout.item_edit_email, contact_emails_holder, false) as ViewGroup
updateTextColors(emailHolder)
- setupEmailTypePicker(emailHolder.contact_email_type)
+ setupEmailTypePicker(emailHolder.contact_email_type, DEFAULT_EMAIL_TYPE, "")
contact_emails_holder.addView(emailHolder)
contact_emails_holder.onGlobalLayout {
emailHolder.contact_email.requestFocus()
@@ -882,7 +1009,7 @@ class EditContactActivity : ContactActivity() {
private fun addNewAddressField() {
val addressHolder = layoutInflater.inflate(R.layout.item_edit_address, contact_addresses_holder, false) as ViewGroup
updateTextColors(addressHolder)
- setupAddressTypePicker(addressHolder.contact_address_type)
+ setupAddressTypePicker(addressHolder.contact_address_type, DEFAULT_ADDRESS_TYPE, "")
contact_addresses_holder.addView(addressHolder)
contact_addresses_holder.onGlobalLayout {
addressHolder.contact_address.requestFocus()
@@ -890,6 +1017,17 @@ class EditContactActivity : ContactActivity() {
}
}
+ private fun addNewIMField() {
+ val IMHolder = layoutInflater.inflate(R.layout.item_edit_im, contact_ims_holder, false) as ViewGroup
+ updateTextColors(IMHolder)
+ setupIMTypePicker(IMHolder.contact_im_type, DEFAULT_IM_TYPE, "")
+ contact_ims_holder.addView(IMHolder)
+ contact_ims_holder.onGlobalLayout {
+ IMHolder.contact_im.requestFocus()
+ showKeyboard(IMHolder.contact_im)
+ }
+ }
+
private fun addNewEventField() {
val eventHolder = layoutInflater.inflate(R.layout.item_event, contact_events_holder, false) as ViewGroup
updateTextColors(eventHolder)
@@ -955,7 +1093,7 @@ class EditContactActivity : ContactActivity() {
private fun parseEmail(contentValues: ContentValues) {
val type = contentValues.getAsInteger(CommonDataKinds.Email.DATA2) ?: DEFAULT_EMAIL_TYPE
val emailValue = contentValues.getAsString(CommonDataKinds.Email.DATA1) ?: return
- val email = Email(emailValue, type)
+ val email = Email(emailValue, type, "")
contact!!.emails.add(email)
}
@@ -963,7 +1101,7 @@ class EditContactActivity : ContactActivity() {
val type = contentValues.getAsInteger(CommonDataKinds.StructuredPostal.DATA2) ?: DEFAULT_ADDRESS_TYPE
val addressValue = contentValues.getAsString(CommonDataKinds.StructuredPostal.DATA4)
?: contentValues.getAsString(CommonDataKinds.StructuredPostal.DATA1) ?: return
- val address = Address(addressValue, type)
+ val address = Address(addressValue, type, "")
contact!!.addresses.add(address)
}
@@ -1027,14 +1165,16 @@ class EditContactActivity : ContactActivity() {
getString(R.string.work_fax) -> CommonDataKinds.Phone.TYPE_FAX_WORK
getString(R.string.home_fax) -> CommonDataKinds.Phone.TYPE_FAX_HOME
getString(R.string.pager) -> CommonDataKinds.Phone.TYPE_PAGER
- else -> CommonDataKinds.Phone.TYPE_OTHER
+ getString(R.string.other) -> CommonDataKinds.Phone.TYPE_OTHER
+ else -> CommonDataKinds.Phone.TYPE_CUSTOM
}
private fun getEmailTypeId(value: String) = when (value) {
getString(R.string.home) -> CommonDataKinds.Email.TYPE_HOME
getString(R.string.work) -> CommonDataKinds.Email.TYPE_WORK
getString(R.string.mobile) -> CommonDataKinds.Email.TYPE_MOBILE
- else -> CommonDataKinds.Email.TYPE_OTHER
+ getString(R.string.other) -> CommonDataKinds.Email.TYPE_OTHER
+ else -> CommonDataKinds.Email.TYPE_CUSTOM
}
private fun getEventTypeId(value: String) = when (value) {
@@ -1046,6 +1186,19 @@ class EditContactActivity : ContactActivity() {
private fun getAddressTypeId(value: String) = when (value) {
getString(R.string.home) -> CommonDataKinds.StructuredPostal.TYPE_HOME
getString(R.string.work) -> CommonDataKinds.StructuredPostal.TYPE_WORK
- else -> CommonDataKinds.StructuredPostal.TYPE_OTHER
+ getString(R.string.other) -> CommonDataKinds.StructuredPostal.TYPE_OTHER
+ else -> CommonDataKinds.StructuredPostal.TYPE_CUSTOM
+ }
+
+ private fun getIMTypeId(value: String) = when (value) {
+ getString(R.string.aim) -> CommonDataKinds.Im.PROTOCOL_AIM
+ getString(R.string.windows_live) -> CommonDataKinds.Im.PROTOCOL_MSN
+ getString(R.string.yahoo) -> CommonDataKinds.Im.PROTOCOL_YAHOO
+ getString(R.string.skype) -> CommonDataKinds.Im.PROTOCOL_SKYPE
+ getString(R.string.qq) -> CommonDataKinds.Im.PROTOCOL_QQ
+ getString(R.string.hangouts) -> CommonDataKinds.Im.PROTOCOL_GOOGLE_TALK
+ getString(R.string.icq) -> CommonDataKinds.Im.PROTOCOL_ICQ
+ getString(R.string.jabber) -> CommonDataKinds.Im.PROTOCOL_JABBER
+ else -> CommonDataKinds.Im.PROTOCOL_CUSTOM
}
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/activities/InsertOrEditContactActivity.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/activities/InsertOrEditContactActivity.kt
new file mode 100644
index 00000000..16430225
--- /dev/null
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/activities/InsertOrEditContactActivity.kt
@@ -0,0 +1,90 @@
+package com.simplemobiletools.contacts.activities
+
+import android.app.Activity
+import android.content.Intent
+import android.os.Bundle
+import android.provider.ContactsContract
+import com.simplemobiletools.commons.extensions.getAdjustedPrimaryColor
+import com.simplemobiletools.commons.extensions.getColoredDrawableWithColor
+import com.simplemobiletools.commons.extensions.toast
+import com.simplemobiletools.commons.extensions.updateTextColors
+import com.simplemobiletools.commons.helpers.PERMISSION_READ_CONTACTS
+import com.simplemobiletools.contacts.R
+import com.simplemobiletools.contacts.adapters.ContactsAdapter
+import com.simplemobiletools.contacts.extensions.config
+import com.simplemobiletools.contacts.extensions.getContactPublicUri
+import com.simplemobiletools.contacts.helpers.ADD_NEW_CONTACT_NUMBER
+import com.simplemobiletools.contacts.helpers.ContactsHelper
+import com.simplemobiletools.contacts.helpers.KEY_PHONE
+import com.simplemobiletools.contacts.helpers.LOCATION_INSERT_OR_EDIT
+import com.simplemobiletools.contacts.models.Contact
+import kotlinx.android.synthetic.main.activity_insert_edit_contact.*
+
+class InsertOrEditContactActivity : SimpleActivity() {
+ private val START_INSERT_ACTIVITY = 1
+ private val START_EDIT_ACTIVITY = 2
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_insert_edit_contact)
+ title = getString(R.string.select_contact)
+ setupViews()
+
+ handlePermission(PERMISSION_READ_CONTACTS) {
+ // we do not really care about the permission request result. Even if it was denied, load private contacts
+ ContactsHelper(this).getContacts {
+ gotContacts(it)
+ }
+ }
+ }
+
+ private fun setupViews() {
+ updateTextColors(insert_edit_contact_holder)
+ new_contact_tmb.setImageDrawable(resources.getColoredDrawableWithColor(R.drawable.ic_new_contact, config.textColor))
+ new_contact_holder.setOnClickListener {
+ Intent().apply {
+ action = Intent.ACTION_INSERT
+ data = ContactsContract.Contacts.CONTENT_URI
+ putExtra(KEY_PHONE, intent.getStringExtra(KEY_PHONE))
+ if (resolveActivity(packageManager) != null) {
+ startActivityForResult(this, START_INSERT_ACTIVITY)
+ } else {
+ toast(R.string.no_app_found)
+ }
+ }
+ }
+
+ existing_contact_label.setTextColor(getAdjustedPrimaryColor())
+ }
+
+ private fun gotContacts(contacts: ArrayList) {
+ Contact.sorting = config.sorting
+ Contact.startWithSurname = config.startNameWithSurname
+ contacts.sort()
+
+ ContactsAdapter(this, contacts, null, LOCATION_INSERT_OR_EDIT, null, existing_contact_list, existing_contact_fastscroller) {
+ Intent(applicationContext, EditContactActivity::class.java).apply {
+ data = getContactPublicUri(it as Contact)
+ action = ADD_NEW_CONTACT_NUMBER
+ putExtra(KEY_PHONE, intent.getStringExtra(KEY_PHONE))
+ startActivityForResult(this, START_EDIT_ACTIVITY)
+ }
+ }.apply {
+ addVerticalDividers(true)
+ existing_contact_list.adapter = this
+ }
+
+ existing_contact_fastscroller.setScrollToY(0)
+ existing_contact_fastscroller.setViews(existing_contact_list) {
+ val item = (existing_contact_list.adapter as ContactsAdapter).contactItems.getOrNull(it)
+ existing_contact_fastscroller.updateBubbleText(item?.getBubbleText() ?: "")
+ }
+ }
+
+ override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
+ super.onActivityResult(requestCode, resultCode, resultData)
+ if (resultCode == Activity.RESULT_OK) {
+ finish()
+ }
+ }
+}
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/activities/MainActivity.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/activities/MainActivity.kt
index 19c92a75..0cb2281b 100644
--- a/app/src/main/kotlin/com/simplemobiletools/contacts/activities/MainActivity.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/activities/MainActivity.kt
@@ -3,6 +3,7 @@ package com.simplemobiletools.contacts.activities
import android.app.SearchManager
import android.content.Context
import android.content.Intent
+import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import android.net.Uri
@@ -163,6 +164,10 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
}
}
isFirstResume = false
+
+ val dialpadIcon = resources.getColoredDrawableWithColor(R.drawable.ic_dialpad, if (isBlackAndWhiteTheme()) Color.BLACK else Color.WHITE)
+ main_dialpad_button.setImageDrawable(dialpadIcon)
+ main_dialpad_button.background.applyColorFilter(getAdjustedPrimaryColor())
}
override fun onPause() {
@@ -369,6 +374,11 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
}
main_tabs_holder.beVisibleIf(skippedTabs < 3)
+
+ main_dialpad_button.setOnClickListener {
+ val intent = Intent(applicationContext, DialpadActivity::class.java)
+ startActivity(intent)
+ }
}
private fun getTabIcon(position: Int): Drawable {
@@ -539,6 +549,8 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
add(Release(16, R.string.release_16))
add(Release(27, R.string.release_27))
add(Release(29, R.string.release_29))
+ add(Release(31, R.string.release_31))
+ add(Release(32, R.string.release_32))
checkWhatsNew(this, BuildConfig.VERSION_CODE)
}
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/activities/ViewContactActivity.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/activities/ViewContactActivity.kt
index 3b41f777..a7995535 100644
--- a/app/src/main/kotlin/com/simplemobiletools/contacts/activities/ViewContactActivity.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/activities/ViewContactActivity.kt
@@ -11,6 +11,7 @@ import android.widget.RelativeLayout
import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.PERMISSION_READ_CONTACTS
import com.simplemobiletools.contacts.R
+import com.simplemobiletools.contacts.dialogs.CallConfirmationDialog
import com.simplemobiletools.contacts.extensions.*
import com.simplemobiletools.contacts.helpers.*
import kotlinx.android.synthetic.main.activity_view_contact.*
@@ -18,6 +19,7 @@ import kotlinx.android.synthetic.main.item_event.view.*
import kotlinx.android.synthetic.main.item_view_address.view.*
import kotlinx.android.synthetic.main.item_view_email.view.*
import kotlinx.android.synthetic.main.item_view_group.view.*
+import kotlinx.android.synthetic.main.item_view_im.view.*
import kotlinx.android.synthetic.main.item_view_phone_number.view.*
import kotlinx.android.synthetic.main.item_website.view.*
@@ -56,7 +58,6 @@ class ViewContactActivity : ContactActivity() {
return true
}
-
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (contact == null) {
return true
@@ -153,6 +154,7 @@ class ViewContactActivity : ContactActivity() {
setupPhoneNumbers()
setupEmails()
setupAddresses()
+ setupIMs()
setupEvents()
setupNotes()
setupOrganization()
@@ -222,10 +224,22 @@ class ViewContactActivity : ContactActivity() {
val phoneNumber = it
contact_numbers_holder.addView(this)
contact_number.text = phoneNumber.value
- contact_number_type.setText(getPhoneNumberTextId(phoneNumber.type))
+ contact_number_type.text = getPhoneNumberTypeText(phoneNumber.type, phoneNumber.label)
setOnClickListener {
- startCallIntent(phoneNumber.value)
+ if (config.showCallConfirmation) {
+ CallConfirmationDialog(this@ViewContactActivity, phoneNumber.value) {
+ startCallIntent(phoneNumber.value)
+ }
+ } else {
+ startCallIntent(phoneNumber.value)
+ }
+ }
+
+ setOnLongClickListener {
+ copyToClipboard(phoneNumber.value)
+ toast(R.string.value_copied_to_clipboard)
+ true
}
}
}
@@ -246,7 +260,7 @@ class ViewContactActivity : ContactActivity() {
val email = it
contact_emails_holder.addView(this)
contact_email.text = email.value
- contact_email_type.setText(getEmailTextId(email.type))
+ contact_email_type.text = getEmailTypeText(email.type, email.label)
setOnClickListener {
sendEmailIntent(email.value)
@@ -270,7 +284,7 @@ class ViewContactActivity : ContactActivity() {
val address = it
contact_addresses_holder.addView(this)
contact_address.text = address.value
- contact_address_type.setText(getAddressTextId(address.type))
+ contact_address_type.text = getAddressTypeText(address.type, address.label)
setOnClickListener {
sendAddressIntent(address.value)
@@ -285,6 +299,26 @@ class ViewContactActivity : ContactActivity() {
}
}
+ private fun setupIMs() {
+ contact_ims_holder.removeAllViews()
+ val IMs = contact!!.IMs
+ if (IMs.isNotEmpty() && showFields and SHOW_IMS_FIELD != 0) {
+ IMs.forEach {
+ layoutInflater.inflate(R.layout.item_view_im, contact_ims_holder, false).apply {
+ val IM = it
+ contact_ims_holder.addView(this)
+ contact_im.text = IM.value
+ contact_im_type.text = getIMTypeText(IM.type, IM.label)
+ }
+ }
+ contact_ims_image.beVisible()
+ contact_ims_holder.beVisible()
+ } else {
+ contact_ims_image.beGone()
+ contact_ims_holder.beGone()
+ }
+ }
+
private fun setupEvents() {
contact_events_holder.removeAllViews()
val events = contact!!.events
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/adapters/ContactsAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/adapters/ContactsAdapter.kt
index 39bd3220..4372437e 100644
--- a/app/src/main/kotlin/com/simplemobiletools/contacts/adapters/ContactsAdapter.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/adapters/ContactsAdapter.kt
@@ -29,12 +29,12 @@ import java.util.*
class ContactsAdapter(activity: SimpleActivity, var contactItems: ArrayList, private val refreshListener: RefreshContactsListener?,
private val location: Int, private val removeListener: RemoveFromGroupListener?, recyclerView: MyRecyclerView,
- fastScroller: FastScroller, itemClick: (Any) -> Unit) :
+ fastScroller: FastScroller, highlightText: String = "", itemClick: (Any) -> Unit) :
MyRecyclerViewAdapter(activity, recyclerView, fastScroller, itemClick) {
private lateinit var contactDrawable: Drawable
private var config = activity.config
- private var textToHighlight = ""
+ private var textToHighlight = highlightText
var adjustedPrimaryColor = activity.getAdjustedPrimaryColor()
var startNameWithSurname: Boolean
@@ -63,6 +63,8 @@ class ContactsAdapter(activity: SimpleActivity, var contactItems: ArrayList
+ val allowLongClick = location != LOCATION_INSERT_OR_EDIT
+ val view = holder.bindView(contact, true, allowLongClick) { itemView, layoutPosition ->
setupView(itemView, contact)
}
bindViewHolder(holder, position, view)
@@ -260,7 +263,13 @@ class ContactsAdapter(activity: SimpleActivity, var contactItems: ArrayList Unit) {
+class CallConfirmationDialog(val activity: BaseSimpleActivity, val callee: String, private val callback: () -> Unit) {
private var view = activity.layoutInflater.inflate(R.layout.dialog_call_confirmation, null)
init {
@@ -18,7 +17,7 @@ class CallConfirmationDialog(val activity: BaseSimpleActivity, val contact: Cont
AlertDialog.Builder(activity)
.setNegativeButton(R.string.cancel, null)
.create().apply {
- val title = String.format(activity.getString(R.string.call_person), contact.getFullName())
+ val title = String.format(activity.getString(R.string.call_person), callee)
activity.setupDialogStuff(view, this, titleText = title) {
view.call_confirm_phone.apply {
startAnimation(AnimationUtils.loadAnimation(activity, R.anim.pulsing_animation))
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/dialogs/CustomLabelDialog.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/dialogs/CustomLabelDialog.kt
new file mode 100644
index 00000000..047f5fe5
--- /dev/null
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/dialogs/CustomLabelDialog.kt
@@ -0,0 +1,36 @@
+package com.simplemobiletools.contacts.dialogs
+
+import android.support.v7.app.AlertDialog
+import com.simplemobiletools.commons.activities.BaseSimpleActivity
+import com.simplemobiletools.commons.extensions.setupDialogStuff
+import com.simplemobiletools.commons.extensions.showKeyboard
+import com.simplemobiletools.commons.extensions.toast
+import com.simplemobiletools.commons.extensions.value
+import com.simplemobiletools.contacts.R
+import kotlinx.android.synthetic.main.dialog_custom_label.view.*
+
+class CustomLabelDialog(val activity: BaseSimpleActivity, val callback: (label: String) -> Unit) {
+ init {
+
+ val view = activity.layoutInflater.inflate(R.layout.dialog_custom_label, null)
+
+ AlertDialog.Builder(activity)
+ .setPositiveButton(R.string.ok, null)
+ .setNegativeButton(R.string.cancel, null)
+ .create().apply {
+ activity.setupDialogStuff(view, this, R.string.label) {
+ showKeyboard(view.custom_label_edittext)
+ getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
+ val label = view.custom_label_edittext.value
+ if (label.isEmpty()) {
+ activity.toast(R.string.empty_name)
+ return@setOnClickListener
+ }
+
+ callback(label)
+ dismiss()
+ }
+ }
+ }
+ }
+}
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/dialogs/ManageVisibleFieldsDialog.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/dialogs/ManageVisibleFieldsDialog.kt
index 9560f4b2..9b9afcf6 100644
--- a/app/src/main/kotlin/com/simplemobiletools/contacts/dialogs/ManageVisibleFieldsDialog.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/dialogs/ManageVisibleFieldsDialog.kt
@@ -23,6 +23,7 @@ class ManageVisibleFieldsDialog(val activity: BaseSimpleActivity) {
put(SHOW_PHONE_NUMBERS_FIELD, R.id.manage_visible_fields_phone_numbers)
put(SHOW_EMAILS_FIELD, R.id.manage_visible_fields_emails)
put(SHOW_ADDRESSES_FIELD, R.id.manage_visible_fields_addresses)
+ put(SHOW_IMS_FIELD, R.id.manage_visible_fields_ims)
put(SHOW_EVENTS_FIELD, R.id.manage_visible_fields_events)
put(SHOW_NOTES_FIELD, R.id.manage_visible_fields_notes)
put(SHOW_ORGANIZATION_FIELD, R.id.manage_visible_fields_organization)
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/extensions/Activity.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/extensions/Activity.kt
index a63eb96d..a61b26f2 100644
--- a/app/src/main/kotlin/com/simplemobiletools/contacts/extensions/Activity.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/extensions/Activity.kt
@@ -36,7 +36,7 @@ fun SimpleActivity.startCallIntent(recipient: String) {
fun SimpleActivity.tryStartCall(contact: Contact) {
if (config.showCallConfirmation) {
- CallConfirmationDialog(this, contact) {
+ CallConfirmationDialog(this, contact.getFullName()) {
startCall(contact)
}
} else {
@@ -62,8 +62,13 @@ fun SimpleActivity.startCall(contact: Contact) {
fun SimpleActivity.showContactSourcePicker(currentSource: String, callback: (newSource: String) -> Unit) {
ContactsHelper(this).getContactSources {
+ val ignoredTypes = arrayListOf(
+ "org.thoughtcrime.securesms", // Signal
+ "org.telegram.messenger" // Telegram
+ )
+
val items = ArrayList()
- val sources = it.map { it.name }
+ val sources = it.filter { !ignoredTypes.contains(it.type) }.map { it.name }
var currentSourceIndex = -1
sources.forEachIndexed { index, account ->
var publicAccount = account
@@ -203,14 +208,16 @@ fun Activity.getVisibleContactSources(): ArrayList {
fun SimpleActivity.contactClicked(contact: Contact) {
when (config.onContactClick) {
- ON_CLICK_CALL_CONTACT -> {
- if (contact.phoneNumbers.isNotEmpty()) {
- tryStartCall(contact)
- } else {
- toast(R.string.no_phone_number_found)
- }
- }
+ ON_CLICK_CALL_CONTACT -> callContact(contact)
ON_CLICK_VIEW_CONTACT -> viewContact(contact)
ON_CLICK_EDIT_CONTACT -> editContact(contact)
}
}
+
+fun SimpleActivity.callContact(contact: Contact) {
+ if (contact.phoneNumbers.isNotEmpty()) {
+ tryStartCall(contact)
+ } else {
+ toast(R.string.no_phone_number_found)
+ }
+}
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/extensions/EditText.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/extensions/EditText.kt
new file mode 100644
index 00000000..65f826c2
--- /dev/null
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/extensions/EditText.kt
@@ -0,0 +1,13 @@
+package com.simplemobiletools.contacts.extensions
+
+import android.text.Editable
+import android.text.TextWatcher
+import android.widget.EditText
+
+fun EditText.afterTextChanged(callback: (String) -> Unit) = addTextChangedListener(object : TextWatcher {
+ override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
+
+ override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
+
+ override fun afterTextChanged(text: Editable) = callback(text.toString())
+})
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/extensions/String.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/extensions/String.kt
index 6134b1ad..f6d41a2b 100644
--- a/app/src/main/kotlin/com/simplemobiletools/contacts/extensions/String.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/extensions/String.kt
@@ -2,6 +2,7 @@ package com.simplemobiletools.contacts.extensions
import android.widget.TextView
import com.simplemobiletools.commons.helpers.getDateFormats
+import com.simplemobiletools.contacts.helpers.PHONE_NUMBER_PATTERN
import org.joda.time.DateTime
import org.joda.time.format.DateTimeFormat
import java.text.DateFormat
@@ -32,3 +33,5 @@ fun String.getDateTimeFromDateString(viewToUpdate: TextView? = null): DateTime {
}
return date
}
+
+fun String.applyRegexFiltering() = replace(PHONE_NUMBER_PATTERN.toRegex(), "")
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/fragments/MyViewPagerFragment.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/fragments/MyViewPagerFragment.kt
index 7afecd04..208c7715 100644
--- a/app/src/main/kotlin/com/simplemobiletools/contacts/fragments/MyViewPagerFragment.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/fragments/MyViewPagerFragment.kt
@@ -241,9 +241,10 @@ abstract class MyViewPagerFragment(context: Context, attributeSet: AttributeSet)
val filtered = contactsIgnoringSearch.filter {
getProperText(it.getFullName(), shouldNormalize).contains(text, true) ||
getProperText(it.nickname, shouldNormalize).contains(text, true) ||
- it.phoneNumbers.any { it.value.contains(text, true) } ||
+ it.doesContainPhoneNumber(text) ||
it.emails.any { it.value.contains(text, true) } ||
it.addresses.any { getProperText(it.value, shouldNormalize).contains(text, true) } ||
+ it.IMs.any { it.value.contains(text, true) } ||
getProperText(it.notes, shouldNormalize).contains(text, true) ||
getProperText(it.organization.company, shouldNormalize).contains(text, true) ||
getProperText(it.organization.jobPosition, shouldNormalize).contains(text, true) ||
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/fragments/RecentsFragment.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/fragments/RecentsFragment.kt
index c1319997..d4b30ab9 100644
--- a/app/src/main/kotlin/com/simplemobiletools/contacts/fragments/RecentsFragment.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/fragments/RecentsFragment.kt
@@ -10,10 +10,10 @@ import com.simplemobiletools.commons.helpers.PERMISSION_READ_CALL_LOG
import com.simplemobiletools.commons.helpers.PERMISSION_WRITE_CALL_LOG
import com.simplemobiletools.contacts.activities.EditContactActivity
import com.simplemobiletools.contacts.adapters.RecentCallsAdapter
+import com.simplemobiletools.contacts.extensions.applyRegexFiltering
import com.simplemobiletools.contacts.extensions.contactClicked
import com.simplemobiletools.contacts.helpers.IS_FROM_SIMPLE_CONTACTS
import com.simplemobiletools.contacts.helpers.KEY_PHONE
-import com.simplemobiletools.contacts.helpers.PHONE_NUMBER_PATTERN
import com.simplemobiletools.contacts.helpers.RECENTS_TAB_MASK
import com.simplemobiletools.contacts.models.Contact
import com.simplemobiletools.contacts.models.RecentCall
@@ -44,10 +44,10 @@ class RecentsFragment(context: Context, attributeSet: AttributeSet) : MyViewPage
val currAdapter = fragment_list.adapter
if (currAdapter == null) {
RecentCallsAdapter(activity!!, recentCalls, activity, fragment_list, fragment_fastscroller) {
- val recentCall = (it as RecentCall).number.replace(PHONE_NUMBER_PATTERN.toRegex(), "")
+ val recentCall = (it as RecentCall).number.applyRegexFiltering()
var selectedContact: Contact? = null
for (contact in allContacts) {
- if (contact.phoneNumbers.any { it.value.replace(PHONE_NUMBER_PATTERN.toRegex(), "") == recentCall }) {
+ if (contact.phoneNumbers.any { it.value.applyRegexFiltering() == recentCall }) {
selectedContact = contact
break
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/helpers/Constants.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/helpers/Constants.kt
index 0f56d22b..58309511 100644
--- a/app/src/main/kotlin/com/simplemobiletools/contacts/helpers/Constants.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/helpers/Constants.kt
@@ -21,8 +21,9 @@ const val SMT_PRIVATE = "smt_private" // used at the contact source of local c
const val IS_PRIVATE = "is_private"
const val GROUP = "group"
const val FIRST_GROUP_ID = 10000
-const val PHONE_NUMBER_PATTERN = "\\D+"
+const val PHONE_NUMBER_PATTERN = "[^0-9#*+]"
const val IS_FROM_SIMPLE_CONTACTS = "is_from_simple_contacts"
+const val ADD_NEW_CONTACT_NUMBER = "add_new_contact_number"
// extras used at third party intents
const val KEY_PHONE = "phone"
@@ -33,6 +34,8 @@ const val LOCATION_FAVORITES_TAB = 1
const val LOCATION_RECENTS_TAB = 2
const val LOCATION_GROUPS_TAB = 3
const val LOCATION_GROUP_CONTACTS = 4
+const val LOCATION_DIALPAD = 5
+const val LOCATION_INSERT_OR_EDIT = 6
const val CONTACTS_TAB_MASK = 1
const val FAVORITES_TAB_MASK = 2
@@ -56,6 +59,7 @@ const val PHOTO_UNCHANGED = 4
const val CELL = "CELL"
const val WORK = "WORK"
const val HOME = "HOME"
+const val OTHER = "OTHER"
const val PREF = "PREF"
const val MAIN = "MAIN"
const val FAX = "FAX"
@@ -64,6 +68,11 @@ const val HOME_FAX = "HOME;FAX"
const val PAGER = "PAGER"
const val MOBILE = "MOBILE"
+// IMs not supported by Ez-vcard
+const val HANGOUTS = "Hangouts"
+const val QQ = "QQ"
+const val JABBER = "Jabber"
+
const val ON_CLICK_CALL_CONTACT = 1
const val ON_CLICK_VIEW_CONTACT = 2
const val ON_CLICK_EDIT_CONTACT = 3
@@ -84,6 +93,7 @@ const val SHOW_GROUPS_FIELD = 2048
const val SHOW_CONTACT_SOURCE_FIELD = 4096
const val SHOW_WEBSITES_FIELD = 8192
const val SHOW_NICKNAME_FIELD = 16384
+const val SHOW_IMS_FIELD = 32768
const val DEFAULT_EMAIL_TYPE = CommonDataKinds.Email.TYPE_HOME
const val DEFAULT_PHONE_NUMBER_TYPE = CommonDataKinds.Phone.TYPE_MOBILE
@@ -91,6 +101,7 @@ const val DEFAULT_ADDRESS_TYPE = CommonDataKinds.StructuredPostal.TYPE_HOME
const val DEFAULT_EVENT_TYPE = CommonDataKinds.Event.TYPE_BIRTHDAY
const val DEFAULT_ORGANIZATION_TYPE = CommonDataKinds.Organization.TYPE_WORK
const val DEFAULT_WEBSITE_TYPE = CommonDataKinds.Website.TYPE_HOMEPAGE
+const val DEFAULT_IM_TYPE = CommonDataKinds.Im.PROTOCOL_SKYPE
// some manufacturer contact account types from https://stackoverflow.com/a/44802016/1967672
val localAccountTypes = arrayListOf("vnd.sec.contact.phone",
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/helpers/ContactsHelper.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/helpers/ContactsHelper.kt
index 17d26c16..88e3b7d7 100644
--- a/app/src/main/kotlin/com/simplemobiletools/contacts/helpers/ContactsHelper.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/helpers/ContactsHelper.kt
@@ -24,6 +24,7 @@ import com.simplemobiletools.contacts.models.*
import com.simplemobiletools.contacts.overloads.times
import java.text.SimpleDateFormat
import java.util.*
+import kotlin.collections.ArrayList
class ContactsHelper(val activity: Activity) {
private val BATCH_SIZE = 100
@@ -42,12 +43,24 @@ class ContactsHelper(val activity: Activity) {
}
val contactsSize = contacts.size()
- var resultContacts = ArrayList(contactsSize)
- (0 until contactsSize).mapTo(resultContacts) { contacts.valueAt(it) }
+ var tempContacts = ArrayList(contactsSize)
+ val resultContacts = ArrayList(contactsSize)
+ (0 until contactsSize).mapTo(tempContacts) { contacts.valueAt(it) }
if (activity.config.filterDuplicates) {
- resultContacts = resultContacts.distinctBy {
+ tempContacts = tempContacts.distinctBy {
it.getHashToCompare()
} as ArrayList
+
+ tempContacts.groupBy { "${it.getFullName().toLowerCase()}${it.emails}" }.values.forEach {
+ if (it.size == 1) {
+ resultContacts.add(it.first())
+ } else {
+ val sorted = it.sortedByDescending { it.getStringToCompare().length }
+ resultContacts.add(sorted.first())
+ }
+ }
+ } else {
+ resultContacts.addAll(tempContacts)
}
// groups are obtained with contactID, not rawID, so assign them to proper contacts like this
@@ -115,7 +128,7 @@ class ContactsHelper(val activity: Activity) {
val suffix = cursor.getStringValue(CommonDataKinds.StructuredName.SUFFIX) ?: ""
val nickname = ""
val photoUri = cursor.getStringValue(CommonDataKinds.StructuredName.PHOTO_URI) ?: ""
- val number = ArrayList() // proper value is obtained below
+ val numbers = ArrayList() // proper value is obtained below
val emails = ArrayList()
val addresses = ArrayList()
val events = ArrayList()
@@ -127,8 +140,10 @@ class ContactsHelper(val activity: Activity) {
val groups = ArrayList()
val organization = Organization("", "")
val websites = ArrayList()
- val contact = Contact(id, prefix, firstName, middleName, surname, suffix, nickname, photoUri, number, emails, addresses,
- events, accountName, starred, contactId, thumbnailUri, null, notes, groups, organization, websites)
+ val cleanNumbers = ArrayList()
+ val ims = ArrayList()
+ val contact = Contact(id, prefix, firstName, middleName, surname, suffix, nickname, photoUri, numbers, emails, addresses,
+ events, accountName, starred, contactId, thumbnailUri, null, notes, groups, organization, websites, cleanNumbers, ims)
contacts.put(id, contact)
} while (cursor.moveToNext())
@@ -139,11 +154,22 @@ class ContactsHelper(val activity: Activity) {
cursor?.close()
}
+ val filterDuplicates = activity.config.filterDuplicates
val phoneNumbers = getPhoneNumbers(null)
var size = phoneNumbers.size()
for (i in 0 until size) {
val key = phoneNumbers.keyAt(i)
- contacts[key]?.phoneNumbers = phoneNumbers.valueAt(i)
+ if (contacts[key] != null) {
+ val numbers = phoneNumbers.valueAt(i)
+ contacts[key].phoneNumbers = numbers
+
+ if (filterDuplicates) {
+ // remove all spaces, dashes etc from numbers for easier comparing, used only at list views
+ numbers.forEach {
+ numbers.mapTo(contacts[key].cleanPhoneNumbers) { PhoneNumber(it.value.applyRegexFiltering(), 0, "") }
+ }
+ }
+ }
}
val nicknames = getNicknames()
@@ -167,6 +193,13 @@ class ContactsHelper(val activity: Activity) {
contacts[key]?.addresses = addresses.valueAt(i)
}
+ val IMs = getIMs()
+ size = IMs.size()
+ for (i in 0 until size) {
+ val key = IMs.keyAt(i)
+ contacts[key]?.IMs = IMs.valueAt(i)
+ }
+
val events = getEvents()
size = events.size()
for (i in 0 until size) {
@@ -202,7 +235,8 @@ class ContactsHelper(val activity: Activity) {
val projection = arrayOf(
ContactsContract.Data.RAW_CONTACT_ID,
CommonDataKinds.Phone.NUMBER,
- CommonDataKinds.Phone.TYPE
+ CommonDataKinds.Phone.TYPE,
+ CommonDataKinds.Phone.LABEL
)
val selection = if (contactId == null) getSourcesSelection() else "${ContactsContract.Data.RAW_CONTACT_ID} = ?"
@@ -216,12 +250,13 @@ class ContactsHelper(val activity: Activity) {
val id = cursor.getIntValue(ContactsContract.Data.RAW_CONTACT_ID)
val number = cursor.getStringValue(CommonDataKinds.Phone.NUMBER) ?: continue
val type = cursor.getIntValue(CommonDataKinds.Phone.TYPE)
+ val label = cursor.getStringValue(CommonDataKinds.Phone.LABEL) ?: ""
if (phoneNumbers[id] == null) {
phoneNumbers.put(id, ArrayList())
}
- val phoneNumber = PhoneNumber(number, type)
+ val phoneNumber = PhoneNumber(number, type, label)
phoneNumbers[id].add(phoneNumber)
} while (cursor.moveToNext())
}
@@ -270,7 +305,8 @@ class ContactsHelper(val activity: Activity) {
val projection = arrayOf(
ContactsContract.Data.RAW_CONTACT_ID,
CommonDataKinds.Email.DATA,
- CommonDataKinds.Email.TYPE
+ CommonDataKinds.Email.TYPE,
+ CommonDataKinds.Email.LABEL
)
val selection = if (contactId == null) getSourcesSelection() else "${ContactsContract.Data.RAW_CONTACT_ID} = ?"
@@ -284,12 +320,13 @@ class ContactsHelper(val activity: Activity) {
val id = cursor.getIntValue(ContactsContract.Data.RAW_CONTACT_ID)
val email = cursor.getStringValue(CommonDataKinds.Email.DATA) ?: continue
val type = cursor.getIntValue(CommonDataKinds.Email.TYPE)
+ val label = cursor.getStringValue(CommonDataKinds.Email.LABEL) ?: ""
if (emails[id] == null) {
emails.put(id, ArrayList())
}
- emails[id]!!.add(Email(email, type))
+ emails[id]!!.add(Email(email, type, label))
} while (cursor.moveToNext())
}
} catch (e: Exception) {
@@ -307,7 +344,8 @@ class ContactsHelper(val activity: Activity) {
val projection = arrayOf(
ContactsContract.Data.RAW_CONTACT_ID,
CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS,
- CommonDataKinds.StructuredPostal.TYPE
+ CommonDataKinds.StructuredPostal.TYPE,
+ CommonDataKinds.StructuredPostal.LABEL
)
val selection = if (contactId == null) getSourcesSelection() else "${ContactsContract.Data.RAW_CONTACT_ID} = ?"
@@ -321,12 +359,13 @@ class ContactsHelper(val activity: Activity) {
val id = cursor.getIntValue(ContactsContract.Data.RAW_CONTACT_ID)
val address = cursor.getStringValue(CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS) ?: continue
val type = cursor.getIntValue(CommonDataKinds.StructuredPostal.TYPE)
+ val label = cursor.getStringValue(CommonDataKinds.StructuredPostal.LABEL) ?: ""
if (addresses[id] == null) {
addresses.put(id, ArrayList())
}
- addresses[id]!!.add(Address(address, type))
+ addresses[id]!!.add(Address(address, type, label))
} while (cursor.moveToNext())
}
} catch (e: Exception) {
@@ -338,6 +377,45 @@ class ContactsHelper(val activity: Activity) {
return addresses
}
+ private fun getIMs(contactId: Int? = null): SparseArray> {
+ val IMs = SparseArray>()
+ val uri = ContactsContract.Data.CONTENT_URI
+ val projection = arrayOf(
+ ContactsContract.Data.RAW_CONTACT_ID,
+ CommonDataKinds.Im.DATA,
+ CommonDataKinds.Im.PROTOCOL,
+ CommonDataKinds.Im.CUSTOM_PROTOCOL
+ )
+
+ val selection = getSourcesSelection(true, contactId != null)
+ val selectionArgs = getSourcesSelectionArgs(CommonDataKinds.Im.CONTENT_ITEM_TYPE, contactId)
+
+ var cursor: Cursor? = null
+ try {
+ cursor = activity.contentResolver.query(uri, projection, selection, selectionArgs, null)
+ if (cursor?.moveToFirst() == true) {
+ do {
+ val id = cursor.getIntValue(ContactsContract.Data.RAW_CONTACT_ID)
+ val IM = cursor.getStringValue(CommonDataKinds.Im.DATA) ?: continue
+ val type = cursor.getIntValue(CommonDataKinds.Im.PROTOCOL)
+ val label = cursor.getStringValue(CommonDataKinds.Im.CUSTOM_PROTOCOL) ?: ""
+
+ if (IMs[id] == null) {
+ IMs.put(id, ArrayList())
+ }
+
+ IMs[id]!!.add(IM(IM, type, label))
+ } while (cursor.moveToNext())
+ }
+ } catch (e: Exception) {
+ activity.showErrorToast(e)
+ } finally {
+ cursor?.close()
+ }
+
+ return IMs
+ }
+
private fun getEvents(contactId: Int? = null): SparseArray> {
val events = SparseArray>()
val uri = ContactsContract.Data.CONTENT_URI
@@ -707,8 +785,10 @@ class ContactsHelper(val activity: Activity) {
val thumbnailUri = cursor.getStringValue(CommonDataKinds.StructuredName.PHOTO_THUMBNAIL_URI) ?: ""
val organization = getOrganizations(id)[id] ?: Organization("", "")
val websites = getWebsites(id)[id] ?: ArrayList()
+ val cleanNumbers = ArrayList()
+ val ims = getIMs(id)[id] ?: ArrayList()
return Contact(id, prefix, firstName, middleName, surname, suffix, nickname, photoUri, number, emails, addresses, events,
- accountName, starred, contactId, thumbnailUri, null, notes, groups, organization, websites)
+ accountName, starred, contactId, thumbnailUri, null, notes, groups, organization, websites, cleanNumbers, ims)
}
} finally {
cursor?.close()
@@ -856,6 +936,7 @@ class ContactsHelper(val activity: Activity) {
withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
withValue(CommonDataKinds.Phone.NUMBER, it.value)
withValue(CommonDataKinds.Phone.TYPE, it.type)
+ withValue(CommonDataKinds.Phone.LABEL, it.label)
operations.add(build())
}
}
@@ -875,6 +956,7 @@ class ContactsHelper(val activity: Activity) {
withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Email.CONTENT_ITEM_TYPE)
withValue(CommonDataKinds.Email.DATA, it.value)
withValue(CommonDataKinds.Email.TYPE, it.type)
+ withValue(CommonDataKinds.Email.LABEL, it.label)
operations.add(build())
}
}
@@ -894,6 +976,27 @@ class ContactsHelper(val activity: Activity) {
withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE)
withValue(CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS, it.value)
withValue(CommonDataKinds.StructuredPostal.TYPE, it.type)
+ withValue(CommonDataKinds.StructuredPostal.LABEL, it.label)
+ operations.add(build())
+ }
+ }
+
+ // delete IMs
+ ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI).apply {
+ val selection = "${ContactsContract.Data.RAW_CONTACT_ID} = ? AND ${ContactsContract.Data.MIMETYPE} = ? "
+ val selectionArgs = arrayOf(contact.id.toString(), CommonDataKinds.Im.CONTENT_ITEM_TYPE)
+ withSelection(selection, selectionArgs)
+ operations.add(build())
+ }
+
+ // add IMs
+ contact.IMs.forEach {
+ ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
+ withValue(ContactsContract.Data.RAW_CONTACT_ID, contact.id)
+ withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Im.CONTENT_ITEM_TYPE)
+ withValue(CommonDataKinds.Im.DATA, it.value)
+ withValue(CommonDataKinds.Im.PROTOCOL, it.type)
+ withValue(CommonDataKinds.Im.CUSTOM_PROTOCOL, it.label)
operations.add(build())
}
}
@@ -1134,6 +1237,7 @@ class ContactsHelper(val activity: Activity) {
withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
withValue(CommonDataKinds.Phone.NUMBER, it.value)
withValue(CommonDataKinds.Phone.TYPE, it.type)
+ withValue(CommonDataKinds.Phone.LABEL, it.label)
operations.add(build())
}
}
@@ -1145,6 +1249,7 @@ class ContactsHelper(val activity: Activity) {
withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Email.CONTENT_ITEM_TYPE)
withValue(CommonDataKinds.Email.DATA, it.value)
withValue(CommonDataKinds.Email.TYPE, it.type)
+ withValue(CommonDataKinds.Email.LABEL, it.label)
operations.add(build())
}
}
@@ -1156,6 +1261,19 @@ class ContactsHelper(val activity: Activity) {
withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE)
withValue(CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS, it.value)
withValue(CommonDataKinds.StructuredPostal.TYPE, it.type)
+ withValue(CommonDataKinds.StructuredPostal.LABEL, it.label)
+ operations.add(build())
+ }
+ }
+
+ // IMs
+ contact.IMs.forEach {
+ ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
+ withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
+ withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Im.CONTENT_ITEM_TYPE)
+ withValue(CommonDataKinds.Im.DATA, it.value)
+ withValue(CommonDataKinds.Im.PROTOCOL, it.type)
+ withValue(CommonDataKinds.Im.CUSTOM_PROTOCOL, it.label)
operations.add(build())
}
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/helpers/DBHelper.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/helpers/DBHelper.kt
index 860b3698..b73283c6 100644
--- a/app/src/main/kotlin/com/simplemobiletools/contacts/helpers/DBHelper.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/helpers/DBHelper.kt
@@ -16,6 +16,8 @@ import com.simplemobiletools.commons.extensions.getBlobValue
import com.simplemobiletools.commons.extensions.getIntValue
import com.simplemobiletools.commons.extensions.getLongValue
import com.simplemobiletools.commons.extensions.getStringValue
+import com.simplemobiletools.contacts.extensions.applyRegexFiltering
+import com.simplemobiletools.contacts.extensions.config
import com.simplemobiletools.contacts.extensions.getByteArray
import com.simplemobiletools.contacts.extensions.getPhotoThumbnailSize
import com.simplemobiletools.contacts.models.*
@@ -35,6 +37,7 @@ class DBHelper private constructor(val context: Context) : SQLiteOpenHelper(cont
private val COL_EVENTS = "events"
private val COL_STARRED = "starred"
private val COL_ADDRESSES = "addresses"
+ private val COL_IMS = "ims"
private val COL_NOTES = "notes"
private val COL_COMPANY = "company"
private val COL_JOB_POSITION = "job_position"
@@ -50,7 +53,7 @@ class DBHelper private constructor(val context: Context) : SQLiteOpenHelper(cont
companion object {
const val DB_NAME = "contacts.db"
- private const val DB_VERSION = 6
+ private const val DB_VERSION = 7
private var dbInstance: DBHelper? = null
var gson = Gson()
@@ -66,7 +69,7 @@ class DBHelper private constructor(val context: Context) : SQLiteOpenHelper(cont
db.execSQL("CREATE TABLE $CONTACTS_TABLE_NAME ($COL_ID INTEGER PRIMARY KEY AUTOINCREMENT, $COL_FIRST_NAME TEXT, $COL_MIDDLE_NAME TEXT, " +
"$COL_SURNAME TEXT, $COL_PHOTO BLOB, $COL_PHONE_NUMBERS TEXT, $COL_EMAILS TEXT, $COL_EVENTS TEXT, $COL_STARRED INTEGER, " +
"$COL_ADDRESSES TEXT, $COL_NOTES TEXT, $COL_GROUPS TEXT, $COL_PREFIX TEXT, $COL_SUFFIX TEXT, $COL_COMPANY TEXT, $COL_JOB_POSITION TEXT," +
- "$COL_WEBSITES TEXT, $COL_NICKNAME TEXT)")
+ "$COL_WEBSITES TEXT, $COL_NICKNAME TEXT, $COL_IMS TEXT)")
// start autoincrement ID from FIRST_CONTACT_ID to avoid conflicts
db.execSQL("REPLACE INTO sqlite_sequence (name, seq) VALUES ('$CONTACTS_TABLE_NAME', $FIRST_CONTACT_ID)")
@@ -99,6 +102,10 @@ class DBHelper private constructor(val context: Context) : SQLiteOpenHelper(cont
if (oldVersion < 6) {
db.execSQL("ALTER TABLE $CONTACTS_TABLE_NAME ADD COLUMN $COL_NICKNAME TEXT DEFAULT ''")
}
+
+ if (oldVersion < 7) {
+ db.execSQL("ALTER TABLE $CONTACTS_TABLE_NAME ADD COLUMN $COL_IMS TEXT DEFAULT ''")
+ }
}
private fun createGroupsTable(db: SQLiteDatabase) {
@@ -144,6 +151,7 @@ class DBHelper private constructor(val context: Context) : SQLiteOpenHelper(cont
put(COL_PHONE_NUMBERS, gson.toJson(contact.phoneNumbers))
put(COL_EMAILS, gson.toJson(contact.emails))
put(COL_ADDRESSES, gson.toJson(contact.addresses))
+ put(COL_IMS, gson.toJson(contact.IMs))
put(COL_EVENTS, gson.toJson(contact.events))
put(COL_STARRED, contact.starred)
put(COL_NOTES, contact.notes)
@@ -257,13 +265,15 @@ class DBHelper private constructor(val context: Context) : SQLiteOpenHelper(cont
fun getContacts(activity: Activity, selection: String? = null, selectionArgs: Array? = null): ArrayList {
val storedGroups = ContactsHelper(activity).getStoredGroups()
+ val filterDuplicates = activity.config.filterDuplicates
val contacts = ArrayList()
val projection = arrayOf(COL_ID, COL_PREFIX, COL_FIRST_NAME, COL_MIDDLE_NAME, COL_SURNAME, COL_SUFFIX, COL_NICKNAME, COL_PHONE_NUMBERS,
- COL_EMAILS, COL_EVENTS, COL_STARRED, COL_PHOTO, COL_ADDRESSES, COL_NOTES, COL_GROUPS, COL_COMPANY, COL_JOB_POSITION, COL_WEBSITES)
+ COL_EMAILS, COL_EVENTS, COL_STARRED, COL_PHOTO, COL_ADDRESSES, COL_IMS, COL_NOTES, COL_GROUPS, COL_COMPANY, COL_JOB_POSITION, COL_WEBSITES)
val phoneNumbersToken = object : TypeToken>() {}.type
val emailsToken = object : TypeToken>() {}.type
val addressesToken = object : TypeToken>() {}.type
+ val IMsToken = object : TypeToken>() {}.type
val eventsToken = object : TypeToken>() {}.type
val groupIdsToken = object : TypeToken>() {}.type
val websitesToken = object : TypeToken>() {}.type
@@ -283,14 +293,30 @@ class DBHelper private constructor(val context: Context) : SQLiteOpenHelper(cont
val phoneNumbers = if (phoneNumbersJson == "[]") ArrayList() else gson.fromJson>(phoneNumbersJson, phoneNumbersToken)
?: ArrayList(1)
+ // labels can be null at upgrading from older app versions, when the label wasn't available at all yet
+ phoneNumbers.filter { it.label == null }.forEach {
+ it.label = ""
+ }
+
val emailsJson = cursor.getStringValue(COL_EMAILS)
val emails = if (emailsJson == "[]") ArrayList() else gson.fromJson>(emailsJson, emailsToken)
?: ArrayList(1)
+ emails.filter { it.label == null }.forEach {
+ it.label = ""
+ }
+
val addressesJson = cursor.getStringValue(COL_ADDRESSES)
val addresses = if (addressesJson == "[]") ArrayList() else gson.fromJson>(addressesJson, addressesToken)
?: ArrayList(1)
+ addresses.filter { it.label == null }.forEach {
+ it.label = ""
+ }
+
+ val IMsJson = cursor.getStringValue(COL_IMS)
+ val IMs = if (IMsJson == "[]") ArrayList() else gson.fromJson>(IMsJson, IMsToken) ?: ArrayList(1)
+
val eventsJson = cursor.getStringValue(COL_EVENTS)
val events = if (eventsJson == "[]") ArrayList() else gson.fromJson>(eventsJson, eventsToken)
?: ArrayList(1)
@@ -322,8 +348,13 @@ class DBHelper private constructor(val context: Context) : SQLiteOpenHelper(cont
val websites = if (websitesJson == "[]") ArrayList() else gson.fromJson>(websitesJson, websitesToken)
?: ArrayList(1)
+ val cleanPhoneNumbers = ArrayList()
+ if (filterDuplicates) {
+ phoneNumbers.mapTo(cleanPhoneNumbers) { PhoneNumber(it.value.applyRegexFiltering(), 0, "") }
+ }
+
val contact = Contact(id, prefix, firstName, middleName, surname, suffix, nickname, "", phoneNumbers, emails, addresses,
- events, SMT_PRIVATE, starred, id, "", photo, notes, groups, organization, websites)
+ events, SMT_PRIVATE, starred, id, "", photo, notes, groups, organization, websites, cleanPhoneNumbers, IMs)
contacts.add(contact)
}
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/helpers/VcfExporter.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/helpers/VcfExporter.kt
index 4832c2ac..3a3dfb92 100644
--- a/app/src/main/kotlin/com/simplemobiletools/contacts/helpers/VcfExporter.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/helpers/VcfExporter.kt
@@ -15,10 +15,7 @@ import com.simplemobiletools.contacts.helpers.VcfExporter.ExportResult.EXPORT_FA
import com.simplemobiletools.contacts.models.Contact
import ezvcard.Ezvcard
import ezvcard.VCard
-import ezvcard.parameter.AddressType
-import ezvcard.parameter.EmailType
import ezvcard.parameter.ImageType
-import ezvcard.parameter.TelephoneType
import ezvcard.property.*
import ezvcard.util.PartialDate
import java.io.File
@@ -62,13 +59,13 @@ class VcfExporter {
contact.phoneNumbers.forEach {
val phoneNumber = Telephone(it.value)
- phoneNumber.types.add(TelephoneType.find(getPhoneNumberLabel(it.type)))
+ phoneNumber.parameters.addType(getPhoneNumberTypeLabel(it.type, it.label))
card.addTelephoneNumber(phoneNumber)
}
contact.emails.forEach {
val email = Email(it.value)
- email.types.add(EmailType.find(getEmailTypeLabel(it.type)))
+ email.parameters.addType(getEmailTypeLabel(it.type, it.label))
card.addEmail(email)
}
@@ -76,7 +73,7 @@ class VcfExporter {
if (it.type == CommonDataKinds.Event.TYPE_BIRTHDAY || it.type == CommonDataKinds.Event.TYPE_ANNIVERSARY) {
val dateTime = it.value.getDateTimeFromDateString()
if (it.value.startsWith("--")) {
- val partialDate = PartialDate.builder().year(null).month(dateTime.monthOfYear - 1).date(dateTime.dayOfMonth).build()
+ val partialDate = PartialDate.builder().year(null).month(dateTime.monthOfYear).date(dateTime.dayOfMonth).build()
if (it.type == CommonDataKinds.Event.TYPE_BIRTHDAY) {
card.birthdays.add(Birthday(partialDate))
} else {
@@ -101,10 +98,26 @@ class VcfExporter {
contact.addresses.forEach {
val address = Address()
address.streetAddress = it.value
- address.types.add(AddressType.find(getAddressTypeLabel(it.type)))
+ address.parameters.addType(getAddressTypeLabel(it.type, it.label))
card.addAddress(address)
}
+ contact.IMs.forEach {
+ val impp = when (it.type) {
+ CommonDataKinds.Im.PROTOCOL_AIM -> Impp.aim(it.value)
+ CommonDataKinds.Im.PROTOCOL_YAHOO -> Impp.yahoo(it.value)
+ CommonDataKinds.Im.PROTOCOL_MSN -> Impp.msn(it.value)
+ CommonDataKinds.Im.PROTOCOL_ICQ -> Impp.icq(it.value)
+ CommonDataKinds.Im.PROTOCOL_SKYPE -> Impp.skype(it.value)
+ CommonDataKinds.Im.PROTOCOL_GOOGLE_TALK -> Impp(HANGOUTS, it.value)
+ CommonDataKinds.Im.PROTOCOL_QQ -> Impp(QQ, it.value)
+ CommonDataKinds.Im.PROTOCOL_JABBER -> Impp(JABBER, it.value)
+ else -> Impp(it.label, it.value)
+ }
+
+ card.addImpp(impp)
+ }
+
if (contact.notes.isNotEmpty()) {
card.addNote(contact.notes)
}
@@ -126,6 +139,15 @@ class VcfExporter {
card.addPhoto(photo)
}
+ if (contact.groups.isNotEmpty()) {
+ val groupList = Categories()
+ contact.groups.forEach {
+ groupList.values.add(it.title)
+ }
+
+ card.categories = groupList
+ }
+
cards.add(card)
contactsExported++
}
@@ -143,24 +165,30 @@ class VcfExporter {
}
}
- private fun getPhoneNumberLabel(type: Int) = when (type) {
+ private fun getPhoneNumberTypeLabel(type: Int, label: String) = when (type) {
CommonDataKinds.Phone.TYPE_MOBILE -> CELL
+ CommonDataKinds.Phone.TYPE_HOME -> HOME
CommonDataKinds.Phone.TYPE_WORK -> WORK
CommonDataKinds.Phone.TYPE_MAIN -> PREF
CommonDataKinds.Phone.TYPE_FAX_WORK -> WORK_FAX
CommonDataKinds.Phone.TYPE_FAX_HOME -> HOME_FAX
CommonDataKinds.Phone.TYPE_PAGER -> PAGER
- else -> HOME
+ CommonDataKinds.Phone.TYPE_OTHER -> OTHER
+ else -> label
}
- private fun getEmailTypeLabel(type: Int) = when (type) {
+ private fun getEmailTypeLabel(type: Int, label: String) = when (type) {
+ CommonDataKinds.Email.TYPE_HOME -> HOME
CommonDataKinds.Email.TYPE_WORK -> WORK
CommonDataKinds.Email.TYPE_MOBILE -> MOBILE
- else -> HOME
+ CommonDataKinds.Email.TYPE_OTHER -> OTHER
+ else -> label
}
- private fun getAddressTypeLabel(type: Int) = when (type) {
+ private fun getAddressTypeLabel(type: Int, label: String) = when (type) {
+ CommonDataKinds.StructuredPostal.TYPE_HOME -> HOME
CommonDataKinds.StructuredPostal.TYPE_WORK -> WORK
- else -> HOME
+ CommonDataKinds.StructuredPostal.TYPE_OTHER -> OTHER
+ else -> label
}
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/helpers/VcfImporter.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/helpers/VcfImporter.kt
index d247b0ba..9cc27af8 100644
--- a/app/src/main/kotlin/com/simplemobiletools/contacts/helpers/VcfImporter.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/helpers/VcfImporter.kt
@@ -6,15 +6,18 @@ import android.provider.ContactsContract.CommonDataKinds
import android.widget.Toast
import com.simplemobiletools.commons.extensions.showErrorToast
import com.simplemobiletools.contacts.activities.SimpleActivity
+import com.simplemobiletools.contacts.extensions.dbHelper
import com.simplemobiletools.contacts.extensions.getCachePhoto
import com.simplemobiletools.contacts.extensions.getCachePhotoUri
import com.simplemobiletools.contacts.helpers.VcfImporter.ImportResult.*
import com.simplemobiletools.contacts.models.*
import ezvcard.Ezvcard
+import ezvcard.VCard
import org.joda.time.DateTime
import org.joda.time.format.DateTimeFormat
import java.io.File
import java.io.FileOutputStream
+import java.net.URLDecoder
import java.util.*
class VcfImporter(val activity: SimpleActivity) {
@@ -48,24 +51,42 @@ class VcfImporter(val activity: SimpleActivity) {
val phoneNumbers = ArrayList()
ezContact.telephoneNumbers.forEach {
- val type = getPhoneNumberTypeId(it.types.firstOrNull()?.value ?: MOBILE)
val number = it.text
- phoneNumbers.add(PhoneNumber(number, type))
+ val type = getPhoneNumberTypeId(it.types.firstOrNull()?.value ?: MOBILE, it.types.getOrNull(1)?.value)
+ val label = if (type == CommonDataKinds.Phone.TYPE_CUSTOM) {
+ it.types.firstOrNull()?.value ?: ""
+ } else {
+ ""
+ }
+
+ phoneNumbers.add(PhoneNumber(number, type, label))
}
val emails = ArrayList()
ezContact.emails.forEach {
- val type = getEmailTypeId(it.types.firstOrNull()?.value ?: HOME)
val email = it.value
- emails.add(Email(email, type))
+ val type = getEmailTypeId(it.types.firstOrNull()?.value ?: HOME)
+ val label = if (type == CommonDataKinds.Email.TYPE_CUSTOM) {
+ it.types.firstOrNull()?.value ?: ""
+ } else {
+ ""
+ }
+
+ emails.add(Email(email, type, label))
}
val addresses = ArrayList()
ezContact.addresses.forEach {
- val type = getAddressTypeId(it.types.firstOrNull()?.value ?: HOME)
val address = it.streetAddress
+ val type = getAddressTypeId(it.types.firstOrNull()?.value ?: HOME)
+ val label = if (type == CommonDataKinds.StructuredPostal.TYPE_CUSTOM) {
+ it.types.firstOrNull()?.value ?: ""
+ } else {
+ ""
+ }
+
if (address?.isNotEmpty() == true) {
- addresses.add(Address(address, type))
+ addresses.add(Address(address, type, label))
}
}
@@ -83,18 +104,44 @@ class VcfImporter(val activity: SimpleActivity) {
val starred = 0
val contactId = 0
val notes = ezContact.notes.firstOrNull()?.value ?: ""
- val groups = ArrayList()
+ val groups = getContactGroups(ezContact)
val company = ezContact.organization?.values?.firstOrNull() ?: ""
val jobPosition = ezContact.titles?.firstOrNull()?.value ?: ""
val organization = Organization(company, jobPosition)
val websites = ezContact.urls.map { it.value } as ArrayList
-
val photoData = ezContact.photos.firstOrNull()?.data
val photo = null
val thumbnailUri = savePhoto(photoData)
+ val cleanPhoneNumbers = ArrayList()
+
+ val IMs = ArrayList()
+ ezContact.impps.forEach {
+ val typeString = it.uri.scheme
+ val value = URLDecoder.decode(it.uri.toString().substring(it.uri.scheme.length + 1), "UTF-8")
+ val type = when {
+ it.isAim -> CommonDataKinds.Im.PROTOCOL_AIM
+ it.isYahoo -> CommonDataKinds.Im.PROTOCOL_YAHOO
+ it.isMsn -> CommonDataKinds.Im.PROTOCOL_MSN
+ it.isIcq -> CommonDataKinds.Im.PROTOCOL_ICQ
+ it.isSkype -> CommonDataKinds.Im.PROTOCOL_SKYPE
+ typeString == HANGOUTS -> CommonDataKinds.Im.PROTOCOL_GOOGLE_TALK
+ typeString == QQ -> CommonDataKinds.Im.PROTOCOL_QQ
+ typeString == JABBER -> CommonDataKinds.Im.PROTOCOL_JABBER
+ else -> CommonDataKinds.Im.PROTOCOL_CUSTOM
+ }
+
+ val label = if (type == CommonDataKinds.Im.PROTOCOL_CUSTOM) URLDecoder.decode(typeString, "UTF-8") else ""
+ val IM = IM(value, type, label)
+ IMs.add(IM)
+ }
val contact = Contact(0, prefix, firstName, middleName, surname, suffix, nickname, photoUri, phoneNumbers, emails, addresses, events,
- targetContactSource, starred, contactId, thumbnailUri, photo, notes, groups, organization, websites)
+ targetContactSource, starred, contactId, thumbnailUri, photo, notes, groups, organization, websites, cleanPhoneNumbers, IMs)
+
+ // if there is no N and ORG fields at the given contact, only FN, treat it as an organization
+ if (contact.getFullName().isEmpty() && contact.organization.isEmpty() && ezContact.formattedName.value.isNotEmpty()) {
+ contact.organization.company = ezContact.formattedName.value
+ }
if (ContactsHelper(activity).insertContact(contact)) {
contactsImported++
@@ -117,29 +164,71 @@ class VcfImporter(val activity: SimpleActivity) {
return dateTime.toString("yyyy-MM-dd")
}
- private fun getPhoneNumberTypeId(type: String) = when (type.toUpperCase()) {
+ private fun getContactGroups(ezContact: VCard): ArrayList {
+ val groups = ArrayList()
+ if (ezContact.categories != null) {
+ val groupNames = ezContact.categories.values
+
+ if (groupNames != null) {
+ val storedGroups = ContactsHelper(activity).getStoredGroups()
+
+ groupNames.forEach {
+ val groupName = it
+ val storedGroup = storedGroups.firstOrNull { it.title == groupName }
+
+ if (storedGroup != null) {
+ groups.add(storedGroup)
+ } else {
+ val newContactGroup = activity.dbHelper.insertGroup(Group(0, groupName))
+
+ if (newContactGroup != null) {
+ groups.add(newContactGroup)
+ }
+ }
+ }
+ }
+ }
+ return groups
+ }
+
+ private fun getPhoneNumberTypeId(type: String, subtype: String?) = when (type.toUpperCase()) {
CELL -> CommonDataKinds.Phone.TYPE_MOBILE
- HOME -> CommonDataKinds.Phone.TYPE_HOME
- WORK -> CommonDataKinds.Phone.TYPE_WORK
+ HOME -> {
+ if (subtype?.toUpperCase() == FAX) {
+ CommonDataKinds.Phone.TYPE_FAX_HOME
+ } else {
+ CommonDataKinds.Phone.TYPE_HOME
+ }
+ }
+ WORK -> {
+ if (subtype?.toUpperCase() == FAX) {
+ CommonDataKinds.Phone.TYPE_FAX_WORK
+ } else {
+ CommonDataKinds.Phone.TYPE_WORK
+ }
+ }
PREF, MAIN -> CommonDataKinds.Phone.TYPE_MAIN
WORK_FAX -> CommonDataKinds.Phone.TYPE_FAX_WORK
HOME_FAX -> CommonDataKinds.Phone.TYPE_FAX_HOME
FAX -> CommonDataKinds.Phone.TYPE_FAX_WORK
PAGER -> CommonDataKinds.Phone.TYPE_PAGER
- else -> CommonDataKinds.Phone.TYPE_OTHER
+ OTHER -> CommonDataKinds.Phone.TYPE_OTHER
+ else -> CommonDataKinds.Phone.TYPE_CUSTOM
}
private fun getEmailTypeId(type: String) = when (type.toUpperCase()) {
HOME -> CommonDataKinds.Email.TYPE_HOME
WORK -> CommonDataKinds.Email.TYPE_WORK
MOBILE -> CommonDataKinds.Email.TYPE_MOBILE
- else -> CommonDataKinds.Email.TYPE_OTHER
+ OTHER -> CommonDataKinds.Email.TYPE_OTHER
+ else -> CommonDataKinds.Email.TYPE_CUSTOM
}
private fun getAddressTypeId(type: String) = when (type.toUpperCase()) {
- HOME -> CommonDataKinds.Email.TYPE_HOME
- WORK -> CommonDataKinds.Email.TYPE_WORK
- else -> CommonDataKinds.Email.TYPE_OTHER
+ HOME -> CommonDataKinds.StructuredPostal.TYPE_HOME
+ WORK -> CommonDataKinds.StructuredPostal.TYPE_WORK
+ OTHER -> CommonDataKinds.StructuredPostal.TYPE_OTHER
+ else -> CommonDataKinds.StructuredPostal.TYPE_CUSTOM
}
private fun savePhoto(byteArray: ByteArray?): String {
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/models/Address.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/models/Address.kt
index 7571a228..cc07321b 100644
--- a/app/src/main/kotlin/com/simplemobiletools/contacts/models/Address.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/models/Address.kt
@@ -1,3 +1,3 @@
package com.simplemobiletools.contacts.models
-data class Address(var value: String, var type: Int)
+data class Address(var value: String, var type: Int, var label: String)
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/models/Contact.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/models/Contact.kt
index 6a18a265..c710d572 100644
--- a/app/src/main/kotlin/com/simplemobiletools/contacts/models/Contact.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/models/Contact.kt
@@ -5,12 +5,14 @@ import com.simplemobiletools.commons.extensions.normalizeString
import com.simplemobiletools.commons.helpers.SORT_BY_FIRST_NAME
import com.simplemobiletools.commons.helpers.SORT_BY_MIDDLE_NAME
import com.simplemobiletools.commons.helpers.SORT_DESCENDING
-import com.simplemobiletools.contacts.helpers.PHONE_NUMBER_PATTERN
+import com.simplemobiletools.contacts.extensions.applyRegexFiltering
data class Contact(val id: Int, var prefix: String, var firstName: String, var middleName: String, var surname: String, var suffix: String, var nickname: String,
var photoUri: String, var phoneNumbers: ArrayList, var emails: ArrayList, var addresses: ArrayList,
var events: ArrayList, var source: String, var starred: Int, val contactId: Int, val thumbnailUri: String, var photo: Bitmap?, var notes: String,
- var groups: ArrayList, var organization: Organization, var websites: ArrayList) : Comparable {
+ var groups: ArrayList, var organization: Organization, var websites: ArrayList, var cleanPhoneNumbers: ArrayList,
+ var IMs: ArrayList) :
+ Comparable {
companion object {
var sorting = 0
var startWithSurname = false
@@ -92,15 +94,33 @@ data class Contact(val id: Int, var prefix: String, var firstName: String, var m
}
}
- fun getHashToCompare(): Int {
- val newPhoneNumbers = ArrayList()
- phoneNumbers.mapTo(newPhoneNumbers) { PhoneNumber(it.value.replace(PHONE_NUMBER_PATTERN.toRegex(), ""), 0) }
-
+ fun getStringToCompare(): String {
val newEmails = ArrayList()
- emails.mapTo(newEmails) { Email(it.value, 0) }
+ emails.mapTo(newEmails) { Email(it.value, 0, "") }
return copy(id = 0, prefix = "", firstName = getFullName().toLowerCase(), middleName = "", surname = "", suffix = "", nickname = "", photoUri = "",
- phoneNumbers = newPhoneNumbers, events = ArrayList(), addresses = ArrayList(), emails = newEmails, source = "", starred = 0,
- contactId = 0, thumbnailUri = "", notes = "", groups = ArrayList(), websites = ArrayList(), organization = Organization("", "")).hashCode()
+ phoneNumbers = ArrayList(), events = ArrayList(), addresses = ArrayList(), emails = newEmails, source = "", starred = 0,
+ contactId = 0, thumbnailUri = "", notes = "", groups = ArrayList(), websites = ArrayList(), organization = Organization("", ""),
+ IMs = ArrayList()).toString()
+ }
+
+ fun getHashToCompare() = getStringToCompare().hashCode()
+
+ // do a more advanced phone number check here, compare numbers and and search query with dashes, spaces and everything but numbers removed
+ fun doesContainPhoneNumber(text: String): Boolean {
+ if (text.isNotEmpty()) {
+ if (phoneNumbers.any { it.value.contains(text) } || cleanPhoneNumbers.any { it.value.contains(text) }) {
+ return true
+ }
+ }
+
+ val filteredNumber = text.applyRegexFiltering()
+ if (filteredNumber.isNotEmpty()) {
+ if (phoneNumbers.any { it.value.contains(filteredNumber) } || cleanPhoneNumbers.any { it.value.contains(filteredNumber) }) {
+ return true
+ }
+ }
+
+ return false
}
}
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/models/Email.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/models/Email.kt
index 268bf7e9..caeafc0d 100644
--- a/app/src/main/kotlin/com/simplemobiletools/contacts/models/Email.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/models/Email.kt
@@ -1,3 +1,3 @@
package com.simplemobiletools.contacts.models
-data class Email(var value: String, var type: Int)
+data class Email(var value: String, var type: Int, var label: String)
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/models/IM.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/models/IM.kt
new file mode 100644
index 00000000..f646e9f4
--- /dev/null
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/models/IM.kt
@@ -0,0 +1,3 @@
+package com.simplemobiletools.contacts.models
+
+data class IM(var value: String, var type: Int, var label: String)
diff --git a/app/src/main/kotlin/com/simplemobiletools/contacts/models/PhoneNumber.kt b/app/src/main/kotlin/com/simplemobiletools/contacts/models/PhoneNumber.kt
index e1eee4cf..1dd39896 100644
--- a/app/src/main/kotlin/com/simplemobiletools/contacts/models/PhoneNumber.kt
+++ b/app/src/main/kotlin/com/simplemobiletools/contacts/models/PhoneNumber.kt
@@ -1,3 +1,3 @@
package com.simplemobiletools.contacts.models
-data class PhoneNumber(var value: String, var type: Int)
+data class PhoneNumber(var value: String, var type: Int, var label: String)
diff --git a/app/src/main/res/drawable-hdpi/ic_clear_char.png b/app/src/main/res/drawable-hdpi/ic_clear_char.png
new file mode 100644
index 00000000..3fd9c535
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_clear_char.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_dialpad.png b/app/src/main/res/drawable-hdpi/ic_dialpad.png
new file mode 100644
index 00000000..f600b5b7
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_dialpad.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_im.png b/app/src/main/res/drawable-hdpi/ic_im.png
new file mode 100644
index 00000000..7a16bc7c
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_im.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_new_contact.png b/app/src/main/res/drawable-hdpi/ic_new_contact.png
new file mode 100644
index 00000000..ded0ae92
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_new_contact.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_clear_char.png b/app/src/main/res/drawable-xhdpi/ic_clear_char.png
new file mode 100644
index 00000000..b51790d1
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_clear_char.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_dialpad.png b/app/src/main/res/drawable-xhdpi/ic_dialpad.png
new file mode 100644
index 00000000..793da560
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_dialpad.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_im.png b/app/src/main/res/drawable-xhdpi/ic_im.png
new file mode 100644
index 00000000..175610f8
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_im.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_new_contact.png b/app/src/main/res/drawable-xhdpi/ic_new_contact.png
new file mode 100644
index 00000000..5637f091
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_new_contact.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_clear_char.png b/app/src/main/res/drawable-xxhdpi/ic_clear_char.png
new file mode 100644
index 00000000..e049a108
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_clear_char.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_dialpad.png b/app/src/main/res/drawable-xxhdpi/ic_dialpad.png
new file mode 100644
index 00000000..1f6bf856
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_dialpad.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_im.png b/app/src/main/res/drawable-xxhdpi/ic_im.png
new file mode 100644
index 00000000..822a47dd
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_im.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_new_contact.png b/app/src/main/res/drawable-xxhdpi/ic_new_contact.png
new file mode 100644
index 00000000..d33121aa
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_new_contact.png differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_clear_char.png b/app/src/main/res/drawable-xxxhdpi/ic_clear_char.png
new file mode 100644
index 00000000..ea552fd3
Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_clear_char.png differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_dialpad.png b/app/src/main/res/drawable-xxxhdpi/ic_dialpad.png
new file mode 100644
index 00000000..7754a7c9
Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_dialpad.png differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_im.png b/app/src/main/res/drawable-xxxhdpi/ic_im.png
new file mode 100644
index 00000000..8d75838c
Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_im.png differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_new_contact.png b/app/src/main/res/drawable-xxxhdpi/ic_new_contact.png
new file mode 100644
index 00000000..07435b94
Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_new_contact.png differ
diff --git a/app/src/main/res/layout/activity_dialpad.xml b/app/src/main/res/layout/activity_dialpad.xml
new file mode 100644
index 00000000..6378918f
--- /dev/null
+++ b/app/src/main/res/layout/activity_dialpad.xml
@@ -0,0 +1,246 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_edit_contact.xml b/app/src/main/res/layout/activity_edit_contact.xml
index 15c77a7c..fa6e8eb7 100644
--- a/app/src/main/res/layout/activity_edit_contact.xml
+++ b/app/src/main/res/layout/activity_edit_contact.xml
@@ -299,6 +299,44 @@
android:paddingTop="@dimen/medium_margin"
android:src="@drawable/ic_plus"/>
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_insert_edit_contact.xml b/app/src/main/res/layout/activity_insert_edit_contact.xml
new file mode 100644
index 00000000..50edfe57
--- /dev/null
+++ b/app/src/main/res/layout/activity_insert_edit_contact.xml
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 1c26ff25..f3b5d8e9 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -22,4 +22,16 @@
android:layout_height="match_parent"
android:layout_below="@+id/main_tabs_holder"/>
+
+
diff --git a/app/src/main/res/layout/activity_view_contact.xml b/app/src/main/res/layout/activity_view_contact.xml
index e471ae49..74ce627d 100644
--- a/app/src/main/res/layout/activity_view_contact.xml
+++ b/app/src/main/res/layout/activity_view_contact.xml
@@ -241,6 +241,26 @@
android:orientation="vertical"
android:paddingLeft="@dimen/small_margin"/>
+
+
+
+
diff --git a/app/src/main/res/layout/dialog_custom_label.xml b/app/src/main/res/layout/dialog_custom_label.xml
new file mode 100644
index 00000000..746ae0cd
--- /dev/null
+++ b/app/src/main/res/layout/dialog_custom_label.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/dialog_manage_visible_fields.xml b/app/src/main/res/layout/dialog_manage_visible_fields.xml
index 599fdb88..477e1210 100644
--- a/app/src/main/res/layout/dialog_manage_visible_fields.xml
+++ b/app/src/main/res/layout/dialog_manage_visible_fields.xml
@@ -86,6 +86,14 @@
android:paddingTop="@dimen/activity_margin"
android:text="@string/addresses"/>
+
+
@@ -31,15 +32,15 @@
android:layout_toRightOf="@+id/contact_tmb"
android:ellipsize="end"
android:maxLines="1"
- android:textSize="@dimen/bigger_text_size"
+ android:textSize="@dimen/big_text_size"
tools:text="John Doe"/>
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/item_recent_call.xml b/app/src/main/res/layout/item_recent_call.xml
index 61334288..14c60772 100644
--- a/app/src/main/res/layout/item_recent_call.xml
+++ b/app/src/main/res/layout/item_recent_call.xml
@@ -14,16 +14,16 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/menu/menu_dialpad.xml b/app/src/main/res/menu/menu_dialpad.xml
new file mode 100644
index 00000000..66fa1d06
--- /dev/null
+++ b/app/src/main/res/menu/menu_dialpad.xml
@@ -0,0 +1,9 @@
+
+
diff --git a/app/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml
index d197f75f..8f2f3892 100644
--- a/app/src/main/res/values-az/strings.xml
+++ b/app/src/main/res/values-az/strings.xml
@@ -15,6 +15,8 @@
Grupa e-poçt göndər
%s şəxsinə zng et
Lazım olan icazələri istə
+ Create new contact
+ Add to an existing contact
Yeni kontakt
Redaktə et
@@ -95,6 +97,10 @@
Kontakt köklərini daxil et
Fayl adı (.vcf olmadan)
+
+ Dialpad
+ Add number to contact
+
Göstərmək üçün sahəni seç
Ön şəkilçi
@@ -108,6 +114,7 @@
Vebsaytlar
Qruplar
Kontakt kökü
+ Instant messaging (IM)
I want to change what fields are visible at contacts. Can I do it?
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 74ec5350..ec3adbe4 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -15,6 +15,8 @@
Sende E-Mail an Gruppe
%s anrufen
Benötigte Berechtigungen anfordern
+ Create new contact
+ Add to an existing contact
Neuer Kontakt
Kontakt bearbeiten
@@ -95,6 +97,10 @@
Kontaktquellen einschließen
Dateiname (ohne .vcf)
+
+ Dialpad
+ Add number to contact
+
Sichtbare Felder auswählen
Titel
@@ -108,6 +114,7 @@
Webseiten
Gruppen
Kontaktquelle
+ Instant messaging (IM)
Ich möchte die sichtbaren Kontaktfelder ändern. Kann ich das machen?
diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml
index 34fd69ec..b548d329 100644
--- a/app/src/main/res/values-el/strings.xml
+++ b/app/src/main/res/values-el/strings.xml
@@ -15,6 +15,8 @@
Αποστολή email σε ομάδες
Κλήση %s
Ζητούνται τα απαιτούμενα δικαιώματα
+ Create new contact
+ Add to an existing contact
Νέα επαφή
Επεξεργασία επαφής
@@ -95,6 +97,10 @@
Συμπερίληψη πηγών επαφών
Όνομα αρχείου (χωρίς .vcf)
+
+ Dialpad
+ Add number to contact
+
Επιλογή εμφάνισης πεδίων
Πρόθεμα
@@ -108,6 +114,7 @@
Ιστοσελίδα
Ομάδες
Προέλευση επαφής
+ Instant messaging (IM)
Θέλω να αλλάξω τα πεδία που θα είναι ορατά στις επαφές. Μπορώ να το κάνω?
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index c8ecf2f7..b3dc4634 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -14,7 +14,9 @@
Envoyer un SMS au groupe
Envoyer un e-mail au groupe
Call %s
- Request the required permissions
+ Demander les autorisations requises
+ Create new contact
+ Add to an existing contact
Nouveau contact
Modifier contact
@@ -23,7 +25,7 @@
Prénom
Deuxième prénom
Nom
- Nickname
+ Surnom
Pas de groupe
@@ -49,12 +51,12 @@
Appeler le contact
Voir les détails du contact
Configurer l\'affichage des champs des contacts
- Try filtering out duplicate contacts
- Manage shown tabs
+ Essayez de filtrer les contacts en double
+ Gérer les onglets affichés
Contacts
Favorites
- Recent calls
- Show a call confirmation dialog before initiating a call
+ Appels récents
+ Afficher une boîte de dialogue de confirmation d\'appel avant de lancer un appel
E-mail
@@ -80,7 +82,7 @@
Ajouter des favoris
Ajouter aux favoris
Retirer des favoris
- You must be at the Edit screen to modify a contact
+ Vous devez être sur l\'écran Modifier pour modifier un contact
Rechercher des contacts
@@ -95,6 +97,10 @@
Inclure les sources du contact
Nom du fichier (sans .vcf)
+
+ Dialpad
+ Add number to contact
+
Sélectionner les champs à afficher
Préfixe
@@ -108,6 +114,7 @@
Sites web
Groupe
Source du contact
+ Messagerie instantanée (MI)
Je veux changer quelles champs sont visibles. Est-ce que je peux ?
diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml
index e3b2bb3f..d5c8544e 100644
--- a/app/src/main/res/values-hr/strings.xml
+++ b/app/src/main/res/values-hr/strings.xml
@@ -9,12 +9,14 @@
Tvrtka
Radno mjesto
Web stranica
- Send SMS to contacts
- Send email to contacts
- Send SMS to group
- Send email to group
- Call %s
- Request the required permissions
+ Pošalji SMS kontaktima
+ Pošalji e-poštu kontaktima
+ Pošalji SMS grupi
+ Pošalji e-poštu grupi
+ Nazovi %s
+ Zatraži potrebna dopuštenja
+ Create new contact
+ Add to an existing contact
Novi kontakt
Uredi kontakt
@@ -48,13 +50,13 @@
Prilikom dodira kontakta
Nazovi kontakt
Prikaži pojedinosti o kontaktu
- Manage shown contact fields
- Try filtering out duplicate contacts
- Manage shown tabs
- Contacts
- Favorites
- Recent calls
- Show a call confirmation dialog before initiating a call
+ Upravljaj prikazanim poljima kontakta
+ Pokušaj filtrirati duple kontakte
+ Upravljaj prikazanim karticama
+ Kontakti
+ Favoriti
+ Nedavni pozivi
+ Pokažite dijaloški okvir za potvrdu poziva prije pokretanja poziva
E-pošta
@@ -80,7 +82,7 @@
Dodaj favorite
Dodaj u favorite
Ukloni iz favorita
- You must be at the Edit screen to modify a contact
+ Morate biti na zaslonu Uređivanje da biste izmijenili kontakt
Pretraži kontakte
@@ -95,6 +97,10 @@
Uključi izvore kontakta
Naziv datoteke (bez .vcf)
+
+ Dialpad
+ Add number to contact
+
Odaberi polja za prikaz
Prefiks
@@ -108,6 +114,7 @@
Web stranice
Grupe
Izvori kontakata
+ Brzo slanje poruka (IM)
Želim promijeniti polja koja su vidljiva na kontaktima. Mogu li to napraviti?
diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml
index 3235994a..e87a4211 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -15,6 +15,8 @@
グループにメールを送信
Call %s
Request the required permissions
+ Create new contact
+ Add to an existing contact
新しい連絡先
連絡先を編集
@@ -95,6 +97,10 @@
Include contact sources
ファイル名 (.vcfを含めない)
+
+ ダイヤルパッド
+ Add number to contact
+
表示する項目を選択
敬称(名前の前)
@@ -108,6 +114,7 @@
ウェブサイト
グループ
インポート元
+ Instant messaging (IM)
連絡先に表示される項目(フィールド)を変更することはできますか?
diff --git a/app/src/main/res/values-ko-rKR/strings.xml b/app/src/main/res/values-ko-rKR/strings.xml
index 94819d86..4c7bbb68 100644
--- a/app/src/main/res/values-ko-rKR/strings.xml
+++ b/app/src/main/res/values-ko-rKR/strings.xml
@@ -15,6 +15,8 @@
Send email to group
Call %s
Request the required permissions
+ Create new contact
+ Add to an existing contact
새로운 연락처
연락처 수정
@@ -95,6 +97,10 @@
가져오기 대상
파일이름 (.vcf 확장자 생략)
+
+ Dialpad
+ Add number to contact
+
Select fields to show
Prefix
@@ -108,6 +114,7 @@
Websites
Groups
Contact source
+ Instant messaging (IM)
I want to change what fields are visible at contacts. Can I do it?
diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml
index 4dbd270c..e99c1beb 100644
--- a/app/src/main/res/values-lt/strings.xml
+++ b/app/src/main/res/values-lt/strings.xml
@@ -15,6 +15,8 @@
Send email to group
Call %s
Request the required permissions
+ Create new contact
+ Add to an existing contact
Naujas kontaktas
Redaguoti kontaktą
@@ -95,6 +97,10 @@
Įtraukti kontaktų šaltinius
Bylos vardas (be .vcf)
+
+ Dialpad
+ Add number to contact
+
Pasirinkti rodomus laukelius
Priešdėlis
@@ -108,6 +114,7 @@
Interneto puslapiai
Grupės
Kontakto šaltinis
+ Instant messaging (IM)
Noriu pakeisti, kokie laukai yra matomi kontaktuose. Ar galiu tai padaryti?
diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml
index f2a6d47f..de97bde8 100644
--- a/app/src/main/res/values-pt/strings.xml
+++ b/app/src/main/res/values-pt/strings.xml
@@ -14,7 +14,9 @@
Enviar SMS para o grupo
Enviar e-mail para o grupo
Ligar a %s
- Request the required permissions
+ Pedir a permissão necessária
+ Create new contact
+ Add to an existing contact
Novo contacto
Editar contacto
@@ -50,7 +52,7 @@
Ver detalhes
Gerir campos a exibir
Tentar filtrar contactos duplicados
- Manage shown tabs
+ Gerir separadores a exibir
Contactos
Favoritos
Chamadas recentes
@@ -76,7 +78,7 @@
Aniversário
- Parece que ainda não adicionou contactos como favoritos
+ Parece que ainda não adicionou contactos aos favoritos
Adicionar favoritos
Adicionar aos favoritos
Remover dos favoritos
@@ -95,6 +97,10 @@
Incluir fontes dos contactos
Nome do ficheiro (sem .vcf)
+
+ Dialpad
+ Add number to contact
+
Selecione os campos a mostrar
Prefixo
@@ -108,6 +114,7 @@
Site
Grupos
Origem do contacto
+ Mensagem instantânea (IM)
I want to change what fields are visible at contacts. Can I do it?
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index c4cc2259..cd412417 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -15,6 +15,8 @@
Отправить письмо группе
Вызов %s
Запрос необходимых разрешений
+ Create new contact
+ Add to an existing contact
Новый контакт
Редактировать контакт
@@ -95,6 +97,10 @@
Включить источники контактов
Имя файла (без .vcf)
+
+ Dialpad
+ Add number to contact
+
Выберите отображаемые поля
Префикс
@@ -108,6 +114,7 @@
Сайты
Группы
Источник контакта
+ Мессенджеры (IM)
Я хочу изменить поля, отображаемые у контактов. Могу ли я это сделать?
diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml
index 6e3f3d25..ac542986 100644
--- a/app/src/main/res/values-sk/strings.xml
+++ b/app/src/main/res/values-sk/strings.xml
@@ -15,6 +15,8 @@
Poslať skupine email
Zavolať %s
Vyžiadať potrebné oprávnenia
+ Vytvoriť nový kontakt
+ Pridať k existujúcemu kontaktu
Nový kontakt
Upraviť kontakt
@@ -95,6 +97,10 @@
Zahrnúť zdroje kontaktov
Názov súboru (bez .vcf)
+
+ Číselník
+ Pridať číslo kontaktu
+
Zvoľte polia na zobrazenie
Titul pred menom
@@ -108,6 +114,7 @@
Webstránky
Skupiny
Zdroje kontaktov
+ Rýchle správy (IM)
Chcem upraviť viditeľné polia kontaktov. Dá sa to?
diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml
index c5f3e8a7..b2198c6e 100644
--- a/app/src/main/res/values-sv/strings.xml
+++ b/app/src/main/res/values-sv/strings.xml
@@ -15,6 +15,8 @@
Skicka e-post till grupp
Ring %s
Begär de behörigheter som krävs
+ Create new contact
+ Add to an existing contact
Ny kontakt
Redigera kontakt
@@ -95,6 +97,10 @@
Inkludera kontaktkällor
Filnamn (utan .vcf)
+
+ Dialpad
+ Add number to contact
+
Välj vilka fält som ska visas
Prefix
@@ -108,6 +114,7 @@
Webbplatser
Grupper
Kontaktkälla
+ Instant messaging (IM)
I want to change what fields are visible at contacts. Can I do it?
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml
index f849e887..583219d7 100644
--- a/app/src/main/res/values-tr/strings.xml
+++ b/app/src/main/res/values-tr/strings.xml
@@ -15,6 +15,8 @@
Gruba e-posta gönder
Call %s
Request the required permissions
+ Create new contact
+ Add to an existing contact
Yeni kişi
Kişiyi düzenle
@@ -95,6 +97,10 @@
Kişi kaynaklarını dahil et
Dosya adı (.vcf olmadan)
+
+ Dialpad
+ Add number to contact
+
Görüntülenecek alanları seç
Önek
@@ -108,6 +114,7 @@
Web siteleri
Gruplar
Kişi kaynağı
+ Instant messaging (IM)
Rehberde görüntülenecek alanları değiştirmek istiyorum. Bunu yapabilir miyim?
diff --git a/app/src/main/res/values-v21/styles.xml b/app/src/main/res/values-v21/styles.xml
new file mode 100644
index 00000000..afdc4448
--- /dev/null
+++ b/app/src/main/res/values-v21/styles.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
index c6f1b48b..1fe54476 100644
--- a/app/src/main/res/values-zh-rTW/strings.xml
+++ b/app/src/main/res/values-zh-rTW/strings.xml
@@ -15,6 +15,8 @@
發送電子郵件給群組
打電話給 %s
請求必要的權限
+ Create new contact
+ Add to an existing contact
新聯絡人
編輯聯絡人
@@ -95,6 +97,10 @@
包含聯絡人來源
檔案名稱 (不含.vcf)
+
+ Dialpad
+ Add number to contact
+
選擇要顯示的欄位
前缀
@@ -108,6 +114,7 @@
網站
群組
聯絡人來源
+ Instant messaging (IM)
我想要更改在通訊錄會看到哪些欄位。我能這麼做嗎?
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index 4061e3b5..b0496177 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -4,4 +4,8 @@
45dp
40dp
52dp
+ 56dp
+ 68dp
+ 60dp
+ 34sp
diff --git a/app/src/main/res/values/donottranslate.xml b/app/src/main/res/values/donottranslate.xml
index aa83af5e..c3d9f092 100644
--- a/app/src/main/res/values/donottranslate.xml
+++ b/app/src/main/res/values/donottranslate.xml
@@ -1,7 +1,22 @@
+ IM
+ AIM
+ Windows Live
+ Yahoo
+ Skype
+ QQ
+ Hangouts
+ ICQ
+ Jabber
+
+ Added a simple dialpad, dialer will come soon
+
+ Added an optional IM field\n
+ Added support for custom phone number/email/address types
+
Added an optional Nickname field
Allow customizing which tabs are visible\n
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index d0c4b5f5..53be90a2 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -15,6 +15,8 @@
Send email to group
Call %s
Request the required permissions
+ Create new contact
+ Add to an existing contact
New contact
Edit contact
@@ -95,6 +97,10 @@
Include contact sources
Filename (without .vcf)
+
+ Dialpad
+ Add number to contact
+
Select fields to show
Prefix
@@ -108,6 +114,7 @@
Websites
Groups
Contact source
+ Instant messaging (IM)
I want to change what fields are visible at contacts. Can I do it?
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 0be50415..b7e0377a 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -2,4 +2,15 @@
+
+
+
+
diff --git a/build.gradle b/build.gradle
index 1b8fb3c3..76ac6173 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,7 +1,7 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
- ext.kotlin_version = '1.2.61'
+ ext.kotlin_version = '1.2.71'
repositories {
google()
@@ -9,7 +9,7 @@ buildscript {
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.1.4'
+ classpath 'com.android.tools.build:gradle:3.2.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 7d73f3c2..503fd67b 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Sun Dec 10 10:08:39 CET 2017
+#Tue Sep 25 13:09:34 CEST 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-4.4.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip