Merge pull request #16 from SimpleMobileTools/master

upd
This commit is contained in:
solokot 2018-09-29 16:52:12 +03:00 committed by GitHub
commit d454b28ad0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
78 changed files with 1735 additions and 204 deletions

View File

@ -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)*
----------------------------

View File

@ -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()

View File

@ -14,6 +14,10 @@
<uses-permission android:name="android.permission.READ_CALL_LOG"/>
<uses-permission android:name="android.permission.WRITE_CALL_LOG"/>
<uses-feature
android:name="android.hardware.telephony"
android:required="false"/>
<uses-permission
android:name="android.permission.USE_FINGERPRINT"
tools:node="remove"/>
@ -114,15 +118,6 @@
android:name=".activities.EditContactActivity"
android:parentActivityName=".activities.MainActivity">
<intent-filter>
<action android:name="android.intent.action.INSERT_OR_EDIT"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="vnd.android.cursor.item/person"/>
<data android:mimeType="vnd.android.cursor.item/contact"/>
<data android:mimeType="vnd.android.cursor.item/raw_contact"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.EDIT"/>
<category android:name="android.intent.category.DEFAULT"/>
@ -159,6 +154,20 @@
</intent-filter>
</activity>
<activity
android:name=".activities.InsertOrEditContactActivity"
android:parentActivityName=".activities.MainActivity">
<intent-filter>
<action android:name="android.intent.action.INSERT_OR_EDIT"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="vnd.android.cursor.item/person"/>
<data android:mimeType="vnd.android.cursor.item/contact"/>
<data android:mimeType="vnd.android.cursor.item/raw_contact"/>
</intent-filter>
</activity>
<activity
android:name="com.simplemobiletools.commons.activities.AboutActivity"
android:label="@string/about"
@ -179,6 +188,11 @@
android:label="@string/frequently_asked_questions"
android:parentActivityName="com.simplemobiletools.commons.activities.AboutActivity"/>
<activity
android:name=".activities.DialpadActivity"
android:label="@string/dialpad"
android:parentActivityName=".activities.MainActivity"/>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.provider"

View File

@ -3,16 +3,11 @@ package com.simplemobiletools.contacts
import android.app.Application
import com.facebook.stetho.Stetho
import com.simplemobiletools.commons.extensions.checkUseEnglish
import com.squareup.leakcanary.LeakCanary
class App : Application() {
override fun onCreate() {
super.onCreate()
if (BuildConfig.DEBUG) {
if (LeakCanary.isInAnalyzerProcess(this)) {
return
}
LeakCanary.install(this)
Stetho.initializeWithDefaults(this)
}

View File

@ -107,28 +107,63 @@ abstract class ContactActivity : SimpleActivity() {
}
}
fun getPhoneNumberTextId(type: Int) = 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 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) {

View File

@ -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<Contact>()
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<Contact>) {
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<Contact>
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)
}
}
}
}

View File

@ -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<ContentValues>("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<IM> {
val IMs = ArrayList<IM>()
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<Event> {
val unknown = getString(R.string.unknown)
val events = ArrayList<Event>()
@ -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
}
}

View File

@ -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>) {
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()
}
}
}

View File

@ -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)
}
}

View File

@ -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

View File

@ -29,12 +29,12 @@ import java.util.*
class ContactsAdapter(activity: SimpleActivity, var contactItems: ArrayList<Contact>, 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<Cont
findItem(R.id.cab_send_sms_to_contacts).isVisible = location == LOCATION_CONTACTS_TAB || location == LOCATION_FAVORITES_TAB || location == LOCATION_GROUP_CONTACTS
findItem(R.id.cab_send_email_to_contacts).isVisible = location == LOCATION_CONTACTS_TAB || location == LOCATION_FAVORITES_TAB || location == LOCATION_GROUP_CONTACTS
findItem(R.id.cab_delete).isVisible = location == LOCATION_CONTACTS_TAB || location == LOCATION_GROUP_CONTACTS
findItem(R.id.cab_select_all).isVisible = location != LOCATION_DIALPAD
findItem(R.id.cab_share).isVisible = location != LOCATION_DIALPAD
if (location == LOCATION_GROUP_CONTACTS) {
findItem(R.id.cab_remove).title = activity.getString(R.string.remove_from_group)
@ -105,7 +107,8 @@ class ContactsAdapter(activity: SimpleActivity, var contactItems: ArrayList<Cont
override fun onBindViewHolder(holder: MyRecyclerViewAdapter.ViewHolder, position: Int) {
val contact = contactItems[position]
val view = holder.bindView(contact, true, true) { itemView, layoutPosition ->
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<Cont
contact_name.setPadding(if (showContactThumbnails) smallPadding else bigPadding, smallPadding, smallPadding, 0)
if (contact_number != null) {
val numberText = contact.phoneNumbers.firstOrNull()?.value ?: ""
val phoneNumberToUse = if (textToHighlight.isEmpty()) {
contact.phoneNumbers.firstOrNull()
} else {
contact.phoneNumbers.firstOrNull { it.value.contains(textToHighlight) } ?: contact.phoneNumbers.firstOrNull()
}
val numberText = phoneNumberToUse?.value ?: ""
contact_number.text = if (textToHighlight.isEmpty()) numberText else numberText.highlightTextPart(textToHighlight, adjustedPrimaryColor)
contact_number.setTextColor(textColor)
contact_number.setPadding(if (showContactThumbnails) smallPadding else bigPadding, 0, smallPadding, 0)

View File

@ -7,10 +7,9 @@ import com.simplemobiletools.commons.extensions.applyColorFilter
import com.simplemobiletools.commons.extensions.setupDialogStuff
import com.simplemobiletools.contacts.R
import com.simplemobiletools.contacts.extensions.config
import com.simplemobiletools.contacts.models.Contact
import kotlinx.android.synthetic.main.dialog_call_confirmation.view.*
class CallConfirmationDialog(val activity: BaseSimpleActivity, val contact: Contact, private val callback: () -> 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))

View File

@ -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()
}
}
}
}
}

View File

@ -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)

View File

@ -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<RadioItem>()
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<String> {
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)
}
}

View File

@ -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())
})

View File

@ -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(), "")

View File

@ -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) ||

View File

@ -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
}

View File

@ -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",

View File

@ -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<Contact>(contactsSize)
(0 until contactsSize).mapTo(resultContacts) { contacts.valueAt(it) }
var tempContacts = ArrayList<Contact>(contactsSize)
val resultContacts = ArrayList<Contact>(contactsSize)
(0 until contactsSize).mapTo(tempContacts) { contacts.valueAt(it) }
if (activity.config.filterDuplicates) {
resultContacts = resultContacts.distinctBy {
tempContacts = tempContacts.distinctBy {
it.getHashToCompare()
} as ArrayList<Contact>
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<PhoneNumber>() // proper value is obtained below
val numbers = ArrayList<PhoneNumber>() // proper value is obtained below
val emails = ArrayList<Email>()
val addresses = ArrayList<Address>()
val events = ArrayList<Event>()
@ -127,8 +140,10 @@ class ContactsHelper(val activity: Activity) {
val groups = ArrayList<Group>()
val organization = Organization("", "")
val websites = ArrayList<String>()
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<PhoneNumber>()
val ims = ArrayList<IM>()
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<ArrayList<IM>> {
val IMs = SparseArray<ArrayList<IM>>()
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<ArrayList<Event>> {
val events = SparseArray<ArrayList<Event>>()
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<PhoneNumber>()
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())
}
}

View File

@ -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<String>? = null): ArrayList<Contact> {
val storedGroups = ContactsHelper(activity).getStoredGroups()
val filterDuplicates = activity.config.filterDuplicates
val contacts = ArrayList<Contact>()
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<List<PhoneNumber>>() {}.type
val emailsToken = object : TypeToken<List<Email>>() {}.type
val addressesToken = object : TypeToken<List<Address>>() {}.type
val IMsToken = object : TypeToken<List<IM>>() {}.type
val eventsToken = object : TypeToken<List<Event>>() {}.type
val groupIdsToken = object : TypeToken<List<Long>>() {}.type
val websitesToken = object : TypeToken<List<String>>() {}.type
@ -283,14 +293,30 @@ class DBHelper private constructor(val context: Context) : SQLiteOpenHelper(cont
val phoneNumbers = if (phoneNumbersJson == "[]") ArrayList() else gson.fromJson<ArrayList<PhoneNumber>>(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<ArrayList<Email>>(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<ArrayList<Address>>(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<ArrayList<IM>>(IMsJson, IMsToken) ?: ArrayList(1)
val eventsJson = cursor.getStringValue(COL_EVENTS)
val events = if (eventsJson == "[]") ArrayList() else gson.fromJson<ArrayList<Event>>(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<ArrayList<String>>(websitesJson, websitesToken)
?: ArrayList(1)
val cleanPhoneNumbers = ArrayList<PhoneNumber>()
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)
}
}

View File

@ -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
}
}

View File

@ -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<PhoneNumber>()
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<Email>()
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<Address>()
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<Group>()
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<String>
val photoData = ezContact.photos.firstOrNull()?.data
val photo = null
val thumbnailUri = savePhoto(photoData)
val cleanPhoneNumbers = ArrayList<PhoneNumber>()
val IMs = ArrayList<IM>()
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<Group> {
val groups = ArrayList<Group>()
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 {

View File

@ -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)

View File

@ -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<PhoneNumber>, var emails: ArrayList<Email>, var addresses: ArrayList<Address>,
var events: ArrayList<Event>, var source: String, var starred: Int, val contactId: Int, val thumbnailUri: String, var photo: Bitmap?, var notes: String,
var groups: ArrayList<Group>, var organization: Organization, var websites: ArrayList<String>) : Comparable<Contact> {
var groups: ArrayList<Group>, var organization: Organization, var websites: ArrayList<String>, var cleanPhoneNumbers: ArrayList<PhoneNumber>,
var IMs: ArrayList<IM>) :
Comparable<Contact> {
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<PhoneNumber>()
phoneNumbers.mapTo(newPhoneNumbers) { PhoneNumber(it.value.replace(PHONE_NUMBER_PATTERN.toRegex(), ""), 0) }
fun getStringToCompare(): String {
val newEmails = ArrayList<Email>()
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
}
}

View File

@ -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)

View File

@ -0,0 +1,3 @@
package com.simplemobiletools.contacts.models
data class IM(var value: String, var type: Int, var label: String)

View File

@ -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)

Binary file not shown.

After

Width:  |  Height:  |  Size: 437 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 332 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 269 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 524 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 395 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 322 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 732 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 408 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 421 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 919 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 336 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 533 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 578 B

View File

@ -0,0 +1,246 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/dialpad_holder"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentBottom="true"
android:focusableInTouchMode="true">
<com.simplemobiletools.commons.views.MyRecyclerView
android:id="@+id/dialpad_list"
android:layout_width="match_parent"
android:layout_height="0dp"
android:clipToPadding="false"
android:scrollbars="none"
app:layoutManager="com.simplemobiletools.commons.views.MyLinearLayoutManager"
app:layout_constraintBottom_toTopOf="@+id/dialpad_input"
app:layout_constraintTop_toTopOf="parent"/>
<com.simplemobiletools.commons.views.FastScroller
android:id="@+id/dialpad_fastscroller"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:paddingStart="@dimen/normal_margin"
android:paddingLeft="@dimen/normal_margin"
app:layout_constraintBottom_toBottomOf="@+id/dialpad_list"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="@+id/dialpad_list">
<include layout="@layout/fastscroller_handle_vertical"/>
</com.simplemobiletools.commons.views.FastScroller>
<TextView
android:id="@+id/dialpad_divider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@drawable/divider"
app:layout_constraintBottom_toTopOf="@+id/dialpad_input"/>
<com.simplemobiletools.commons.views.MyEditText
android:id="@+id/dialpad_input"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/activity_margin"
android:gravity="center"
android:inputType="phone"
android:textCursorDrawable="@null"
android:textSize="@dimen/dialpad_text_size"
app:layout_constraintBottom_toTopOf="@+id/dialpad_2"
app:layout_constraintEnd_toStartOf="@+id/dialpad_clear_char"
app:layout_constraintStart_toStartOf="parent"/>
<ImageView
android:id="@+id/dialpad_clear_char"
style="@style/MyBorderlessBackgroundStyle"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginRight="@dimen/activity_margin"
android:paddingLeft="@dimen/activity_margin"
android:paddingRight="@dimen/activity_margin"
android:src="@drawable/ic_clear_char"
app:layout_constraintBottom_toBottomOf="@+id/dialpad_input"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/dialpad_input"/>
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/dialpad_1"
style="@style/DialpadNumberStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/activity_margin"
android:text="1"
app:layout_constraintBottom_toTopOf="@+id/dialpad_4"
app:layout_constraintEnd_toStartOf="@+id/dialpad_2"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"/>
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/dialpad_2"
style="@style/DialpadNumberStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="2"
app:layout_constraintBottom_toTopOf="@+id/dialpad_5"
app:layout_constraintEnd_toStartOf="@+id/dialpad_3"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/dialpad_1"/>
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/dialpad_3"
style="@style/DialpadNumberStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginRight="@dimen/activity_margin"
android:text="3"
app:layout_constraintBottom_toTopOf="@+id/dialpad_6"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/dialpad_2"/>
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/dialpad_4"
style="@style/DialpadNumberStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/activity_margin"
android:text="4"
app:layout_constraintBottom_toTopOf="@+id/dialpad_7"
app:layout_constraintEnd_toStartOf="@+id/dialpad_5"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"/>
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/dialpad_5"
style="@style/DialpadNumberStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="5"
app:layout_constraintBottom_toTopOf="@+id/dialpad_8"
app:layout_constraintEnd_toStartOf="@+id/dialpad_6"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/dialpad_4"/>
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/dialpad_6"
style="@style/DialpadNumberStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginRight="@dimen/activity_margin"
android:text="6"
app:layout_constraintBottom_toTopOf="@+id/dialpad_9"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/dialpad_5"/>
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/dialpad_7"
style="@style/DialpadNumberStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/activity_margin"
android:text="7"
app:layout_constraintBottom_toTopOf="@+id/dialpad_asterisk"
app:layout_constraintEnd_toStartOf="@+id/dialpad_8"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"/>
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/dialpad_8"
style="@style/DialpadNumberStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="8"
app:layout_constraintBottom_toTopOf="@+id/dialpad_0_holder"
app:layout_constraintEnd_toStartOf="@+id/dialpad_9"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/dialpad_7"/>
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/dialpad_9"
style="@style/DialpadNumberStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginRight="@dimen/activity_margin"
android:text="9"
app:layout_constraintBottom_toTopOf="@+id/dialpad_hashtag"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/dialpad_8"/>
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/dialpad_asterisk"
style="@style/DialpadNumberStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/activity_margin"
android:text="*"
app:layout_constraintBottom_toTopOf="@+id/dialpad_call_button"
app:layout_constraintEnd_toStartOf="@+id/dialpad_0_holder"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"/>
<RelativeLayout
android:id="@+id/dialpad_0_holder"
style="@style/MyBorderlessBackgroundStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toTopOf="@+id/dialpad_call_button"
app:layout_constraintEnd_toStartOf="@+id/dialpad_hashtag"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/dialpad_asterisk">
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/dialpad_0"
style="@style/DialpadNumberStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:text="0"/>
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/dialpad_plus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@+id/dialpad_0"
android:layout_alignBottom="@+id/dialpad_0"
android:layout_centerHorizontal="true"
android:layout_toRightOf="@+id/dialpad_0"
android:gravity="center"
android:paddingLeft="@dimen/small_margin"
android:paddingTop="@dimen/small_margin"
android:text="+"
android:textSize="@dimen/actionbar_text_size"/>
</RelativeLayout>
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/dialpad_hashtag"
style="@style/DialpadNumberStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginRight="@dimen/activity_margin"
android:text="#"
app:layout_constraintBottom_toTopOf="@+id/dialpad_call_button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/dialpad_0_holder"/>
<ImageView
android:id="@+id/dialpad_call_button"
android:layout_width="@dimen/dialpad_button_size"
android:layout_height="@dimen/dialpad_button_size"
android:layout_marginBottom="@dimen/activity_margin"
android:background="@drawable/circle_background"
android:elevation="@dimen/medium_margin"
android:padding="@dimen/normal_margin"
android:src="@drawable/ic_phone_big"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
</android.support.constraint.ConstraintLayout>

View File

@ -299,6 +299,44 @@
android:paddingTop="@dimen/medium_margin"
android:src="@drawable/ic_plus"/>
<ImageView
android:id="@+id/contact_ims_image"
android:layout_width="@dimen/contact_icons_size"
android:layout_height="@dimen/contact_icons_size"
android:layout_alignTop="@+id/contact_ims_holder"
android:paddingBottom="@dimen/small_margin"
android:paddingEnd="@dimen/small_margin"
android:paddingRight="@dimen/small_margin"
android:paddingTop="@dimen/medium_margin"
android:src="@drawable/ic_im"/>
<LinearLayout
android:id="@+id/contact_ims_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/contact_addresses_add_new"
android:layout_marginTop="@dimen/medium_margin"
android:layout_toRightOf="@+id/contact_name_image"
android:orientation="vertical">
<include layout="@layout/item_edit_im"/>
</LinearLayout>
<ImageView
android:id="@+id/contact_ims_add_new"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/contact_ims_holder"
android:layout_centerHorizontal="true"
android:layout_marginTop="@dimen/small_margin"
android:background="@drawable/button_background"
android:paddingBottom="@dimen/medium_margin"
android:paddingLeft="@dimen/activity_margin"
android:paddingRight="@dimen/activity_margin"
android:paddingTop="@dimen/medium_margin"
android:src="@drawable/ic_plus"/>
<ImageView
android:id="@+id/contact_events_image"
android:layout_width="@dimen/contact_icons_size"
@ -314,7 +352,7 @@
android:id="@+id/contact_events_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/contact_addresses_add_new"
android:layout_below="@+id/contact_ims_add_new"
android:layout_marginTop="@dimen/medium_margin"
android:layout_toRightOf="@+id/contact_name_image"
android:orientation="vertical">

View File

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/insert_edit_contact_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RelativeLayout
android:id="@+id/new_contact_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:paddingRight="@dimen/activity_margin">
<ImageView
android:id="@+id/new_contact_tmb"
android:layout_width="@dimen/normal_icon_size"
android:layout_height="@dimen/normal_icon_size"
android:layout_alignTop="@+id/new_contact_name"
android:layout_alignBottom="@+id/new_contact_name"
android:padding="@dimen/medium_margin"
android:src="@drawable/ic_new_contact"/>
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/new_contact_name"
android:layout_width="match_parent"
android:layout_height="@dimen/create_new_contact_height"
android:layout_toRightOf="@+id/new_contact_tmb"
android:ellipsize="end"
android:gravity="center_vertical"
android:maxLines="1"
android:text="@string/create_new_contact"
android:textSize="@dimen/big_text_size"/>
</RelativeLayout>
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/existing_contact_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/new_contact_holder"
android:layout_marginStart="@dimen/bigger_margin"
android:layout_marginLeft="@dimen/bigger_margin"
android:layout_marginBottom="@dimen/activity_margin"
android:text="@string/add_to_existing_contact"
android:textAllCaps="true"
android:textSize="@dimen/smaller_text_size"/>
<com.simplemobiletools.commons.views.MyRecyclerView
android:id="@+id/existing_contact_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/existing_contact_label"
android:clipToPadding="false"
android:scrollbars="none"
app:layoutManager="com.simplemobiletools.commons.views.MyLinearLayoutManager"/>
<com.simplemobiletools.commons.views.FastScroller
android:id="@+id/existing_contact_fastscroller"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_below="@+id/existing_contact_label"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:paddingStart="@dimen/normal_margin"
android:paddingLeft="@dimen/normal_margin">
<include layout="@layout/fastscroller_handle_vertical"/>
</com.simplemobiletools.commons.views.FastScroller>
</RelativeLayout>

View File

@ -22,4 +22,16 @@
android:layout_height="match_parent"
android:layout_below="@+id/main_tabs_holder"/>
<ImageView
android:id="@+id/main_dialpad_button"
android:layout_width="@dimen/dialpad_button_size"
android:layout_height="@dimen/dialpad_button_size"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="@dimen/activity_margin"
android:background="@drawable/circle_background"
android:elevation="@dimen/medium_margin"
android:padding="@dimen/activity_margin"
android:src="@drawable/ic_dialpad"/>
</RelativeLayout>

View File

@ -241,6 +241,26 @@
android:orientation="vertical"
android:paddingLeft="@dimen/small_margin"/>
<ImageView
android:id="@+id/contact_ims_image"
android:layout_width="@dimen/contact_icons_size"
android:layout_height="@dimen/contact_icons_size"
android:layout_alignTop="@+id/contact_ims_holder"
android:paddingBottom="@dimen/small_margin"
android:paddingEnd="@dimen/small_margin"
android:paddingRight="@dimen/small_margin"
android:paddingTop="@dimen/medium_margin"
android:src="@drawable/ic_im"/>
<LinearLayout
android:id="@+id/contact_ims_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/contact_addresses_holder"
android:layout_toRightOf="@+id/contact_name_image"
android:orientation="vertical"
android:paddingLeft="@dimen/small_margin"/>
<ImageView
android:id="@+id/contact_events_image"
android:layout_width="@dimen/contact_icons_size"
@ -256,7 +276,7 @@
android:id="@+id/contact_events_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/contact_addresses_holder"
android:layout_below="@+id/contact_ims_holder"
android:layout_toRightOf="@+id/contact_name_image"
android:orientation="vertical"/>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/dialog_holder"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="@dimen/activity_margin">
<com.simplemobiletools.commons.views.MyEditText
android:id="@+id/custom_label_edittext"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/activity_margin"
android:layout_marginRight="@dimen/activity_margin"
android:inputType="textCapSentences"
android:textCursorDrawable="@null"
android:textSize="@dimen/normal_text_size"/>
</RelativeLayout>

View File

@ -86,6 +86,14 @@
android:paddingTop="@dimen/activity_margin"
android:text="@string/addresses"/>
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/manage_visible_fields_ims"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/activity_margin"
android:paddingTop="@dimen/activity_margin"
android:text="@string/instant_messaging"/>
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/manage_visible_fields_events"
android:layout_width="match_parent"

View File

@ -13,13 +13,14 @@
<RelativeLayout
android:id="@+id/contact_holder"
android:layout_width="match_parent"
android:layout_height="@dimen/contact_item_height"
android:layout_height="@dimen/contact_item_with_number_height"
android:paddingTop="@dimen/tiny_margin"
android:paddingRight="@dimen/activity_margin">
<ImageView
android:id="@+id/contact_tmb"
android:layout_width="@dimen/normal_icon_size"
android:layout_height="@dimen/normal_icon_size"
android:layout_height="match_parent"
android:padding="@dimen/medium_margin"
android:src="@drawable/ic_person"/>
@ -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"/>
<TextView
android:id="@+id/contact_number"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/contact_name"
android:layout_below="@+id/contact_name"
android:layout_alignLeft="@+id/contact_name"
android:layout_toRightOf="@+id/contact_tmb"
android:maxLines="1"
android:textSize="@dimen/bigger_text_size"

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/contact_im_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.simplemobiletools.commons.views.MyEditText
android:id="@+id/contact_im"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toLeftOf="@+id/contact_im_type"
android:layout_toStartOf="@+id/contact_im_type"
android:hint="@string/im"
android:lines="1"
android:maxLines="1"
android:singleLine="true"
android:textCursorDrawable="@null"
android:textSize="@dimen/bigger_text_size"/>
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/contact_im_type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/contact_im"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignTop="@+id/contact_im"
android:layout_centerVertical="true"
android:background="?attr/selectableItemBackground"
android:gravity="center"
android:paddingLeft="@dimen/medium_margin"
android:paddingRight="@dimen/medium_margin"
android:text="@string/skype"
android:textSize="@dimen/bigger_text_size"/>
</RelativeLayout>

View File

@ -14,16 +14,16 @@
<android.support.constraint.ConstraintLayout
android:id="@+id/recent_call_holder"
android:layout_width="match_parent"
android:layout_height="@dimen/contact_item_height"
android:gravity="center_vertical"
android:layout_height="@dimen/contact_item_with_number_height"
android:paddingLeft="@dimen/activity_margin"
android:paddingRight="@dimen/activity_margin">
<TextView
android:id="@+id/recent_call_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_height="0dp"
android:ellipsize="end"
android:gravity="center_vertical"
android:maxLines="1"
android:paddingRight="@dimen/activity_margin"
android:textSize="@dimen/big_text_size"
@ -39,6 +39,7 @@
android:layout_height="wrap_content"
android:layout_below="@+id/recent_call_name"
android:maxLines="1"
android:paddingBottom="@dimen/tiny_margin"
android:textSize="@dimen/bigger_text_size"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/recent_call_date_time"

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/contact_im_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:paddingBottom="@dimen/normal_margin"
android:paddingTop="@dimen/normal_margin">
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/contact_im"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toLeftOf="@+id/contact_im_type"
android:layout_toStartOf="@+id/contact_im_type"
android:lines="1"
android:maxLines="1"
android:singleLine="true"
android:textSize="@dimen/bigger_text_size"/>
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/contact_im_type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/contact_im"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignTop="@+id/contact_im"
android:layout_centerVertical="true"
android:gravity="center"
android:paddingLeft="@dimen/medium_margin"
android:paddingRight="@dimen/medium_margin"
android:text="@string/aim"
android:textSize="@dimen/bigger_text_size"/>
</RelativeLayout>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/add_number_to_contact"
android:icon="@drawable/ic_plus"
android:title="@string/add_number_to_contact"
app:showAsAction="ifRoom"/>
</menu>

View File

@ -15,6 +15,8 @@
<string name="send_email_to_group">Grupa e-poçt göndər</string>
<string name="call_person">%s şəxsinə zng et</string>
<string name="request_the_required_permissions">Lazım olan icazələri istə</string>
<string name="create_new_contact">Create new contact</string>
<string name="add_to_existing_contact">Add to an existing contact</string>
<string name="new_contact">Yeni kontakt</string>
<string name="edit_contact">Redaktə et</string>
@ -95,6 +97,10 @@
<string name="include_contact_sources">Kontakt köklərini daxil et</string>
<string name="filename_without_vcf">Fayl adı (.vcf olmadan)</string>
<!-- Dialpad -->
<string name="dialpad">Dialpad</string>
<string name="add_number_to_contact">Add number to contact</string>
<!-- Visible fields -->
<string name="select_fields_to_show">Göstərmək üçün sahəni seç</string>
<string name="prefix">Ön şəkilçi</string>
@ -108,6 +114,7 @@
<string name="websites">Vebsaytlar</string>
<string name="groups">Qruplar </string>
<string name="contact_source">Kontakt kökü</string>
<string name="instant_messaging">Instant messaging (IM)</string>
<!-- FAQ -->
<string name="faq_1_title">I want to change what fields are visible at contacts. Can I do it?</string>

View File

@ -15,6 +15,8 @@
<string name="send_email_to_group">Sende E-Mail an Gruppe</string>
<string name="call_person">%s anrufen</string>
<string name="request_the_required_permissions">Benötigte Berechtigungen anfordern</string>
<string name="create_new_contact">Create new contact</string>
<string name="add_to_existing_contact">Add to an existing contact</string>
<string name="new_contact">Neuer Kontakt</string>
<string name="edit_contact">Kontakt bearbeiten</string>
@ -95,6 +97,10 @@
<string name="include_contact_sources">Kontaktquellen einschließen</string>
<string name="filename_without_vcf">Dateiname (ohne .vcf)</string>
<!-- Dialpad -->
<string name="dialpad">Dialpad</string>
<string name="add_number_to_contact">Add number to contact</string>
<!-- Visible fields -->
<string name="select_fields_to_show">Sichtbare Felder auswählen</string>
<string name="prefix">Titel</string>
@ -108,6 +114,7 @@
<string name="websites">Webseiten</string>
<string name="groups">Gruppen</string>
<string name="contact_source">Kontaktquelle</string>
<string name="instant_messaging">Instant messaging (IM)</string>
<!-- FAQ -->
<string name="faq_1_title">Ich möchte die sichtbaren Kontaktfelder ändern. Kann ich das machen?</string>

View File

@ -15,6 +15,8 @@
<string name="send_email_to_group">Αποστολή email σε ομάδες</string>
<string name="call_person">Κλήση %s</string>
<string name="request_the_required_permissions">Ζητούνται τα απαιτούμενα δικαιώματα</string>
<string name="create_new_contact">Create new contact</string>
<string name="add_to_existing_contact">Add to an existing contact</string>
<string name="new_contact">Νέα επαφή</string>
<string name="edit_contact">Επεξεργασία επαφής</string>
@ -95,6 +97,10 @@
<string name="include_contact_sources">Συμπερίληψη πηγών επαφών</string>
<string name="filename_without_vcf">Όνομα αρχείου (χωρίς .vcf)</string>
<!-- Dialpad -->
<string name="dialpad">Dialpad</string>
<string name="add_number_to_contact">Add number to contact</string>
<!-- Visible fields -->
<string name="select_fields_to_show">Επιλογή εμφάνισης πεδίων</string>
<string name="prefix">Πρόθεμα</string>
@ -108,6 +114,7 @@
<string name="websites">Ιστοσελίδα</string>
<string name="groups">Ομάδες</string>
<string name="contact_source">Προέλευση επαφής</string>
<string name="instant_messaging">Instant messaging (IM)</string>
<!-- FAQ -->
<string name="faq_1_title">Θέλω να αλλάξω τα πεδία που θα είναι ορατά στις επαφές. Μπορώ να το κάνω?</string>

View File

@ -14,7 +14,9 @@
<string name="send_sms_to_group">Envoyer un SMS au groupe</string>
<string name="send_email_to_group">Envoyer un e-mail au groupe</string>
<string name="call_person">Call %s</string>
<string name="request_the_required_permissions">Request the required permissions</string>
<string name="request_the_required_permissions">Demander les autorisations requises</string>
<string name="create_new_contact">Create new contact</string>
<string name="add_to_existing_contact">Add to an existing contact</string>
<string name="new_contact">Nouveau contact</string>
<string name="edit_contact">Modifier contact</string>
@ -23,7 +25,7 @@
<string name="first_name">Prénom</string>
<string name="middle_name">Deuxième prénom</string>
<string name="surname">Nom</string>
<string name="nickname">Nickname</string>
<string name="nickname">Surnom</string>
<!-- Groups -->
<string name="no_groups">Pas de groupe</string>
@ -49,12 +51,12 @@
<string name="call_contact">Appeler le contact</string>
<string name="view_contact">Voir les détails du contact</string>
<string name="manage_shown_contact_fields">Configurer l\'affichage des champs des contacts</string>
<string name="filter_duplicates">Try filtering out duplicate contacts</string>
<string name="manage_shown_tabs">Manage shown tabs</string>
<string name="filter_duplicates">Essayez de filtrer les contacts en double</string>
<string name="manage_shown_tabs">Gérer les onglets affichés</string>
<string name="contacts">Contacts</string>
<string name="favorites">Favorites</string>
<string name="recent_calls">Recent calls</string>
<string name="show_call_confirmation_dialog">Show a call confirmation dialog before initiating a call</string>
<string name="recent_calls">Appels récents</string>
<string name="show_call_confirmation_dialog">Afficher une boîte de dialogue de confirmation d\'appel avant de lancer un appel</string>
<!-- Emails -->
<string name="email">E-mail</string>
@ -80,7 +82,7 @@
<string name="add_favorites">Ajouter des favoris</string>
<string name="add_to_favorites">Ajouter aux favoris</string>
<string name="remove_from_favorites">Retirer des favoris</string>
<string name="must_be_at_edit">You must be at the Edit screen to modify a contact</string>
<string name="must_be_at_edit">Vous devez être sur l\'écran Modifier pour modifier un contact</string>
<!-- Search -->
<string name="search_contacts">Rechercher des contacts</string>
@ -95,6 +97,10 @@
<string name="include_contact_sources">Inclure les sources du contact</string>
<string name="filename_without_vcf">Nom du fichier (sans .vcf)</string>
<!-- Dialpad -->
<string name="dialpad">Dialpad</string>
<string name="add_number_to_contact">Add number to contact</string>
<!-- Visible fields -->
<string name="select_fields_to_show">Sélectionner les champs à afficher</string>
<string name="prefix">Préfixe</string>
@ -108,6 +114,7 @@
<string name="websites">Sites web</string>
<string name="groups">Groupe</string>
<string name="contact_source">Source du contact</string>
<string name="instant_messaging">Messagerie instantanée (MI)</string>
<!-- FAQ -->
<string name="faq_1_title">Je veux changer quelles champs sont visibles. Est-ce que je peux ?</string>

View File

@ -9,12 +9,14 @@
<string name="company">Tvrtka</string>
<string name="job_position">Radno mjesto</string>
<string name="website">Web stranica</string>
<string name="send_sms_to_contacts">Send SMS to contacts</string>
<string name="send_email_to_contacts">Send email to contacts</string>
<string name="send_sms_to_group">Send SMS to group</string>
<string name="send_email_to_group">Send email to group</string>
<string name="call_person">Call %s</string>
<string name="request_the_required_permissions">Request the required permissions</string>
<string name="send_sms_to_contacts">Pošalji SMS kontaktima</string>
<string name="send_email_to_contacts">Pošalji e-poštu kontaktima</string>
<string name="send_sms_to_group">Pošalji SMS grupi</string>
<string name="send_email_to_group">Pošalji e-poštu grupi</string>
<string name="call_person">Nazovi %s</string>
<string name="request_the_required_permissions">Zatraži potrebna dopuštenja</string>
<string name="create_new_contact">Create new contact</string>
<string name="add_to_existing_contact">Add to an existing contact</string>
<string name="new_contact">Novi kontakt</string>
<string name="edit_contact">Uredi kontakt</string>
@ -48,13 +50,13 @@
<string name="on_contact_click">Prilikom dodira kontakta</string>
<string name="call_contact">Nazovi kontakt</string>
<string name="view_contact">Prikaži pojedinosti o kontaktu</string>
<string name="manage_shown_contact_fields">Manage shown contact fields</string>
<string name="filter_duplicates">Try filtering out duplicate contacts</string>
<string name="manage_shown_tabs">Manage shown tabs</string>
<string name="contacts">Contacts</string>
<string name="favorites">Favorites</string>
<string name="recent_calls">Recent calls</string>
<string name="show_call_confirmation_dialog">Show a call confirmation dialog before initiating a call</string>
<string name="manage_shown_contact_fields">Upravljaj prikazanim poljima kontakta</string>
<string name="filter_duplicates">Pokušaj filtrirati duple kontakte</string>
<string name="manage_shown_tabs">Upravljaj prikazanim karticama</string>
<string name="contacts">Kontakti</string>
<string name="favorites">Favoriti</string>
<string name="recent_calls">Nedavni pozivi</string>
<string name="show_call_confirmation_dialog">Pokažite dijaloški okvir za potvrdu poziva prije pokretanja poziva</string>
<!-- Emails -->
<string name="email">E-pošta</string>
@ -80,7 +82,7 @@
<string name="add_favorites">Dodaj favorite</string>
<string name="add_to_favorites">Dodaj u favorite</string>
<string name="remove_from_favorites">Ukloni iz favorita</string>
<string name="must_be_at_edit">You must be at the Edit screen to modify a contact</string>
<string name="must_be_at_edit">Morate biti na zaslonu Uređivanje da biste izmijenili kontakt</string>
<!-- Search -->
<string name="search_contacts">Pretraži kontakte</string>
@ -95,6 +97,10 @@
<string name="include_contact_sources">Uključi izvore kontakta</string>
<string name="filename_without_vcf">Naziv datoteke (bez .vcf)</string>
<!-- Dialpad -->
<string name="dialpad">Dialpad</string>
<string name="add_number_to_contact">Add number to contact</string>
<!-- Visible fields -->
<string name="select_fields_to_show">Odaberi polja za prikaz</string>
<string name="prefix">Prefiks</string>
@ -108,6 +114,7 @@
<string name="websites">Web stranice</string>
<string name="groups">Grupe</string>
<string name="contact_source">Izvori kontakata</string>
<string name="instant_messaging">Brzo slanje poruka (IM)</string>
<!-- FAQ -->
<string name="faq_1_title">Želim promijeniti polja koja su vidljiva na kontaktima. Mogu li to napraviti?</string>

View File

@ -15,6 +15,8 @@
<string name="send_email_to_group">グループにメールを送信</string>
<string name="call_person">Call %s</string>
<string name="request_the_required_permissions">Request the required permissions</string>
<string name="create_new_contact">Create new contact</string>
<string name="add_to_existing_contact">Add to an existing contact</string>
<string name="new_contact">新しい連絡先</string>
<string name="edit_contact">連絡先を編集</string>
@ -95,6 +97,10 @@
<string name="include_contact_sources">Include contact sources</string>
<string name="filename_without_vcf">ファイル名 (.vcfを含めない)</string>
<!-- Dialpad -->
<string name="dialpad">ダイヤルパッド</string>
<string name="add_number_to_contact">Add number to contact</string>
<!-- Visible fields -->
<string name="select_fields_to_show">表示する項目を選択</string>
<string name="prefix">敬称(名前の前)</string>
@ -108,6 +114,7 @@
<string name="websites">ウェブサイト</string>
<string name="groups">グループ</string>
<string name="contact_source">インポート元</string>
<string name="instant_messaging">Instant messaging (IM)</string>
<!-- FAQ -->
<string name="faq_1_title">連絡先に表示される項目(フィールド)を変更することはできますか?</string>

View File

@ -15,6 +15,8 @@
<string name="send_email_to_group">Send email to group</string>
<string name="call_person">Call %s</string>
<string name="request_the_required_permissions">Request the required permissions</string>
<string name="create_new_contact">Create new contact</string>
<string name="add_to_existing_contact">Add to an existing contact</string>
<string name="new_contact">새로운 연락처</string>
<string name="edit_contact">연락처 수정</string>
@ -95,6 +97,10 @@
<string name="include_contact_sources">가져오기 대상</string>
<string name="filename_without_vcf">파일이름 (.vcf 확장자 생략)</string>
<!-- Dialpad -->
<string name="dialpad">Dialpad</string>
<string name="add_number_to_contact">Add number to contact</string>
<!-- Visible fields -->
<string name="select_fields_to_show">Select fields to show</string>
<string name="prefix">Prefix</string>
@ -108,6 +114,7 @@
<string name="websites">Websites</string>
<string name="groups">Groups</string>
<string name="contact_source">Contact source</string>
<string name="instant_messaging">Instant messaging (IM)</string>
<!-- FAQ -->
<string name="faq_1_title">I want to change what fields are visible at contacts. Can I do it?</string>

View File

@ -15,6 +15,8 @@
<string name="send_email_to_group">Send email to group</string>
<string name="call_person">Call %s</string>
<string name="request_the_required_permissions">Request the required permissions</string>
<string name="create_new_contact">Create new contact</string>
<string name="add_to_existing_contact">Add to an existing contact</string>
<string name="new_contact">Naujas kontaktas</string>
<string name="edit_contact">Redaguoti kontaktą</string>
@ -95,6 +97,10 @@
<string name="include_contact_sources">Įtraukti kontaktų šaltinius</string>
<string name="filename_without_vcf">Bylos vardas (be .vcf)</string>
<!-- Dialpad -->
<string name="dialpad">Dialpad</string>
<string name="add_number_to_contact">Add number to contact</string>
<!-- Visible fields -->
<string name="select_fields_to_show">Pasirinkti rodomus laukelius</string>
<string name="prefix">Priešdėlis</string>
@ -108,6 +114,7 @@
<string name="websites">Interneto puslapiai</string>
<string name="groups">Grupės</string>
<string name="contact_source">Kontakto šaltinis</string>
<string name="instant_messaging">Instant messaging (IM)</string>
<!-- FAQ -->
<string name="faq_1_title">Noriu pakeisti, kokie laukai yra matomi kontaktuose. Ar galiu tai padaryti?</string>

View File

@ -14,7 +14,9 @@
<string name="send_sms_to_group">Enviar SMS para o grupo</string>
<string name="send_email_to_group">Enviar e-mail para o grupo</string>
<string name="call_person">Ligar a %s</string>
<string name="request_the_required_permissions">Request the required permissions</string>
<string name="request_the_required_permissions">Pedir a permissão necessária</string>
<string name="create_new_contact">Create new contact</string>
<string name="add_to_existing_contact">Add to an existing contact</string>
<string name="new_contact">Novo contacto</string>
<string name="edit_contact">Editar contacto</string>
@ -50,7 +52,7 @@
<string name="view_contact">Ver detalhes</string>
<string name="manage_shown_contact_fields">Gerir campos a exibir</string>
<string name="filter_duplicates">Tentar filtrar contactos duplicados</string>
<string name="manage_shown_tabs">Manage shown tabs</string>
<string name="manage_shown_tabs">Gerir separadores a exibir</string>
<string name="contacts">Contactos</string>
<string name="favorites">Favoritos</string>
<string name="recent_calls">Chamadas recentes</string>
@ -76,7 +78,7 @@
<string name="anniversary">Aniversário</string>
<!-- Favorites -->
<string name="no_favorites">Parece que ainda não adicionou contactos como favoritos</string>
<string name="no_favorites">Parece que ainda não adicionou contactos aos favoritos</string>
<string name="add_favorites">Adicionar favoritos</string>
<string name="add_to_favorites">Adicionar aos favoritos</string>
<string name="remove_from_favorites">Remover dos favoritos</string>
@ -95,6 +97,10 @@
<string name="include_contact_sources">Incluir fontes dos contactos</string>
<string name="filename_without_vcf">Nome do ficheiro (sem .vcf)</string>
<!-- Dialpad -->
<string name="dialpad">Dialpad</string>
<string name="add_number_to_contact">Add number to contact</string>
<!-- Visible fields -->
<string name="select_fields_to_show">Selecione os campos a mostrar</string>
<string name="prefix">Prefixo</string>
@ -108,6 +114,7 @@
<string name="websites">Site</string>
<string name="groups">Grupos</string>
<string name="contact_source">Origem do contacto</string>
<string name="instant_messaging">Mensagem instantânea (IM)</string>
<!-- FAQ -->
<string name="faq_1_title">I want to change what fields are visible at contacts. Can I do it?</string>

View File

@ -15,6 +15,8 @@
<string name="send_email_to_group">Отправить письмо группе</string>
<string name="call_person">Вызов %s</string>
<string name="request_the_required_permissions">Запрос необходимых разрешений</string>
<string name="create_new_contact">Create new contact</string>
<string name="add_to_existing_contact">Add to an existing contact</string>
<string name="new_contact">Новый контакт</string>
<string name="edit_contact">Редактировать контакт</string>
@ -95,6 +97,10 @@
<string name="include_contact_sources">Включить источники контактов</string>
<string name="filename_without_vcf">Имя файла (без .vcf)</string>
<!-- Dialpad -->
<string name="dialpad">Dialpad</string>
<string name="add_number_to_contact">Add number to contact</string>
<!-- Visible fields -->
<string name="select_fields_to_show">Выберите отображаемые поля</string>
<string name="prefix">Префикс</string>
@ -108,6 +114,7 @@
<string name="websites">Сайты</string>
<string name="groups">Группы</string>
<string name="contact_source">Источник контакта</string>
<string name="instant_messaging">Мессенджеры (IM)</string>
<!-- FAQ -->
<string name="faq_1_title">Я хочу изменить поля, отображаемые у контактов. Могу ли я это сделать?</string>

View File

@ -15,6 +15,8 @@
<string name="send_email_to_group">Poslať skupine email</string>
<string name="call_person">Zavolať %s</string>
<string name="request_the_required_permissions">Vyžiadať potrebné oprávnenia</string>
<string name="create_new_contact">Vytvoriť nový kontakt</string>
<string name="add_to_existing_contact">Pridať k existujúcemu kontaktu</string>
<string name="new_contact">Nový kontakt</string>
<string name="edit_contact">Upraviť kontakt</string>
@ -95,6 +97,10 @@
<string name="include_contact_sources">Zahrnúť zdroje kontaktov</string>
<string name="filename_without_vcf">Názov súboru (bez .vcf)</string>
<!-- Dialpad -->
<string name="dialpad">Číselník</string>
<string name="add_number_to_contact">Pridať číslo kontaktu</string>
<!-- Visible fields -->
<string name="select_fields_to_show">Zvoľte polia na zobrazenie</string>
<string name="prefix">Titul pred menom</string>
@ -108,6 +114,7 @@
<string name="websites">Webstránky</string>
<string name="groups">Skupiny</string>
<string name="contact_source">Zdroje kontaktov</string>
<string name="instant_messaging">Rýchle správy (IM)</string>
<!-- FAQ -->
<string name="faq_1_title">Chcem upraviť viditeľné polia kontaktov. Dá sa to?</string>

View File

@ -15,6 +15,8 @@
<string name="send_email_to_group">Skicka e-post till grupp</string>
<string name="call_person">Ring %s</string>
<string name="request_the_required_permissions">Begär de behörigheter som krävs</string>
<string name="create_new_contact">Create new contact</string>
<string name="add_to_existing_contact">Add to an existing contact</string>
<string name="new_contact">Ny kontakt</string>
<string name="edit_contact">Redigera kontakt</string>
@ -95,6 +97,10 @@
<string name="include_contact_sources">Inkludera kontaktkällor</string>
<string name="filename_without_vcf">Filnamn (utan .vcf)</string>
<!-- Dialpad -->
<string name="dialpad">Dialpad</string>
<string name="add_number_to_contact">Add number to contact</string>
<!-- Visible fields -->
<string name="select_fields_to_show">Välj vilka fält som ska visas</string>
<string name="prefix">Prefix</string>
@ -108,6 +114,7 @@
<string name="websites">Webbplatser</string>
<string name="groups">Grupper</string>
<string name="contact_source">Kontaktkälla</string>
<string name="instant_messaging">Instant messaging (IM)</string>
<!-- FAQ -->
<string name="faq_1_title">I want to change what fields are visible at contacts. Can I do it?</string>

View File

@ -15,6 +15,8 @@
<string name="send_email_to_group">Gruba e-posta gönder</string>
<string name="call_person">Call %s</string>
<string name="request_the_required_permissions">Request the required permissions</string>
<string name="create_new_contact">Create new contact</string>
<string name="add_to_existing_contact">Add to an existing contact</string>
<string name="new_contact">Yeni kişi</string>
<string name="edit_contact">Kişiyi düzenle</string>
@ -95,6 +97,10 @@
<string name="include_contact_sources">Kişi kaynaklarını dahil et</string>
<string name="filename_without_vcf">Dosya adı (.vcf olmadan)</string>
<!-- Dialpad -->
<string name="dialpad">Dialpad</string>
<string name="add_number_to_contact">Add number to contact</string>
<!-- Visible fields -->
<string name="select_fields_to_show">Görüntülenecek alanları seç</string>
<string name="prefix">Önek</string>
@ -108,6 +114,7 @@
<string name="websites">Web siteleri</string>
<string name="groups">Gruplar</string>
<string name="contact_source">Kişi kaynağı</string>
<string name="instant_messaging">Instant messaging (IM)</string>
<!-- FAQ -->
<string name="faq_1_title">Rehberde görüntülenecek alanları değiştirmek istiyorum. Bunu yapabilir miyim?</string>

View File

@ -0,0 +1,7 @@
<resources>
<style name="DialpadNumberStyle" parent="DialpadNumberStyle.Base">
<item name="android:background">?attr/selectableItemBackgroundBorderless</item>
</style>
</resources>

View File

@ -15,6 +15,8 @@
<string name="send_email_to_group">發送電子郵件給群組</string>
<string name="call_person">打電話給 %s</string>
<string name="request_the_required_permissions">請求必要的權限</string>
<string name="create_new_contact">Create new contact</string>
<string name="add_to_existing_contact">Add to an existing contact</string>
<string name="new_contact">新聯絡人</string>
<string name="edit_contact">編輯聯絡人</string>
@ -95,6 +97,10 @@
<string name="include_contact_sources">包含聯絡人來源</string>
<string name="filename_without_vcf">檔案名稱 (不含.vcf)</string>
<!-- Dialpad -->
<string name="dialpad">Dialpad</string>
<string name="add_number_to_contact">Add number to contact</string>
<!-- Visible fields -->
<string name="select_fields_to_show">選擇要顯示的欄位</string>
<string name="prefix">前缀</string>
@ -108,6 +114,7 @@
<string name="websites">網站</string>
<string name="groups">群組</string>
<string name="contact_source">聯絡人來源</string>
<string name="instant_messaging">Instant messaging (IM)</string>
<!-- FAQ -->
<string name="faq_1_title">我想要更改在通訊錄會看到哪些欄位。我能這麼做嗎?</string>

View File

@ -4,4 +4,8 @@
<dimen name="contact_actions_size">45dp</dimen>
<dimen name="contact_icons_size">40dp</dimen>
<dimen name="contact_item_height">52dp</dimen>
<dimen name="contact_item_with_number_height">56dp</dimen>
<dimen name="create_new_contact_height">68dp</dimen>
<dimen name="dialpad_button_size">60dp</dimen>
<dimen name="dialpad_text_size">34sp</dimen>
</resources>

View File

@ -1,7 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="im">IM</string>
<string name="aim">AIM</string>
<string name="windows_live">Windows Live</string>
<string name="yahoo">Yahoo</string>
<string name="skype">Skype</string>
<string name="qq">QQ</string>
<string name="hangouts">Hangouts</string>
<string name="icq">ICQ</string>
<string name="jabber">Jabber</string>
<!-- Release notes -->
<string name="release_32">Added a simple dialpad, dialer will come soon</string>
<string name="release_31">
Added an optional IM field\n
Added support for custom phone number/email/address types
</string>
<string name="release_29">Added an optional Nickname field</string>
<string name="release_27">
Allow customizing which tabs are visible\n

View File

@ -15,6 +15,8 @@
<string name="send_email_to_group">Send email to group</string>
<string name="call_person">Call %s</string>
<string name="request_the_required_permissions">Request the required permissions</string>
<string name="create_new_contact">Create new contact</string>
<string name="add_to_existing_contact">Add to an existing contact</string>
<string name="new_contact">New contact</string>
<string name="edit_contact">Edit contact</string>
@ -95,6 +97,10 @@
<string name="include_contact_sources">Include contact sources</string>
<string name="filename_without_vcf">Filename (without .vcf)</string>
<!-- Dialpad -->
<string name="dialpad">Dialpad</string>
<string name="add_number_to_contact">Add number to contact</string>
<!-- Visible fields -->
<string name="select_fields_to_show">Select fields to show</string>
<string name="prefix">Prefix</string>
@ -108,6 +114,7 @@
<string name="websites">Websites</string>
<string name="groups">Groups</string>
<string name="contact_source">Contact source</string>
<string name="instant_messaging">Instant messaging (IM)</string>
<!-- FAQ -->
<string name="faq_1_title">I want to change what fields are visible at contacts. Can I do it?</string>

View File

@ -2,4 +2,15 @@
<style name="AppTheme" parent="AppTheme.Base"/>
<style name="DialpadNumberStyle.Base">
<item name="android:gravity">center</item>
<item name="android:paddingBottom">@dimen/medium_margin</item>
<item name="android:paddingTop">@dimen/normal_margin</item>
<item name="android:textSize">@dimen/dialpad_text_size</item>
</style>
<style name="DialpadNumberStyle" parent="DialpadNumberStyle.Base">
<item name="android:background">?attr/selectableItemBackground</item>
</style>
</resources>

View File

@ -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

View File

@ -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