Merge pull request #5 from SimpleMobileTools/master

up
This commit is contained in:
solokot 2018-02-11 17:06:19 +03:00 committed by GitHub
commit 71744e3126
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
49 changed files with 1448 additions and 426 deletions

View File

@ -1,6 +1,12 @@
Changelog
==========
Version 3.1.4 *(2018-02-03)*
----------------------------
* Add a unified "Phone storage" label to local phone storage
* Properly handle special characters at contact ex/importing
Version 3.1.3 *(2018-01-29)*
----------------------------

View File

@ -10,8 +10,8 @@ android {
applicationId "com.simplemobiletools.contacts"
minSdkVersion 16
targetSdkVersion 27
versionCode 8
versionName "3.1.3"
versionCode 9
versionName "3.1.4"
setProperty("archivesBaseName", "contacts")
}
@ -20,6 +20,9 @@ android {
}
buildTypes {
debug {
applicationIdSuffix ".debug"
}
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
@ -42,7 +45,7 @@ ext {
}
dependencies {
implementation 'com.simplemobiletools:commons:3.9.5'
implementation 'com.simplemobiletools:commons:3.11.3'
implementation 'joda-time:joda-time:2.9.9'
debugImplementation "com.squareup.leakcanary:leakcanary-android:$leakCanaryVersion"

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_launcher_name">Contacts_debug</string>
</resources>

View File

@ -74,6 +74,40 @@
android:label="@string/settings"
android:parentActivityName=".activities.MainActivity"/>
<activity
android:name=".activities.ViewContactActivity"
android:label="@string/details"
android:parentActivityName=".activities.MainActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="vnd.android.cursor.dir/person"/>
<data android:mimeType="vnd.android.cursor.dir/contact"/>
<data android:mimeType="vnd.android.cursor.item/group"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<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="com.android.contacts.action.QUICK_CONTACT"/>
<action android:name="android.provider.action.QUICK_CONTACT"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="vnd.android.cursor.item/contact"/>
<data android:mimeType="vnd.android.cursor.item/person"/>
</intent-filter>
</activity>
<activity
android:name=".activities.EditContactActivity"
android:parentActivityName=".activities.MainActivity">
@ -111,24 +145,6 @@
<data android:mimeType="vnd.android.cursor.dir/raw_contact"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="vnd.android.cursor.dir/person"/>
<data android:mimeType="vnd.android.cursor.dir/contact"/>
<data android:mimeType="vnd.android.cursor.item/group"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<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"/>
@ -139,16 +155,6 @@
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="vnd.android.cursor.dir/group"/>
</intent-filter>
<intent-filter android:label="@string/edit_contact">
<action android:name="com.android.contacts.action.QUICK_CONTACT"/>
<action android:name="android.provider.action.QUICK_CONTACT"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="vnd.android.cursor.item/contact"/>
<data android:mimeType="vnd.android.cursor.item/person"/>
</intent-filter>
</activity>
<activity

View File

@ -0,0 +1,160 @@
package com.simplemobiletools.contacts.activities
import android.graphics.drawable.Drawable
import android.provider.ContactsContract
import android.widget.ImageView
import android.widget.TextView
import com.bumptech.glide.Glide
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.RequestOptions
import com.bumptech.glide.request.target.Target
import com.simplemobiletools.commons.dialogs.ConfirmationDialog
import com.simplemobiletools.commons.dialogs.RadioGroupDialog
import com.simplemobiletools.commons.extensions.getColoredBitmap
import com.simplemobiletools.commons.extensions.getContrastColor
import com.simplemobiletools.commons.helpers.getDateFormats
import com.simplemobiletools.commons.models.RadioItem
import com.simplemobiletools.contacts.R
import com.simplemobiletools.contacts.extensions.config
import com.simplemobiletools.contacts.extensions.sendEmailIntent
import com.simplemobiletools.contacts.extensions.sendSMSIntent
import com.simplemobiletools.contacts.extensions.shareContacts
import com.simplemobiletools.contacts.helpers.ContactsHelper
import com.simplemobiletools.contacts.models.Contact
import org.joda.time.DateTime
import org.joda.time.format.DateTimeFormat
import java.text.DateFormat
import java.text.SimpleDateFormat
import java.util.*
abstract class ContactActivity : SimpleActivity() {
var contact: Contact? = null
var currentContactPhotoPath = ""
fun showPhotoPlaceholder(photoView: ImageView) {
val placeholder = resources.getColoredBitmap(R.drawable.ic_person, config.primaryColor.getContrastColor())
val padding = resources.getDimension(R.dimen.activity_margin).toInt()
photoView.setPadding(padding, padding, padding, padding)
photoView.setImageBitmap(placeholder)
currentContactPhotoPath = ""
}
fun updateContactPhoto(path: String, photoView: ImageView) {
currentContactPhotoPath = path
val options = RequestOptions()
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.centerCrop()
Glide.with(this)
.load(path)
.transition(DrawableTransitionOptions.withCrossFade())
.apply(options)
.listener(object : RequestListener<Drawable> {
override fun onResourceReady(resource: Drawable?, model: Any?, target: Target<Drawable>?, dataSource: DataSource?, isFirstResource: Boolean): Boolean {
photoView.setPadding(0, 0, 0, 0)
return false
}
override fun onLoadFailed(e: GlideException?, model: Any?, target: Target<Drawable>?, isFirstResource: Boolean): Boolean {
showPhotoPlaceholder(photoView)
return true
}
}).into(photoView)
}
fun getDateTime(dateString: String, viewToUpdate: TextView? = null): DateTime {
val dateFormats = getDateFormats()
var date = DateTime()
for (format in dateFormats) {
try {
date = DateTime.parse(dateString, DateTimeFormat.forPattern(format))
val formatter = DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.getDefault())
var localPattern = (formatter as SimpleDateFormat).toLocalizedPattern()
val hasYear = format.contains("y")
if (!hasYear) {
localPattern = localPattern.replace("y", "").trim()
date = date.withYear(DateTime().year)
}
val formattedString = date.toString(localPattern)
viewToUpdate?.text = formattedString
break
} catch (ignored: Exception) {
}
}
return date
}
fun deleteContact() {
ConfirmationDialog(this) {
ContactsHelper(this).deleteContact(contact!!)
finish()
}
}
fun shareContact() {
shareContacts(arrayListOf(contact!!))
}
fun trySendSMS() {
val numbers = contact!!.phoneNumbers
if (numbers.size == 1) {
sendSMSIntent(numbers.first().value)
} else if (numbers.size > 1) {
val items = ArrayList<RadioItem>()
numbers.forEachIndexed { index, phoneNumber ->
items.add(RadioItem(index, phoneNumber.value, phoneNumber.value))
}
RadioGroupDialog(this, items) {
sendSMSIntent(it as String)
}
}
}
fun trySendEmail() {
val emails = contact!!.emails
if (emails.size == 1) {
sendEmailIntent(emails.first().value)
} else if (emails.size > 1) {
val items = ArrayList<RadioItem>()
emails.forEachIndexed { index, email ->
items.add(RadioItem(index, email.value, email.value))
}
RadioGroupDialog(this, items) {
sendEmailIntent(it as String)
}
}
}
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 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 getEventTextId(type: Int) = when (type) {
ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY -> R.string.birthday
ContactsContract.CommonDataKinds.Event.TYPE_ANNIVERSARY -> R.string.anniversary
else -> R.string.other
}
}

View File

@ -4,10 +4,8 @@ import android.app.DatePickerDialog
import android.content.ClipData
import android.content.Intent
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import android.net.Uri
import android.os.Bundle
import android.provider.ContactsContract
import android.provider.ContactsContract.CommonDataKinds
import android.provider.MediaStore
import android.view.Menu
@ -16,20 +14,10 @@ import android.view.ViewGroup
import android.view.WindowManager
import android.widget.ImageView
import android.widget.TextView
import com.bumptech.glide.Glide
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.RequestOptions
import com.bumptech.glide.request.target.Target
import com.simplemobiletools.commons.dialogs.ConfirmationDialog
import com.simplemobiletools.commons.dialogs.RadioGroupDialog
import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.PERMISSION_READ_CONTACTS
import com.simplemobiletools.commons.helpers.PERMISSION_WRITE_CONTACTS
import com.simplemobiletools.commons.helpers.getDateFormats
import com.simplemobiletools.commons.models.RadioItem
import com.simplemobiletools.contacts.R
import com.simplemobiletools.contacts.extensions.*
@ -39,16 +27,14 @@ import com.simplemobiletools.contacts.models.Email
import com.simplemobiletools.contacts.models.Event
import com.simplemobiletools.contacts.models.PhoneNumber
import kotlinx.android.synthetic.main.activity_edit_contact.*
import kotlinx.android.synthetic.main.item_email.view.*
import kotlinx.android.synthetic.main.item_edit_email.view.*
import kotlinx.android.synthetic.main.item_edit_phone_number.view.*
import kotlinx.android.synthetic.main.item_event.view.*
import kotlinx.android.synthetic.main.item_phone_number.view.*
import org.joda.time.DateTime
import org.joda.time.format.DateTimeFormat
import java.text.DateFormat
import java.text.SimpleDateFormat
import java.util.*
class EditContactActivity : SimpleActivity() {
class EditContactActivity : ContactActivity() {
private val INTENT_TAKE_PHOTO = 1
private val INTENT_CHOOSE_PHOTO = 2
private val INTENT_CROP_PHOTO = 3
@ -60,9 +46,7 @@ class EditContactActivity : SimpleActivity() {
private val KEY_PHONE = "phone"
private var wasActivityInitialized = false
private var currentContactPhotoPath = ""
private var lastPhotoIntentUri: Uri? = null
private var contact: Contact? = null
private var isSaving = false
override fun onCreate(savedInstanceState: Bundle?) {
@ -88,7 +72,7 @@ class EditContactActivity : SimpleActivity() {
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu_contact, menu)
menuInflater.inflate(R.menu.menu_edit_contact, menu)
if (wasActivityInitialized) {
menu.findItem(R.id.delete).isVisible = contact?.id != 0
menu.findItem(R.id.share).isVisible = contact?.id != 0
@ -111,7 +95,7 @@ class EditContactActivity : SimpleActivity() {
if (resultCode == RESULT_OK) {
when (requestCode) {
INTENT_TAKE_PHOTO, INTENT_CHOOSE_PHOTO -> startCropPhotoIntent(lastPhotoIntentUri!!)
INTENT_CROP_PHOTO -> updateContactPhoto(lastPhotoIntentUri.toString())
INTENT_CROP_PHOTO -> updateContactPhoto(lastPhotoIntentUri.toString(), contact_photo)
}
}
}
@ -119,9 +103,7 @@ class EditContactActivity : SimpleActivity() {
private fun initContact() {
var contactId = intent.getIntExtra(CONTACT_ID, 0)
val action = intent.action
if (contactId == 0 && (action == ContactsContract.QuickContact.ACTION_QUICK_CONTACT ||
action == Intent.ACTION_VIEW ||
action == Intent.ACTION_EDIT)) {
if (contactId == 0 && action == Intent.ACTION_EDIT) {
val data = intent.data
if (data != null) {
val rawId = if (data.path.contains("lookup")) {
@ -165,9 +147,9 @@ class EditContactActivity : SimpleActivity() {
contact_photo.background = ColorDrawable(config.primaryColor)
if (contact!!.photoUri.isEmpty()) {
showPhotoPlaceholder()
showPhotoPlaceholder(contact_photo)
} else {
updateContactPhoto(contact!!.photoUri)
updateContactPhoto(contact!!.photoUri, contact_photo)
}
val textColor = config.textColor
@ -197,6 +179,12 @@ class EditContactActivity : SimpleActivity() {
contact_email_add_new.setOnClickListener { addNewEmailField() }
contact_event_add_new.setOnClickListener { addNewEventField() }
contact_toggle_favorite.apply {
setImageDrawable(getStarDrawable(contact!!.starred == 1))
tag = contact!!.starred
applyColorFilter(textColor)
}
updateTextColors(contact_scrollview)
wasActivityInitialized = true
invalidateOptionsMenu()
@ -226,14 +214,7 @@ class EditContactActivity : SimpleActivity() {
contact_first_name.setText(contact!!.firstName)
contact_middle_name.setText(contact!!.middleName)
contact_surname.setText(contact!!.surname)
contact_source.text = contact!!.source
contact_toggle_favorite.apply {
beVisible()
setImageDrawable(getStarDrawable(contact!!.starred == 1))
tag = contact!!.starred
applyColorFilter(config.textColor)
}
contact_source.text = getPublicContactSource(contact!!.source)
setupPhoneNumbers()
setupEmails()
@ -244,11 +225,11 @@ class EditContactActivity : SimpleActivity() {
contact!!.phoneNumbers.forEachIndexed { index, number ->
var numberHolder = contact_numbers_holder.getChildAt(index)
if (numberHolder == null) {
numberHolder = layoutInflater.inflate(R.layout.item_phone_number, contact_numbers_holder, false)
numberHolder = layoutInflater.inflate(R.layout.item_edit_phone_number, contact_numbers_holder, false)
contact_numbers_holder.addView(numberHolder)
}
(numberHolder as? ViewGroup)?.apply {
numberHolder!!.apply {
contact_number.setText(number.value)
setupPhoneNumberTypePicker(contact_number_type, number.type)
}
@ -259,11 +240,11 @@ class EditContactActivity : SimpleActivity() {
contact!!.emails.forEachIndexed { index, email ->
var emailHolder = contact_emails_holder.getChildAt(index)
if (emailHolder == null) {
emailHolder = layoutInflater.inflate(R.layout.item_email, contact_emails_holder, false)
emailHolder = layoutInflater.inflate(R.layout.item_edit_email, contact_emails_holder, false)
contact_emails_holder.addView(emailHolder)
}
(emailHolder as? ViewGroup)?.apply {
emailHolder!!.apply {
contact_email.setText(email.value)
setupEmailTypePicker(contact_email_type, email.type)
}
@ -278,7 +259,7 @@ class EditContactActivity : SimpleActivity() {
contact_events_holder.addView(eventHolder)
}
(eventHolder as? ViewGroup)?.apply {
(eventHolder as ViewGroup).apply {
val contactEvent = contact_event.apply {
getDateTime(event.value, this)
tag = event.value
@ -302,50 +283,16 @@ class EditContactActivity : SimpleActivity() {
private fun setupNewContact() {
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE)
supportActionBar?.title = resources.getString(R.string.new_contact)
contact = Contact(0, "", "", "", "", ArrayList(), ArrayList(), ArrayList(), "", 0, 0, "")
contact_source.text = config.lastUsedContactSource
contact = Contact(0, "", "", "", "", ArrayList(), ArrayList(), ArrayList(), config.lastUsedContactSource, 0, 0, "")
contact_source.text = getPublicContactSource(contact!!.source)
contact_source.setOnClickListener {
showContactSourcePicker(contact_source.value) {
contact_source.text = it
showContactSourcePicker(contact!!.source) {
contact!!.source = if (it == getString(R.string.phone_storage_hidden)) SMT_PRIVATE else it
contact_source.text = getPublicContactSource(it)
}
}
}
private fun shareContact() {
shareContacts(arrayListOf(contact!!))
}
private fun showPhotoPlaceholder() {
val placeholder = resources.getColoredBitmap(R.drawable.ic_person, config.primaryColor.getContrastColor())
val padding = resources.getDimension(R.dimen.activity_margin).toInt()
contact_photo.setPadding(padding, padding, padding, padding)
contact_photo.setImageBitmap(placeholder)
currentContactPhotoPath = ""
}
private fun updateContactPhoto(path: String) {
currentContactPhotoPath = path
val options = RequestOptions()
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.centerCrop()
Glide.with(this)
.load(path)
.transition(DrawableTransitionOptions.withCrossFade())
.apply(options)
.listener(object : RequestListener<Drawable> {
override fun onResourceReady(resource: Drawable?, model: Any?, target: Target<Drawable>?, dataSource: DataSource?, isFirstResource: Boolean): Boolean {
contact_photo.setPadding(0, 0, 0, 0)
return false
}
override fun onLoadFailed(e: GlideException?, model: Any?, target: Target<Drawable>?, isFirstResource: Boolean): Boolean {
showPhotoPlaceholder()
return true
}
}).into(contact_photo)
}
private fun setupTypePickers() {
if (contact!!.phoneNumbers.isEmpty()) {
val numberHolder = contact_numbers_holder.getChildAt(0)
@ -430,31 +377,6 @@ class EditContactActivity : SimpleActivity() {
removeContactEventButton.beGone()
}
private fun getDateTime(dateString: String, viewToUpdate: TextView? = null): DateTime {
val dateFormats = getDateFormats()
var date = DateTime()
for (format in dateFormats) {
try {
date = DateTime.parse(dateString, DateTimeFormat.forPattern(format))
val formatter = DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.getDefault())
var localPattern = (formatter as SimpleDateFormat).toLocalizedPattern()
val hasYear = format.contains("y")
if (!hasYear) {
localPattern = localPattern.replace("y", "").trim()
date = date.withYear(DateTime().year)
}
val formattedString = date.toString(localPattern)
viewToUpdate?.text = formattedString
break
} catch (ignored: Exception) {
}
}
return date
}
private fun showNumberTypePicker(numberTypeField: TextView) {
val items = arrayListOf(
RadioItem(CommonDataKinds.Phone.TYPE_MOBILE, getString(R.string.mobile)),
@ -515,7 +437,7 @@ class EditContactActivity : SimpleActivity() {
phoneNumbers = getFilledPhoneNumbers()
emails = getFilledEmails()
events = getFilledEvents()
source = contact_source.value
source = contact!!.source
starred = if (isContactStarred()) 1 else 0
Thread {
@ -608,7 +530,7 @@ class EditContactActivity : SimpleActivity() {
}
private fun addNewPhoneNumberField() {
val numberHolder = layoutInflater.inflate(R.layout.item_phone_number, contact_numbers_holder, false) as ViewGroup
val numberHolder = layoutInflater.inflate(R.layout.item_edit_phone_number, contact_numbers_holder, false) as ViewGroup
updateTextColors(numberHolder)
setupPhoneNumberTypePicker(numberHolder.contact_number_type)
contact_numbers_holder.addView(numberHolder)
@ -619,7 +541,7 @@ class EditContactActivity : SimpleActivity() {
}
private fun addNewEmailField() {
val emailHolder = layoutInflater.inflate(R.layout.item_email, contact_emails_holder, false) as ViewGroup
val emailHolder = layoutInflater.inflate(R.layout.item_edit_email, contact_emails_holder, false) as ViewGroup
updateTextColors(emailHolder)
setupEmailTypePicker(emailHolder.contact_email_type)
contact_emails_holder.addView(emailHolder)
@ -636,13 +558,6 @@ class EditContactActivity : SimpleActivity() {
contact_events_holder.addView(eventHolder)
}
private fun deleteContact() {
ConfirmationDialog(this) {
ContactsHelper(this).deleteContact(contact!!)
finish()
}
}
private fun toggleFavorite() {
val isStarred = isContactStarred()
contact_toggle_favorite.apply {
@ -670,39 +585,7 @@ class EditContactActivity : SimpleActivity() {
when (it as Int) {
TAKE_PHOTO -> startTakePhotoIntent()
CHOOSE_PHOTO -> startChoosePhotoIntent()
else -> showPhotoPlaceholder()
}
}
}
private fun trySendSMS() {
val numbers = contact!!.phoneNumbers
if (numbers.size == 1) {
sendSMSIntent(numbers.first().value)
} else if (numbers.size > 1) {
val items = ArrayList<RadioItem>()
numbers.forEachIndexed { index, phoneNumber ->
items.add(RadioItem(index, phoneNumber.value, phoneNumber.value))
}
RadioGroupDialog(this, items) {
sendSMSIntent(it as String)
}
}
}
private fun trySendEmail() {
val emails = contact!!.emails
if (emails.size == 1) {
sendEmailIntent(emails.first().value)
} else if (emails.size > 1) {
val items = ArrayList<RadioItem>()
emails.forEachIndexed { index, email ->
items.add(RadioItem(index, email.value, email.value))
}
RadioGroupDialog(this, items) {
sendEmailIntent(it as String)
else -> showPhotoPlaceholder(contact_photo)
}
}
}
@ -736,17 +619,6 @@ class EditContactActivity : SimpleActivity() {
}
}
private fun getPhoneNumberTextId(type: Int) = when (type) {
CommonDataKinds.Phone.TYPE_MOBILE -> R.string.mobile
CommonDataKinds.Phone.TYPE_HOME -> R.string.home
CommonDataKinds.Phone.TYPE_WORK -> R.string.work
CommonDataKinds.Phone.TYPE_MAIN -> R.string.main_number
CommonDataKinds.Phone.TYPE_FAX_WORK -> R.string.work_fax
CommonDataKinds.Phone.TYPE_FAX_HOME -> R.string.home_fax
CommonDataKinds.Phone.TYPE_PAGER -> R.string.pager
else -> R.string.other
}
private fun getPhoneNumberTypeId(value: String) = when (value) {
getString(R.string.mobile) -> CommonDataKinds.Phone.TYPE_MOBILE
getString(R.string.home) -> CommonDataKinds.Phone.TYPE_HOME
@ -758,13 +630,6 @@ class EditContactActivity : SimpleActivity() {
else -> CommonDataKinds.Phone.TYPE_OTHER
}
private fun getEmailTextId(type: Int) = when (type) {
CommonDataKinds.Email.TYPE_HOME -> R.string.home
CommonDataKinds.Email.TYPE_WORK -> R.string.work
CommonDataKinds.Email.TYPE_MOBILE -> R.string.mobile
else -> R.string.other
}
private fun getEmailTypeId(value: String) = when (value) {
getString(R.string.home) -> CommonDataKinds.Email.TYPE_HOME
getString(R.string.work) -> CommonDataKinds.Email.TYPE_WORK
@ -772,12 +637,6 @@ class EditContactActivity : SimpleActivity() {
else -> CommonDataKinds.Email.TYPE_OTHER
}
private fun getEventTextId(type: Int) = when (type) {
CommonDataKinds.Event.TYPE_BIRTHDAY -> R.string.birthday
CommonDataKinds.Event.TYPE_ANNIVERSARY -> R.string.anniversary
else -> R.string.other
}
private fun getEventTypeId(value: String) = when (value) {
getString(R.string.birthday) -> CommonDataKinds.Event.TYPE_BIRTHDAY
getString(R.string.anniversary) -> CommonDataKinds.Event.TYPE_ANNIVERSARY

View File

@ -56,6 +56,7 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
if (it) {
handlePermission(PERMISSION_WRITE_CONTACTS) {
if (it) {
storeLocalAccountData()
initFragments()
} else {
toast(R.string.no_contacts_permission)
@ -214,6 +215,34 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
}
}
private fun storeLocalAccountData() {
if (config.localAccountType == "-1") {
// some manufacturer contact account types from https://stackoverflow.com/a/44802016/1967672
val localAccountTypes = arrayListOf("vnd.sec.contact.phone",
"com.htc.android.pcsc",
"com.sonyericsson.localcontacts",
"com.lge.sync",
"com.lge.phone",
"vnd.tmobileus.contact.phone",
"com.android.huawei.phone",
"Local Phone Account")
ContactsHelper(this).getContactSources {
var localAccountType = ""
var localAccountName = ""
it.forEach {
if (localAccountTypes.contains(it.type)) {
localAccountType = it.type
localAccountName = it.name
}
}
config.localAccountType = localAccountType
config.localAccountName = localAccountName
}
}
}
private fun getOtherViewPagerItem(used: Int) = if (used == 1) 0 else 1
private fun initFragments() {

View File

@ -1,11 +1,16 @@
package com.simplemobiletools.contacts.activities
import android.os.Bundle
import com.simplemobiletools.commons.dialogs.RadioGroupDialog
import com.simplemobiletools.commons.extensions.beVisibleIf
import com.simplemobiletools.commons.extensions.updateTextColors
import com.simplemobiletools.commons.extensions.useEnglishToggled
import com.simplemobiletools.commons.models.RadioItem
import com.simplemobiletools.contacts.R
import com.simplemobiletools.contacts.extensions.config
import com.simplemobiletools.contacts.helpers.ON_CLICK_CALL_CONTACT
import com.simplemobiletools.contacts.helpers.ON_CLICK_EDIT_CONTACT
import com.simplemobiletools.contacts.helpers.ON_CLICK_VIEW_CONTACT
import kotlinx.android.synthetic.main.activity_settings.*
import java.util.*
@ -20,11 +25,12 @@ class SettingsActivity : SimpleActivity() {
setupCustomizeColors()
setupUseEnglish()
setupAvoidWhatsNew()
setupShowInfoBubble()
setupShowContactThumbnails()
setupShowPhoneNumbers()
setupCallContactOnClick()
setupStartNameWithSurname()
setupOnContactClick()
updateTextColors(settings_holder)
}
@ -44,6 +50,14 @@ class SettingsActivity : SimpleActivity() {
}
}
private fun setupAvoidWhatsNew() {
settings_avoid_whats_new.isChecked = config.avoidWhatsNew
settings_avoid_whats_new_holder.setOnClickListener {
settings_avoid_whats_new.toggle()
config.avoidWhatsNew = settings_avoid_whats_new.isChecked
}
}
private fun setupShowInfoBubble() {
settings_show_info_bubble.isChecked = config.showInfoBubble
settings_show_info_bubble_holder.setOnClickListener {
@ -68,14 +82,6 @@ class SettingsActivity : SimpleActivity() {
}
}
private fun setupCallContactOnClick() {
settings_call_contact_on_click.isChecked = config.callContact
settings_call_contact_on_click_holder.setOnClickListener {
settings_call_contact_on_click.toggle()
config.callContact = settings_call_contact_on_click.isChecked
}
}
private fun setupStartNameWithSurname() {
settings_start_with_surname.isChecked = config.startNameWithSurname
settings_start_with_surname_holder.setOnClickListener {
@ -83,4 +89,25 @@ class SettingsActivity : SimpleActivity() {
config.startNameWithSurname = settings_start_with_surname.isChecked
}
}
private fun setupOnContactClick() {
settings_on_contact_click.text = getOnContactClickText()
settings_on_contact_click_holder.setOnClickListener {
val items = arrayListOf(
RadioItem(ON_CLICK_CALL_CONTACT, getString(R.string.call_contact)),
RadioItem(ON_CLICK_VIEW_CONTACT, getString(R.string.view_contact)),
RadioItem(ON_CLICK_EDIT_CONTACT, getString(R.string.edit_contact)))
RadioGroupDialog(this@SettingsActivity, items, config.onContactClick) {
config.onContactClick = it as Int
settings_on_contact_click.text = getOnContactClickText()
}
}
}
private fun getOnContactClickText() = getString(when (config.onContactClick) {
ON_CLICK_CALL_CONTACT -> R.string.call_contact
ON_CLICK_VIEW_CONTACT -> R.string.view_contact
else -> R.string.edit_contact
})
}

View File

@ -0,0 +1,255 @@
package com.simplemobiletools.contacts.activities
import android.content.Intent
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.provider.ContactsContract
import android.view.Menu
import android.view.MenuItem
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.RelativeLayout
import android.widget.TextView
import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.PERMISSION_READ_CONTACTS
import com.simplemobiletools.contacts.R
import com.simplemobiletools.contacts.extensions.*
import com.simplemobiletools.contacts.helpers.*
import kotlinx.android.synthetic.main.activity_view_contact.*
import kotlinx.android.synthetic.main.item_event.view.*
import kotlinx.android.synthetic.main.item_view_email.view.*
import kotlinx.android.synthetic.main.item_view_phone_number.view.*
class ViewContactActivity : ContactActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_view_contact)
}
override fun onResume() {
super.onResume()
tryInitContact()
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu_view_contact, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.edit -> editContact(contact!!)
R.id.share -> shareContact()
R.id.delete -> deleteContact()
else -> return super.onOptionsItemSelected(item)
}
return true
}
private fun tryInitContact() {
handlePermission(PERMISSION_READ_CONTACTS) {
if (it) {
initContact()
} else {
toast(R.string.no_contacts_permission)
finish()
}
}
}
private fun initContact() {
var contactId = intent.getIntExtra(CONTACT_ID, 0)
val action = intent.action
if (contactId == 0 && (action == ContactsContract.QuickContact.ACTION_QUICK_CONTACT || action == Intent.ACTION_VIEW)) {
val data = intent.data
if (data != null) {
val rawId = if (data.path.contains("lookup")) {
getLookupUriRawId(data)
} else {
getContactUriRawId(data)
}
if (rawId != -1) {
contactId = rawId
}
}
}
if (contactId != 0) {
contact = ContactsHelper(this).getContactWithId(contactId)
if (contact == null) {
toast(R.string.unknown_error_occurred)
finish()
return
}
}
if (contact == null) {
finish()
return
}
setupEditContact()
setupTypePickers()
contact_send_sms.beVisibleIf(contact!!.phoneNumbers.isNotEmpty())
contact_start_call.beVisibleIf(contact!!.phoneNumbers.isNotEmpty())
contact_send_email.beVisibleIf(contact!!.emails.isNotEmpty())
contact_photo.background = ColorDrawable(config.primaryColor)
if (contact!!.photoUri.isEmpty()) {
showPhotoPlaceholder(contact_photo)
} else {
updateContactPhoto(contact!!.photoUri, contact_photo)
}
val textColor = config.textColor
contact_send_sms.applyColorFilter(textColor)
contact_start_call.applyColorFilter(textColor)
contact_send_email.applyColorFilter(textColor)
contact_name_image.applyColorFilter(textColor)
contact_number_image.applyColorFilter(textColor)
contact_email_image.applyColorFilter(textColor)
contact_event_image.applyColorFilter(textColor)
contact_source_image.applyColorFilter(textColor)
contact_send_sms.setOnClickListener { trySendSMS() }
contact_start_call.setOnClickListener { tryStartCall(contact!!) }
contact_send_email.setOnClickListener { trySendEmail() }
updateTextColors(contact_scrollview)
invalidateOptionsMenu()
}
private fun setupEditContact() {
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN)
contact!!.apply {
contact_first_name.text = firstName
contact_first_name.beVisibleIf(firstName.isNotEmpty())
contact_middle_name.text = middleName
contact_middle_name.beVisibleIf(middleName.isNotEmpty())
contact_surname.text = surname
contact_surname.beVisibleIf(surname.isNotEmpty())
if (firstName.isEmpty() && middleName.isEmpty() && surname.isEmpty()) {
contact_name_image.beInvisible()
(contact_photo.layoutParams as RelativeLayout.LayoutParams).bottomMargin = resources.getDimension(R.dimen.medium_margin).toInt()
}
contact_source.text = getPublicContactSource(source)
}
contact_toggle_favorite.apply {
beVisible()
setImageDrawable(getStarDrawable(contact!!.starred == 1))
tag = contact!!.starred
applyColorFilter(config.textColor)
}
setupPhoneNumbers()
setupEmails()
setupEvents()
}
private fun setupPhoneNumbers() {
val phoneNumbers = contact!!.phoneNumbers
phoneNumbers.forEachIndexed { index, number ->
var numberHolder = contact_numbers_holder.getChildAt(index)
if (numberHolder == null) {
numberHolder = layoutInflater.inflate(R.layout.item_view_phone_number, contact_numbers_holder, false)
contact_numbers_holder.addView(numberHolder)
}
numberHolder!!.apply {
contact_number.text = number.value
setupPhoneNumberTypePicker(contact_number_type, number.type)
}
}
contact_number_image.beVisibleIf(phoneNumbers.isNotEmpty())
contact_numbers_holder.beVisibleIf(phoneNumbers.isNotEmpty())
}
private fun setupEmails() {
val emails = contact!!.emails
emails.forEachIndexed { index, email ->
var emailHolder = contact_emails_holder.getChildAt(index)
if (emailHolder == null) {
emailHolder = layoutInflater.inflate(R.layout.item_view_email, contact_emails_holder, false)
contact_emails_holder.addView(emailHolder)
}
emailHolder!!.apply {
contact_email.text = email.value
setupEmailTypePicker(contact_email_type, email.type)
}
}
contact_email_image.beVisibleIf(emails.isNotEmpty())
contact_emails_holder.beVisibleIf(emails.isNotEmpty())
}
private fun setupEvents() {
val events = contact!!.events
events.forEachIndexed { index, event ->
var eventHolder = contact_events_holder.getChildAt(index)
if (eventHolder == null) {
eventHolder = layoutInflater.inflate(R.layout.item_event, contact_events_holder, false)
contact_events_holder.addView(eventHolder)
}
(eventHolder as ViewGroup).apply {
contact_event.apply {
getDateTime(event.value, this)
tag = event.value
alpha = 1f
}
setupEventTypePicker(this, event.type)
contact_event_remove.beGone()
}
}
contact_event_image.beVisibleIf(events.isNotEmpty())
contact_events_holder.beVisibleIf(events.isNotEmpty())
}
private fun setupTypePickers() {
if (contact!!.phoneNumbers.isEmpty()) {
val numberHolder = contact_numbers_holder.getChildAt(0)
(numberHolder as? ViewGroup)?.contact_number_type?.apply {
setupPhoneNumberTypePicker(this)
}
}
if (contact!!.emails.isEmpty()) {
val emailHolder = contact_emails_holder.getChildAt(0)
(emailHolder as? ViewGroup)?.contact_email_type?.apply {
setupEmailTypePicker(this)
}
}
if (contact!!.events.isEmpty()) {
val eventHolder = contact_events_holder.getChildAt(0)
(eventHolder as? ViewGroup)?.apply {
setupEventTypePicker(this)
}
}
}
private fun setupPhoneNumberTypePicker(numberTypeField: TextView, type: Int = DEFAULT_PHONE_NUMBER_TYPE) {
numberTypeField.setText(getPhoneNumberTextId(type))
}
private fun setupEmailTypePicker(emailTypeField: TextView, type: Int = DEFAULT_EMAIL_TYPE) {
emailTypeField.setText(getEmailTextId(type))
}
private fun setupEventTypePicker(eventHolder: ViewGroup, type: Int = DEFAULT_EVENT_TYPE) {
eventHolder.contact_event_type.setText(getEventTextId(type))
}
private fun getStarDrawable(on: Boolean) = resources.getDrawable(if (on) R.drawable.ic_star_on_big else R.drawable.ic_star_off_big)
}

View File

@ -19,7 +19,7 @@ import com.simplemobiletools.commons.views.MyRecyclerView
import com.simplemobiletools.contacts.R
import com.simplemobiletools.contacts.activities.SimpleActivity
import com.simplemobiletools.contacts.extensions.config
import com.simplemobiletools.contacts.extensions.openContact
import com.simplemobiletools.contacts.extensions.editContact
import com.simplemobiletools.contacts.extensions.shareContacts
import com.simplemobiletools.contacts.helpers.ContactsHelper
import com.simplemobiletools.contacts.interfaces.RefreshContactsListener
@ -51,7 +51,7 @@ class ContactsAdapter(activity: SimpleActivity, var contactItems: MutableList<Co
override fun prepareActionMode(menu: Menu) {
menu.apply {
findItem(R.id.cab_edit).isVisible = activity.config.callContact && isOneItemSelected()
findItem(R.id.cab_edit).isVisible = isOneItemSelected()
findItem(R.id.cab_remove).isVisible = isFavoritesFragment
findItem(R.id.cab_select_all).isVisible = isFavoritesFragment
findItem(R.id.cab_add_to_favorites).isVisible = !isFavoritesFragment
@ -104,8 +104,7 @@ class ContactsAdapter(activity: SimpleActivity, var contactItems: MutableList<Co
}
private fun editContact() {
activity.openContact(contactItems[selectedPositions.first()])
finishActMode()
activity.editContact(contactItems[selectedPositions.first()])
}
private fun askConfirmDelete() {

View File

@ -9,19 +9,24 @@ import com.simplemobiletools.commons.interfaces.MyAdapterListener
import com.simplemobiletools.contacts.R
import com.simplemobiletools.contacts.activities.SimpleActivity
import com.simplemobiletools.contacts.extensions.config
import com.simplemobiletools.contacts.models.ContactSource
import kotlinx.android.synthetic.main.item_filter_contact_source.view.*
import java.util.*
class FilterContactSourcesAdapter(val activity: SimpleActivity, private val contactSources: List<String>, private val displayContactSources: Set<String>) :
class FilterContactSourcesAdapter(val activity: SimpleActivity, private val contactSources: List<ContactSource>, private val displayContactSources: Set<String>) :
RecyclerView.Adapter<FilterContactSourcesAdapter.ViewHolder>() {
private val itemViews = SparseArray<View>()
private val selectedPositions = HashSet<Int>()
init {
contactSources.forEachIndexed { index, value ->
if (activity.config.showAllContacts() || displayContactSources.contains(value)) {
contactSources.forEachIndexed { index, contactSource ->
if (activity.config.showAllContacts() || displayContactSources.contains(contactSource.name)) {
selectedPositions.add(index)
}
if (contactSource.name == activity.config.localAccountName && contactSource.type == activity.config.localAccountType) {
contactSource.name = activity.getString(R.string.phone_storage)
}
}
}
@ -47,11 +52,7 @@ class FilterContactSourcesAdapter(val activity: SimpleActivity, private val cont
override fun itemLongClicked(position: Int) {}
}
fun getSelectedItemsSet(): HashSet<String> {
val selectedItemsSet = HashSet<String>(selectedPositions.size)
selectedPositions.forEach { selectedItemsSet.add(contactSources[it]) }
return selectedItemsSet
}
fun getSelectedItemsSet() = selectedPositions
override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ViewHolder {
val view = activity.layoutInflater.inflate(R.layout.item_filter_contact_source, parent, false)
@ -60,7 +61,7 @@ class FilterContactSourcesAdapter(val activity: SimpleActivity, private val cont
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val contactSource = contactSources[position]
itemViews.put(position, holder.bindView(contactSource))
itemViews.put(position, holder.bindView(contactSource.name))
toggleItemSelection(selectedPositions.contains(position), position)
}

View File

@ -58,7 +58,7 @@ class AddFavoritesDialog(val activity: SimpleActivity, private val callback: ()
val contactsHelper = ContactsHelper(activity)
val allDisplayedContacts = ArrayList<Contact>()
allContacts.mapTo(allDisplayedContacts, { it })
val selectedContacts = (view.select_contact_list.adapter as SelectContactsAdapter).getSelectedItemsSet()
val selectedContacts = (view?.select_contact_list?.adapter as? SelectContactsAdapter)?.getSelectedItemsSet() ?: LinkedHashSet()
val contactIDsToAdd = selectedContacts.map { it.contactId.toString() } as ArrayList<String>
contactsHelper.addFavorites(contactIDsToAdd)

View File

@ -8,17 +8,21 @@ import com.simplemobiletools.contacts.activities.SimpleActivity
import com.simplemobiletools.contacts.adapters.FilterContactSourcesAdapter
import com.simplemobiletools.contacts.extensions.config
import com.simplemobiletools.contacts.helpers.ContactsHelper
import com.simplemobiletools.contacts.models.ContactSource
import kotlinx.android.synthetic.main.dialog_export_contacts.view.*
import java.io.File
import java.util.*
class ExportContactsDialog(val activity: SimpleActivity, val path: String, private val callback: (file: File, contactSources: HashSet<String>) -> Unit) {
private var contactSources = ArrayList<ContactSource>()
init {
val view = (activity.layoutInflater.inflate(R.layout.dialog_export_contacts, null) as ViewGroup).apply {
export_contacts_folder.text = activity.humanizePath(path)
export_contacts_filename.setText("contacts_${System.currentTimeMillis() / 1000}")
export_contacts_filename.setText("contacts_${activity.getCurrentFormattedDateTime()}")
ContactsHelper(activity).getContactSources {
it.mapTo(contactSources, { it.copy() })
activity.runOnUiThread {
export_contacts_list.adapter = FilterContactSourcesAdapter(activity, it, activity.config.displayContactSources)
}
@ -41,8 +45,12 @@ class ExportContactsDialog(val activity: SimpleActivity, val path: String, priva
return@setOnClickListener
}
val contactSources = (view.export_contacts_list.adapter as FilterContactSourcesAdapter).getSelectedItemsSet()
callback(file, contactSources)
val selectedIndexes = (view.export_contacts_list.adapter as FilterContactSourcesAdapter).getSelectedItemsSet()
val selectedContactSources = HashSet<String>()
selectedIndexes.forEach {
selectedContactSources.add(contactSources[it].name)
}
callback(file, selectedContactSources)
dismiss()
}
else -> activity.toast(R.string.invalid_name)

View File

@ -7,11 +7,14 @@ import com.simplemobiletools.contacts.activities.SimpleActivity
import com.simplemobiletools.contacts.adapters.FilterContactSourcesAdapter
import com.simplemobiletools.contacts.extensions.config
import com.simplemobiletools.contacts.helpers.ContactsHelper
import com.simplemobiletools.contacts.models.ContactSource
import kotlinx.android.synthetic.main.dialog_filter_contact_sources.view.*
import java.util.*
class FilterContactSourcesDialog(val activity: SimpleActivity, private val callback: () -> Unit) {
private var dialog: AlertDialog? = null
private val view = activity.layoutInflater.inflate(R.layout.dialog_filter_contact_sources, null)
private var contactSources = ArrayList<ContactSource>()
init {
ContactsHelper(activity).getContactSources {
@ -19,6 +22,7 @@ class FilterContactSourcesDialog(val activity: SimpleActivity, private val callb
return@getContactSources
}
it.mapTo(contactSources, { it.copy() })
val selectedSources = activity.config.displayContactSources
activity.runOnUiThread {
view.filter_contact_sources_list.adapter = FilterContactSourcesAdapter(activity, it, selectedSources)
@ -34,9 +38,14 @@ class FilterContactSourcesDialog(val activity: SimpleActivity, private val callb
}
private fun confirmEventTypes() {
val selectedItems = (view.filter_contact_sources_list.adapter as FilterContactSourcesAdapter).getSelectedItemsSet()
if (activity.config.displayContactSources != selectedItems) {
activity.config.displayContactSources = selectedItems
val selectedIndexes = (view.filter_contact_sources_list.adapter as FilterContactSourcesAdapter).getSelectedItemsSet()
val selectedContactSources = HashSet<String>()
selectedIndexes.forEach {
selectedContactSources.add(contactSources[it].name)
}
if (activity.config.displayContactSources != selectedContactSources) {
activity.config.displayContactSources = selectedContactSources
callback()
}
dialog?.dismiss()

View File

@ -4,22 +4,26 @@ import android.support.v7.app.AlertDialog
import android.view.ViewGroup
import com.simplemobiletools.commons.extensions.setupDialogStuff
import com.simplemobiletools.commons.extensions.toast
import com.simplemobiletools.commons.extensions.value
import com.simplemobiletools.contacts.R
import com.simplemobiletools.contacts.activities.SimpleActivity
import com.simplemobiletools.contacts.extensions.config
import com.simplemobiletools.contacts.extensions.getPublicContactSource
import com.simplemobiletools.contacts.extensions.showContactSourcePicker
import com.simplemobiletools.contacts.helpers.VcfImporter
import com.simplemobiletools.contacts.helpers.VcfImporter.ImportResult.IMPORT_FAIL
import kotlinx.android.synthetic.main.dialog_import_contacts.view.*
class ImportContactsDialog(val activity: SimpleActivity, val path: String, private val callback: (refreshView: Boolean) -> Unit) {
private var targetContactSource = ""
init {
val view = (activity.layoutInflater.inflate(R.layout.dialog_import_contacts, null) as ViewGroup).apply {
import_contacts_title.text = activity.config.lastUsedContactSource
targetContactSource = activity.config.lastUsedContactSource
import_contacts_title.text = activity.getPublicContactSource(targetContactSource)
import_contacts_title.setOnClickListener {
activity.showContactSourcePicker(import_contacts_title.value) {
import_contacts_title.text = it
activity.showContactSourcePicker(targetContactSource) {
targetContactSource = it
import_contacts_title.text = activity.getPublicContactSource(it)
}
}
}
@ -32,7 +36,7 @@ class ImportContactsDialog(val activity: SimpleActivity, val path: String, priva
getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
activity.toast(R.string.importing)
Thread {
val result = VcfImporter(activity).importContacts(path, view.import_contacts_title.value)
val result = VcfImporter(activity).importContacts(path, targetContactSource)
handleParseResult(result)
dismiss()
}.start()

View File

@ -2,7 +2,6 @@ package com.simplemobiletools.contacts.extensions
import android.content.Intent
import android.net.Uri
import com.simplemobiletools.commons.R
import com.simplemobiletools.commons.activities.BaseSimpleActivity
import com.simplemobiletools.commons.dialogs.RadioGroupDialog
import com.simplemobiletools.commons.extensions.getFilePublicUri
@ -11,6 +10,7 @@ import com.simplemobiletools.commons.extensions.toast
import com.simplemobiletools.commons.helpers.PERMISSION_CALL_PHONE
import com.simplemobiletools.commons.models.RadioItem
import com.simplemobiletools.contacts.BuildConfig
import com.simplemobiletools.contacts.R
import com.simplemobiletools.contacts.activities.SimpleActivity
import com.simplemobiletools.contacts.helpers.ContactsHelper
import com.simplemobiletools.contacts.helpers.VcfExporter
@ -51,10 +51,15 @@ fun SimpleActivity.tryStartCall(contact: Contact) {
fun SimpleActivity.showContactSourcePicker(currentSource: String, callback: (newSource: String) -> Unit) {
ContactsHelper(this).getContactSources {
val items = ArrayList<RadioItem>()
val sources = it
val sources = it.map { it.name }
var currentSourceIndex = -1
sources.forEachIndexed { index, account ->
items.add(RadioItem(index, account))
var publicAccount = account
if (account == config.localAccountName) {
publicAccount = getString(R.string.phone_storage)
}
items.add(RadioItem(index, publicAccount))
if (account == currentSource) {
currentSourceIndex = index
}
@ -68,6 +73,8 @@ fun SimpleActivity.showContactSourcePicker(currentSource: String, callback: (new
}
}
fun SimpleActivity.getPublicContactSource(source: String) = if (source == config.localAccountName) getString(R.string.phone_storage) else source
fun BaseSimpleActivity.shareContacts(contacts: ArrayList<Contact>) {
val file = getTempFile()
if (file == null) {

View File

@ -8,12 +8,13 @@ import android.net.Uri
import android.os.Build
import android.provider.ContactsContract
import android.support.v4.content.FileProvider
import com.simplemobiletools.commons.R
import com.simplemobiletools.commons.extensions.getIntValue
import com.simplemobiletools.commons.extensions.isLollipopPlus
import com.simplemobiletools.commons.extensions.toast
import com.simplemobiletools.contacts.BuildConfig
import com.simplemobiletools.contacts.R
import com.simplemobiletools.contacts.activities.EditContactActivity
import com.simplemobiletools.contacts.activities.ViewContactActivity
import com.simplemobiletools.contacts.helpers.CONTACT_ID
import com.simplemobiletools.contacts.helpers.Config
import com.simplemobiletools.contacts.models.Contact
@ -21,7 +22,14 @@ import java.io.File
val Context.config: Config get() = Config.newInstance(applicationContext)
fun Context.openContact(contact: Contact) {
fun Context.viewContact(contact: Contact) {
Intent(applicationContext, ViewContactActivity::class.java).apply {
putExtra(CONTACT_ID, contact.id)
startActivity(this)
}
}
fun Context.editContact(contact: Contact) {
Intent(applicationContext, EditContactActivity::class.java).apply {
putExtra(CONTACT_ID, contact.id)
startActivity(this)
@ -55,7 +63,9 @@ fun Context.getLookupUriRawId(dataUri: Uri): Int {
val lookupKey = getLookupKeyFromUri(dataUri)
if (lookupKey != null && isLollipopPlus()) {
val uri = lookupContactUri(lookupKey, this)
return getContactUriRawId(uri)
if (uri != null) {
return getContactUriRawId(uri)
}
}
return -1
}
@ -94,9 +104,13 @@ fun isEncodedContactUri(uri: Uri?): Boolean {
return lastPathSegment == "encoded"
}
fun lookupContactUri(lookup: String, context: Context): Uri {
fun lookupContactUri(lookup: String, context: Context): Uri? {
val lookupUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_LOOKUP_URI, lookup)
return ContactsContract.Contacts.lookupContact(context.contentResolver, lookupUri)
return try {
ContactsContract.Contacts.lookupContact(context.contentResolver, lookupUri)
} catch (e: Exception) {
null
}
}
fun Context.getCachePhoto(): File {

View File

@ -13,10 +13,10 @@ import com.simplemobiletools.contacts.activities.MainActivity
import com.simplemobiletools.contacts.activities.SimpleActivity
import com.simplemobiletools.contacts.adapters.ContactsAdapter
import com.simplemobiletools.contacts.extensions.config
import com.simplemobiletools.contacts.extensions.openContact
import com.simplemobiletools.contacts.extensions.editContact
import com.simplemobiletools.contacts.extensions.tryStartCall
import com.simplemobiletools.contacts.helpers.Config
import com.simplemobiletools.contacts.helpers.ContactsHelper
import com.simplemobiletools.contacts.extensions.viewContact
import com.simplemobiletools.contacts.helpers.*
import com.simplemobiletools.contacts.models.Contact
import kotlinx.android.synthetic.main.fragment_layout.view.*
@ -119,15 +119,17 @@ abstract class MyViewPagerFragment(context: Context, attributeSet: AttributeSet)
if (currAdapter == null || forceListRedraw) {
forceListRedraw = false
ContactsAdapter(activity as SimpleActivity, contacts, activity, this is FavoritesFragment, fragment_list, fragment_fastscroller) {
if (config.callContact) {
val contact = it as Contact
if (contact.phoneNumbers.isNotEmpty()) {
(activity as SimpleActivity).tryStartCall(it)
} else {
activity!!.toast(R.string.no_phone_number_found)
when (config.onContactClick) {
ON_CLICK_CALL_CONTACT -> {
val contact = it as Contact
if (contact.phoneNumbers.isNotEmpty()) {
(activity as SimpleActivity).tryStartCall(it)
} else {
activity!!.toast(R.string.no_phone_number_found)
}
}
} else {
context!!.openContact(it as Contact)
ON_CLICK_VIEW_CONTACT -> context!!.viewContact(it as Contact)
ON_CLICK_EDIT_CONTACT -> context!!.editContact(it as Contact)
}
}.apply {
setupDragListener(true)
@ -166,7 +168,7 @@ abstract class MyViewPagerFragment(context: Context, attributeSet: AttributeSet)
}
fun onSearchQueryChanged(text: String) {
(fragment_list.adapter as ContactsAdapter).apply {
(fragment_list.adapter as? ContactsAdapter)?.apply {
val filtered = contactsIgnoringSearch.filter {
it.getFullName(startNameWithSurname).contains(text, true) ||
it.phoneNumbers.any { it.value.contains(text, true) } ||

View File

@ -14,17 +14,13 @@ class Config(context: Context) : BaseConfig(context) {
get() = prefs.getInt(SORTING, SORT_BY_FIRST_NAME)
set(sorting) = prefs.edit().putInt(SORTING, sorting).apply()
var callContact: Boolean
get() = prefs.getBoolean(CALL_CONTACT_ON_CLICK, false)
set(callContact) = prefs.edit().putBoolean(CALL_CONTACT_ON_CLICK, callContact).apply()
var displayContactSources: Set<String>
get() = prefs.getStringSet(DISPLAY_CONTACT_SOURCES, hashSetOf("-1"))
set(displayContactSources) = prefs.edit().remove(DISPLAY_CONTACT_SOURCES).putStringSet(DISPLAY_CONTACT_SOURCES, displayContactSources).apply()
fun showAllContacts() = displayContactSources.size == 1 && displayContactSources.first() == "-1"
var showContactThumbnails : Boolean
var showContactThumbnails: Boolean
get() = prefs.getBoolean(SHOW_CONTACT_THUMBNAILS, true)
set(showContactThumbnails) = prefs.edit().putBoolean(SHOW_CONTACT_THUMBNAILS, showContactThumbnails).apply()
@ -43,4 +39,16 @@ class Config(context: Context) : BaseConfig(context) {
var lastUsedViewPagerPage: Int
get() = prefs.getInt(LAST_USED_VIEW_PAGER_PAGE, 0)
set(lastUsedViewPagerPage) = prefs.edit().putInt(LAST_USED_VIEW_PAGER_PAGE, lastUsedViewPagerPage).apply()
var localAccountName: String
get() = prefs.getString(LOCAL_ACCOUNT_NAME, "-1")
set(localAccountName) = prefs.edit().putString(LOCAL_ACCOUNT_NAME, localAccountName).apply()
var localAccountType: String
get() = prefs.getString(LOCAL_ACCOUNT_TYPE, "-1")
set(localAccountType) = prefs.edit().putString(LOCAL_ACCOUNT_TYPE, localAccountType).apply()
var onContactClick: Int
get() = prefs.getInt(ON_CONTACT_CLICK, ON_CLICK_VIEW_CONTACT)
set(onContactClick) = prefs.edit().putInt(ON_CONTACT_CLICK, onContactClick).apply()
}

View File

@ -3,15 +3,18 @@ package com.simplemobiletools.contacts.helpers
import android.provider.ContactsContract.CommonDataKinds
// shared prefs
const val CALL_CONTACT_ON_CLICK = "call_contact_on_click"
const val SHOW_CONTACT_THUMBNAILS = "show_contact_thumbnails"
const val SHOW_PHONE_NUMBERS = "show_phone_numbers"
const val DISPLAY_CONTACT_SOURCES = "display_contact_sources"
const val START_NAME_WITH_SURNAME = "start_name_with_surname"
const val LAST_USED_CONTACT_SOURCE = "last_used_contact_source"
const val LAST_USED_VIEW_PAGER_PAGE = "last_used_view_pager_page"
const val LOCAL_ACCOUNT_NAME = "local_account_name"
const val LOCAL_ACCOUNT_TYPE = "local_account_type"
const val ON_CONTACT_CLICK = "on_contact_click"
const val CONTACT_ID = "contact_id"
const val SMT_PRIVATE = "smt_private"
// contact photo changes
const val PHOTO_ADDED = 1
@ -27,7 +30,7 @@ const val DEFAULT_EVENT_TYPE = CommonDataKinds.Event.TYPE_BIRTHDAY
// export/import
const val BEGIN_VCARD = "BEGIN:VCARD"
const val END_VCARD = "END:VCARD"
const val N = "N:"
const val N = "N"
const val TEL = "TEL"
const val BDAY = "BDAY:"
const val ANNIVERSARY = "ANNIVERSARY:"
@ -50,3 +53,7 @@ const val HOME_FAX = "HOME;FAX"
const val PAGER = "PAGER"
const val MOBILE = "MOBILE"
const val VOICE = "VOICE"
const val ON_CLICK_CALL_CONTACT = 1
const val ON_CLICK_VIEW_CONTACT = 2
const val ON_CLICK_EDIT_CONTACT = 3

View File

@ -22,10 +22,7 @@ import com.simplemobiletools.commons.helpers.SORT_BY_SURNAME
import com.simplemobiletools.commons.helpers.SORT_DESCENDING
import com.simplemobiletools.contacts.R
import com.simplemobiletools.contacts.extensions.config
import com.simplemobiletools.contacts.models.Contact
import com.simplemobiletools.contacts.models.Email
import com.simplemobiletools.contacts.models.Event
import com.simplemobiletools.contacts.models.PhoneNumber
import com.simplemobiletools.contacts.models.*
import java.io.ByteArrayOutputStream
import java.util.*
@ -229,11 +226,11 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
return null
}
fun getContactSources(callback: (ArrayList<String>) -> Unit) {
val accounts = HashSet<String>()
fun getContactSources(callback: (ArrayList<ContactSource>) -> Unit) {
val sources = LinkedHashSet<ContactSource>()
Thread {
val uri = ContactsContract.RawContacts.CONTENT_URI
val projection = arrayOf(ContactsContract.RawContacts.ACCOUNT_NAME)
val projection = arrayOf(ContactsContract.RawContacts.ACCOUNT_NAME, ContactsContract.RawContacts.ACCOUNT_TYPE)
var cursor: Cursor? = null
try {
@ -241,14 +238,17 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
if (cursor?.moveToFirst() == true) {
do {
val name = cursor.getStringValue(ContactsContract.RawContacts.ACCOUNT_NAME) ?: continue
accounts.add(name)
val type = cursor.getStringValue(ContactsContract.RawContacts.ACCOUNT_TYPE) ?: continue
val contactSource = ContactSource(name, type)
sources.add(contactSource)
} while (cursor.moveToNext())
}
} finally {
cursor?.close()
}
callback(ArrayList(accounts))
sources.add(ContactSource(activity.getString(R.string.phone_storage_hidden), SMT_PRIVATE))
callback(ArrayList(sources))
}.start()
}
@ -302,6 +302,24 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
return sort
}
private fun getRealContactId(id: Long): Int {
val uri = ContactsContract.Data.CONTENT_URI
val projection = getContactProjection()
val selection = "${ContactsContract.Data.MIMETYPE} = ? AND ${ContactsContract.Data.RAW_CONTACT_ID} = ?"
val selectionArgs = arrayOf(CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE, id.toString())
var cursor: Cursor? = null
try {
cursor = activity.contentResolver.query(uri, projection, selection, selectionArgs, null)
if (cursor?.moveToFirst() == true) {
return cursor.getIntValue(ContactsContract.Data.CONTACT_ID)
}
} finally {
cursor?.close()
}
return 0
}
fun updateContact(contact: Contact, photoUpdateStatus: Int): Boolean {
return try {
activity.toast(R.string.updating)
@ -434,100 +452,117 @@ class ContactsHelper(val activity: BaseSimpleActivity) {
}
fun insertContact(contact: Contact): Boolean {
return try {
val operations = ArrayList<ContentProviderOperation>()
ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI).apply {
withValue(ContactsContract.RawContacts.ACCOUNT_NAME, contact.source)
withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, getContactSourceType(contact.source))
operations.add(build())
}
// names
ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
withValue(CommonDataKinds.StructuredName.GIVEN_NAME, contact.firstName)
withValue(CommonDataKinds.StructuredName.MIDDLE_NAME, contact.middleName)
withValue(CommonDataKinds.StructuredName.FAMILY_NAME, contact.surname)
operations.add(build())
}
// phone numbers
contact.phoneNumbers.forEach {
ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
withValue(CommonDataKinds.Phone.NUMBER, it.value)
withValue(CommonDataKinds.Phone.TYPE, it.type)
operations.add(build())
}
}
// emails
contact.emails.forEach {
ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Email.CONTENT_ITEM_TYPE)
withValue(CommonDataKinds.Email.DATA, it.value)
withValue(CommonDataKinds.Email.TYPE, it.type)
operations.add(build())
}
}
// events
contact.events.forEach {
ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Event.CONTENT_ITEM_TYPE)
withValue(CommonDataKinds.Event.START_DATE, it.value)
withValue(CommonDataKinds.Event.TYPE, it.type)
operations.add(build())
}
}
// photo (inspired by https://gist.github.com/slightfoot/5985900)
var fullSizePhotoData: ByteArray? = null
var scaledSizePhotoData: ByteArray?
if (contact.photoUri.isNotEmpty()) {
val photoUri = Uri.parse(contact.photoUri)
val bitmap = MediaStore.Images.Media.getBitmap(activity.contentResolver, photoUri)
val thumbnailSize = getThumbnailSize()
val scaledPhoto = Bitmap.createScaledBitmap(bitmap, thumbnailSize, thumbnailSize, false)
scaledSizePhotoData = bitmapToByteArray(scaledPhoto)
fullSizePhotoData = bitmapToByteArray(bitmap)
scaledPhoto.recycle()
bitmap.recycle()
ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Photo.CONTENT_ITEM_TYPE)
withValue(CommonDataKinds.Photo.PHOTO, scaledSizePhotoData)
operations.add(build())
}
}
val results: Array<ContentProviderResult>
return if (contact.source == SMT_PRIVATE) {
insertLocalContact(contact)
} else {
try {
results = activity.contentResolver.applyBatch(ContactsContract.AUTHORITY, operations)
} finally {
scaledSizePhotoData = null
}
val operations = ArrayList<ContentProviderOperation>()
ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI).apply {
withValue(ContactsContract.RawContacts.ACCOUNT_NAME, contact.source)
withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, getContactSourceType(contact.source))
operations.add(build())
}
// fullsize photo
if (contact.photoUri.isNotEmpty() && fullSizePhotoData != null) {
val rawContactId = ContentUris.parseId(results[0].uri)
addFullSizePhoto(rawContactId, fullSizePhotoData)
}
// names
ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
withValue(CommonDataKinds.StructuredName.GIVEN_NAME, contact.firstName)
withValue(CommonDataKinds.StructuredName.MIDDLE_NAME, contact.middleName)
withValue(CommonDataKinds.StructuredName.FAMILY_NAME, contact.surname)
operations.add(build())
}
true
} catch (e: Exception) {
activity.showErrorToast(e)
false
// phone numbers
contact.phoneNumbers.forEach {
ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
withValue(CommonDataKinds.Phone.NUMBER, it.value)
withValue(CommonDataKinds.Phone.TYPE, it.type)
operations.add(build())
}
}
// emails
contact.emails.forEach {
ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Email.CONTENT_ITEM_TYPE)
withValue(CommonDataKinds.Email.DATA, it.value)
withValue(CommonDataKinds.Email.TYPE, it.type)
operations.add(build())
}
}
// events
contact.events.forEach {
ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Event.CONTENT_ITEM_TYPE)
withValue(CommonDataKinds.Event.START_DATE, it.value)
withValue(CommonDataKinds.Event.TYPE, it.type)
operations.add(build())
}
}
// photo (inspired by https://gist.github.com/slightfoot/5985900)
var fullSizePhotoData: ByteArray? = null
var scaledSizePhotoData: ByteArray?
if (contact.photoUri.isNotEmpty()) {
val photoUri = Uri.parse(contact.photoUri)
val bitmap = MediaStore.Images.Media.getBitmap(activity.contentResolver, photoUri)
val thumbnailSize = getThumbnailSize()
val scaledPhoto = Bitmap.createScaledBitmap(bitmap, thumbnailSize, thumbnailSize, false)
scaledSizePhotoData = bitmapToByteArray(scaledPhoto)
fullSizePhotoData = bitmapToByteArray(bitmap)
scaledPhoto.recycle()
bitmap.recycle()
ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Photo.CONTENT_ITEM_TYPE)
withValue(CommonDataKinds.Photo.PHOTO, scaledSizePhotoData)
operations.add(build())
}
}
val results: Array<ContentProviderResult>
try {
results = activity.contentResolver.applyBatch(ContactsContract.AUTHORITY, operations)
} finally {
scaledSizePhotoData = null
}
// fullsize photo
val rawId = ContentUris.parseId(results[0].uri)
if (contact.photoUri.isNotEmpty() && fullSizePhotoData != null) {
addFullSizePhoto(rawId, fullSizePhotoData)
}
// favorite
val userId = getRealContactId(rawId)
if (userId != 0 && contact.starred == 1) {
val uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, userId.toString())
val contentValues = ContentValues(1)
contentValues.put(ContactsContract.Contacts.STARRED, contact.starred)
activity.contentResolver.update(uri, contentValues, null, null)
}
true
} catch (e: Exception) {
activity.showErrorToast(e)
false
}
}
}
private fun insertLocalContact(contact: Contact): Boolean {
return true
}
private fun addFullSizePhoto(contactId: Long, fullSizePhotoData: ByteArray) {
val baseUri = ContentUris.withAppendedId(ContactsContract.RawContacts.CONTENT_URI, contactId)
val displayPhotoUri = Uri.withAppendedPath(baseUri, ContactsContract.RawContacts.DisplayPhoto.CONTENT_DIRECTORY)

View File

@ -0,0 +1,64 @@
package com.simplemobiletools.contacts.helpers
import java.io.ByteArrayOutputStream
import java.net.URLEncoder
// https://alvinalexander.com/java/jwarehouse/android/core/java/com/google/android/mms/pdu/QuotedPrintable.java.shtml
object QuotedPrintable {
private const val ESCAPE_CHAR: Byte = '='.toByte()
fun decode(value: String?): String {
val bytes = value?.toByteArray()
if (bytes == null || bytes.isEmpty()) {
return ""
}
val buffer = ByteArrayOutputStream()
var i = 0
while (i < bytes.size) {
val b = bytes[i].toInt()
if (b == ESCAPE_CHAR.toInt()) {
try {
if ('\r' == bytes[i + 1].toChar() && '\n' == bytes[i + 2].toChar()) {
i += 3
continue
}
val u = Character.digit(bytes[++i].toChar(), 16)
val l = Character.digit(bytes[++i].toChar(), 16)
if (u == -1 || l == -1) {
return ""
}
buffer.write(((u shl 4) + l).toChar().toInt())
} catch (e: ArrayIndexOutOfBoundsException) {
return ""
}
} else {
buffer.write(b)
}
i++
}
return String(buffer.toByteArray())
}
fun encode(value: String): String {
val result = StringBuilder()
value.forEach {
if (it == ' ') {
result.append(' ')
} else {
val urlEncoded = urlEncode(it.toString())
if (urlEncoded == it.toString()) {
val hex = String.format("%04x", it.toInt()).trimStart('0').toUpperCase()
result.append("=$hex")
} else {
result.append(urlEncoded)
}
}
}
return result.toString()
}
fun urlEncode(value: String) = URLEncoder.encode(value, "UTF-8").replace("+", " ").replace('%', '=')
}

View File

@ -36,7 +36,7 @@ class VcfExporter {
for (contact in contacts) {
out.writeLn(BEGIN_VCARD)
out.writeLn(VERSION_2_1)
out.writeLn("$N${contact.surname};${contact.firstName};${contact.middleName};;")
out.writeLn("$N${getNames(contact)}")
contact.phoneNumbers.forEach {
out.writeLn("$TEL;${getPhoneNumberLabel(it.type)}:${it.value}")
@ -91,6 +91,24 @@ class VcfExporter {
})
}
private fun getNames(contact: Contact): String {
var result = ""
var firstName = contact.firstName
var surName = contact.surname
var middleName = contact.middleName
if (QuotedPrintable.urlEncode(firstName) != firstName
|| QuotedPrintable.urlEncode(surName) != surName
|| QuotedPrintable.urlEncode(middleName) != middleName) {
firstName = QuotedPrintable.encode(firstName)
surName = QuotedPrintable.encode(surName)
middleName = QuotedPrintable.encode(middleName)
result += ";CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE"
}
return "$result:$surName;$firstName;$middleName;;"
}
private fun getPhoneNumberLabel(type: Int) = when (type) {
CommonDataKinds.Phone.TYPE_MOBILE -> CELL
CommonDataKinds.Phone.TYPE_HOME -> HOME

View File

@ -34,6 +34,10 @@ class VcfImporter(val activity: SimpleActivity) {
private var currentPhotoString = StringBuilder()
private var currentPhotoCompressionFormat = Bitmap.CompressFormat.JPEG
private var isGettingName = false
private var currentNameIsANSI = false
private var currentNameString = StringBuilder()
private var contactsImported = 0
private var contactsFailed = 0
@ -54,11 +58,15 @@ class VcfImporter(val activity: SimpleActivity) {
isGettingPhoto = false
}
continue
} else if (line.startsWith('\t') && isGettingName) {
currentNameString.append(line.trimStart('\t'))
isGettingName = false
parseNames()
}
when {
line.toUpperCase() == BEGIN_VCARD -> resetValues()
line.toUpperCase().startsWith(N) -> parseNames(line.substring(N.length))
line.toUpperCase().startsWith(N) -> addNames(line.substring(N.length))
line.toUpperCase().startsWith(TEL) -> addPhoneNumber(line.substring(TEL.length))
line.toUpperCase().startsWith(EMAIL) -> addEmail(line.substring(EMAIL.length))
line.toUpperCase().startsWith(BDAY) -> addBirthday(line.substring(BDAY.length))
@ -81,12 +89,23 @@ class VcfImporter(val activity: SimpleActivity) {
}
}
private fun parseNames(names: String) {
val nameParts = names.split(";")
curSurname = nameParts[0]
curFirstName = nameParts[1]
private fun addNames(names: String) {
val parts = names.split(":")
currentNameIsANSI = parts.first().toUpperCase().contains("QUOTED-PRINTABLE")
currentNameString.append(parts[1].trimEnd('='))
if (!isGettingName && currentNameIsANSI && names.endsWith('=')) {
isGettingName = true
} else {
parseNames()
}
}
private fun parseNames() {
val nameParts = currentNameString.split(";")
curSurname = if (currentNameIsANSI) QuotedPrintable.decode(nameParts[0]) else nameParts[0]
curFirstName = if (currentNameIsANSI) QuotedPrintable.decode(nameParts[1]) else nameParts[1]
if (nameParts.size > 2) {
curMiddleName = nameParts[2]
curMiddleName = if (currentNameIsANSI) QuotedPrintable.decode(nameParts[2]) else nameParts[2]
}
}
@ -198,5 +217,9 @@ class VcfImporter(val activity: SimpleActivity) {
isGettingPhoto = false
currentPhotoString = StringBuilder()
currentPhotoCompressionFormat = Bitmap.CompressFormat.JPEG
isGettingName = false
currentNameIsANSI = false
currentNameString = StringBuilder()
}
}

View File

@ -8,7 +8,7 @@ data class Contact(val id: Int, var firstName: String, var middleName: String, v
var phoneNumbers: ArrayList<PhoneNumber>, var emails: ArrayList<Email>, var events: ArrayList<Event>, var source: String,
var starred: Int, val contactId: Int, val thumbnailUri: String) : Comparable<Contact> {
companion object {
var sorting: Int = 0
var sorting = 0
}
override fun compareTo(other: Contact): Int {

View File

@ -0,0 +1,3 @@
package com.simplemobiletools.contacts.models
data class ContactSource(var name: String, var type: String)

View File

@ -10,7 +10,10 @@
android:id="@+id/contact_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/activity_margin">
android:paddingBottom="@dimen/medium_margin"
android:paddingLeft="@dimen/normal_margin"
android:paddingRight="@dimen/normal_margin"
android:paddingTop="@dimen/activity_margin">
<ImageView
android:id="@+id/contact_photo"
@ -28,8 +31,7 @@
android:layout_toRightOf="@+id/contact_photo"
android:adjustViewBounds="true"
android:padding="@dimen/tiny_margin"
android:src="@drawable/ic_star_off_big"
android:visibility="gone"/>
android:src="@drawable/ic_star_off_big"/>
<LinearLayout
android:id="@+id/contact_actions_holder"
@ -155,7 +157,7 @@
android:layout_toRightOf="@+id/contact_number_image"
android:orientation="vertical">
<include layout="@layout/item_phone_number"/>
<include layout="@layout/item_edit_phone_number"/>
</LinearLayout>
@ -193,7 +195,7 @@
android:layout_toRightOf="@+id/contact_name_image"
android:orientation="vertical">
<include layout="@layout/item_email"/>
<include layout="@layout/item_edit_email"/>
</LinearLayout>

View File

@ -17,7 +17,10 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_margin"
android:background="?attr/selectableItemBackground"
android:padding="@dimen/activity_margin">
android:paddingBottom="@dimen/activity_margin"
android:paddingLeft="@dimen/normal_margin"
android:paddingRight="@dimen/normal_margin"
android:paddingTop="@dimen/activity_margin">
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/settings_customize_colors"
@ -36,7 +39,10 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_margin"
android:background="?attr/selectableItemBackground"
android:padding="@dimen/activity_margin">
android:paddingBottom="@dimen/activity_margin"
android:paddingLeft="@dimen/normal_margin"
android:paddingRight="@dimen/normal_margin"
android:paddingTop="@dimen/activity_margin">
<com.simplemobiletools.commons.views.MySwitchCompat
android:id="@+id/settings_use_english"
@ -50,13 +56,39 @@
</RelativeLayout>
<RelativeLayout
android:id="@+id/settings_avoid_whats_new_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_margin"
android:background="?attr/selectableItemBackground"
android:paddingBottom="@dimen/activity_margin"
android:paddingLeft="@dimen/normal_margin"
android:paddingRight="@dimen/normal_margin"
android:paddingTop="@dimen/activity_margin">
<com.simplemobiletools.commons.views.MySwitchCompat
android:id="@+id/settings_avoid_whats_new"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@null"
android:clickable="false"
android:paddingLeft="@dimen/medium_margin"
android:paddingStart="@dimen/medium_margin"
android:text="@string/avoid_whats_new"/>
</RelativeLayout>
<RelativeLayout
android:id="@+id/settings_show_info_bubble_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_margin"
android:background="?attr/selectableItemBackground"
android:padding="@dimen/activity_margin">
android:paddingBottom="@dimen/activity_margin"
android:paddingLeft="@dimen/normal_margin"
android:paddingRight="@dimen/normal_margin"
android:paddingTop="@dimen/activity_margin">
<com.simplemobiletools.commons.views.MySwitchCompat
android:id="@+id/settings_show_info_bubble"
@ -76,7 +108,10 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_margin"
android:background="?attr/selectableItemBackground"
android:padding="@dimen/activity_margin">
android:paddingBottom="@dimen/activity_margin"
android:paddingLeft="@dimen/normal_margin"
android:paddingRight="@dimen/normal_margin"
android:paddingTop="@dimen/activity_margin">
<com.simplemobiletools.commons.views.MySwitchCompat
android:id="@+id/settings_show_contact_thumbnails"
@ -96,7 +131,10 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_margin"
android:background="?attr/selectableItemBackground"
android:padding="@dimen/activity_margin">
android:paddingBottom="@dimen/activity_margin"
android:paddingLeft="@dimen/normal_margin"
android:paddingRight="@dimen/normal_margin"
android:paddingTop="@dimen/activity_margin">
<com.simplemobiletools.commons.views.MySwitchCompat
android:id="@+id/settings_show_phone_numbers"
@ -110,33 +148,16 @@
</RelativeLayout>
<RelativeLayout
android:id="@+id/settings_call_contact_on_click_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_margin"
android:background="?attr/selectableItemBackground"
android:padding="@dimen/activity_margin">
<com.simplemobiletools.commons.views.MySwitchCompat
android:id="@+id/settings_call_contact_on_click"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@null"
android:clickable="false"
android:paddingLeft="@dimen/medium_margin"
android:paddingStart="@dimen/medium_margin"
android:text="@string/call_contact_on_click"/>
</RelativeLayout>
<RelativeLayout
android:id="@+id/settings_start_with_surname_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_margin"
android:background="?attr/selectableItemBackground"
android:padding="@dimen/activity_margin">
android:paddingBottom="@dimen/activity_margin"
android:paddingLeft="@dimen/normal_margin"
android:paddingRight="@dimen/normal_margin"
android:paddingTop="@dimen/activity_margin">
<com.simplemobiletools.commons.views.MySwitchCompat
android:id="@+id/settings_start_with_surname"
@ -149,5 +170,40 @@
android:text="@string/start_name_with_surname"/>
</RelativeLayout>
<RelativeLayout
android:id="@+id/settings_on_contact_click_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/medium_margin"
android:background="?attr/selectableItemBackground"
android:paddingBottom="@dimen/bigger_margin"
android:paddingLeft="@dimen/normal_margin"
android:paddingRight="@dimen/normal_margin"
android:paddingTop="@dimen/bigger_margin">
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/settings_on_contact_click_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toLeftOf="@+id/settings_on_contact_click"
android:layout_toStartOf="@+id/settings_on_contact_click"
android:paddingLeft="@dimen/medium_margin"
android:paddingRight="@dimen/medium_margin"
android:text="@string/on_contact_click"/>
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/settings_on_contact_click"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_marginEnd="@dimen/small_margin"
android:layout_marginRight="@dimen/small_margin"
android:background="@null"
android:clickable="false"/>
</RelativeLayout>
</LinearLayout>
</ScrollView>

View File

@ -0,0 +1,239 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/contact_scrollview"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RelativeLayout
android:id="@+id/contact_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/medium_margin"
android:paddingLeft="@dimen/normal_margin"
android:paddingRight="@dimen/normal_margin"
android:paddingTop="@dimen/activity_margin">
<ImageView
android:id="@+id/contact_photo"
android:layout_width="@dimen/contact_photo_size"
android:layout_height="@dimen/contact_photo_size"/>
<ImageView
android:id="@+id/contact_toggle_favorite"
style="@style/MyBorderlessBackgroundStyle"
android:layout_width="@dimen/contact_actions_size"
android:layout_height="@dimen/contact_actions_size"
android:layout_alignBottom="@id/contact_photo"
android:layout_alignTop="@+id/contact_photo"
android:layout_marginLeft="@dimen/medium_margin"
android:layout_toRightOf="@+id/contact_photo"
android:adjustViewBounds="true"
android:padding="@dimen/tiny_margin"
android:src="@drawable/ic_star_off_big"
android:visibility="gone"/>
<LinearLayout
android:id="@+id/contact_actions_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignBottom="@id/contact_photo"
android:layout_alignTop="@+id/contact_photo"
android:gravity="center_vertical|right">
<ImageView
android:id="@+id/contact_send_email"
style="@style/MyBorderlessBackgroundStyle"
android:layout_width="@dimen/contact_actions_size"
android:layout_height="@dimen/contact_actions_size"
android:adjustViewBounds="true"
android:paddingLeft="@dimen/activity_margin"
android:paddingRight="@dimen/activity_margin"
android:scaleType="center"
android:src="@drawable/ic_email"/>
<ImageView
android:id="@+id/contact_start_call"
style="@style/MyBorderlessBackgroundStyle"
android:layout_width="@dimen/contact_actions_size"
android:layout_height="@dimen/contact_actions_size"
android:layout_marginLeft="@dimen/normal_margin"
android:adjustViewBounds="true"
android:paddingLeft="@dimen/activity_margin"
android:paddingRight="@dimen/activity_margin"
android:scaleType="center"
android:src="@drawable/ic_phone"/>
<ImageView
android:id="@+id/contact_send_sms"
style="@style/MyBorderlessBackgroundStyle"
android:layout_width="@dimen/contact_actions_size"
android:layout_height="@dimen/contact_actions_size"
android:layout_marginLeft="@dimen/normal_margin"
android:adjustViewBounds="true"
android:paddingLeft="@dimen/activity_margin"
android:paddingRight="@dimen/activity_margin"
android:scaleType="center"
android:src="@drawable/ic_sms"/>
</LinearLayout>
<ImageView
android:id="@+id/contact_name_image"
android:layout_width="@dimen/contact_icons_size"
android:layout_height="@dimen/contact_icons_size"
android:layout_alignTop="@+id/contact_first_name"
android:paddingBottom="@dimen/small_margin"
android:paddingEnd="@dimen/small_margin"
android:paddingRight="@dimen/small_margin"
android:paddingTop="@dimen/medium_margin"
android:src="@drawable/ic_person"/>
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/contact_first_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/contact_photo"
android:layout_centerVertical="true"
android:layout_marginTop="@dimen/normal_margin"
android:layout_toRightOf="@+id/contact_name_image"
android:lines="1"
android:maxLines="1"
android:paddingBottom="@dimen/normal_margin"
android:paddingLeft="@dimen/small_margin"
android:paddingTop="@dimen/normal_margin"
android:singleLine="true"
android:textSize="@dimen/bigger_text_size"/>
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/contact_middle_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/contact_first_name"
android:layout_centerVertical="true"
android:layout_toRightOf="@+id/contact_name_image"
android:lines="1"
android:maxLines="1"
android:paddingBottom="@dimen/normal_margin"
android:paddingLeft="@dimen/small_margin"
android:paddingTop="@dimen/normal_margin"
android:singleLine="true"
android:textSize="@dimen/bigger_text_size"/>
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/contact_surname"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/contact_middle_name"
android:layout_centerVertical="true"
android:layout_toRightOf="@+id/contact_name_image"
android:lines="1"
android:maxLines="1"
android:paddingBottom="@dimen/normal_margin"
android:paddingLeft="@dimen/small_margin"
android:paddingTop="@dimen/normal_margin"
android:singleLine="true"
android:textSize="@dimen/bigger_text_size"/>
<ImageView
android:id="@+id/contact_number_image"
android:layout_width="@dimen/contact_icons_size"
android:layout_height="@dimen/contact_icons_size"
android:layout_alignTop="@+id/contact_numbers_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_phone"/>
<LinearLayout
android:id="@+id/contact_numbers_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/contact_surname"
android:layout_toRightOf="@+id/contact_number_image"
android:orientation="vertical"
android:paddingLeft="@dimen/small_margin">
<include layout="@layout/item_view_phone_number"/>
</LinearLayout>
<ImageView
android:id="@+id/contact_email_image"
android:layout_width="@dimen/contact_icons_size"
android:layout_height="@dimen/contact_icons_size"
android:layout_alignTop="@+id/contact_emails_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_email"/>
<LinearLayout
android:id="@+id/contact_emails_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/contact_numbers_holder"
android:layout_toRightOf="@+id/contact_name_image"
android:orientation="vertical"
android:paddingLeft="@dimen/small_margin">
<include layout="@layout/item_view_email"/>
</LinearLayout>
<ImageView
android:id="@+id/contact_event_image"
android:layout_width="@dimen/contact_icons_size"
android:layout_height="@dimen/contact_icons_size"
android:layout_alignTop="@+id/contact_events_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_cake"/>
<LinearLayout
android:id="@+id/contact_events_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/contact_emails_holder"
android:layout_toRightOf="@+id/contact_name_image"
android:orientation="vertical">
<include layout="@layout/item_event"/>
</LinearLayout>
<ImageView
android:id="@+id/contact_source_image"
android:layout_width="@dimen/contact_icons_size"
android:layout_height="@dimen/contact_icons_size"
android:layout_alignTop="@+id/contact_source"
android:paddingBottom="@dimen/small_margin"
android:paddingEnd="@dimen/small_margin"
android:paddingRight="@dimen/small_margin"
android:paddingTop="@dimen/medium_margin"
android:src="@drawable/ic_account_box"/>
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/contact_source"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/contact_events_holder"
android:layout_centerVertical="true"
android:layout_toRightOf="@+id/contact_name_image"
android:background="?attr/selectableItemBackground"
android:lines="1"
android:maxLines="1"
android:paddingBottom="@dimen/normal_margin"
android:paddingLeft="@dimen/small_margin"
android:paddingTop="@dimen/normal_margin"
android:singleLine="true"
android:textSize="@dimen/bigger_text_size"
tools:text="hello@simplemobiletools.com"/>
</RelativeLayout>
</ScrollView>

View File

@ -34,7 +34,7 @@
android:paddingLeft="@dimen/small_margin"
android:paddingRight="@dimen/small_margin"
android:paddingTop="@dimen/small_margin"
android:textSize="@dimen/normal_text_size"
android:textSize="@dimen/bigger_text_size"
tools:text="John Doe"/>
<TextView
@ -46,7 +46,7 @@
android:layout_toLeftOf="@+id/contact_checkbox"
android:layout_toRightOf="@+id/contact_tmb"
android:maxLines="1"
android:textSize="@dimen/normal_text_size"
android:textSize="@dimen/bigger_text_size"
tools:text="0123 456 789"/>
<com.simplemobiletools.commons.views.MyAppCompatCheckbox

View File

@ -31,7 +31,7 @@
android:ellipsize="end"
android:gravity="center_vertical"
android:maxLines="1"
android:textSize="@dimen/bigger_text_size"
android:textSize="@dimen/big_text_size"
tools:text="John Doe"/>
<com.simplemobiletools.commons.views.MyAppCompatCheckbox

View File

@ -31,7 +31,7 @@
android:layout_toRightOf="@+id/contact_tmb"
android:ellipsize="end"
android:maxLines="1"
android:textSize="@dimen/normal_text_size"
android:textSize="@dimen/bigger_text_size"
tools:text="John Doe"/>
<TextView
@ -42,7 +42,7 @@
android:layout_below="@+id/contact_name"
android:layout_toRightOf="@+id/contact_tmb"
android:maxLines="1"
android:textSize="@dimen/normal_text_size"
android:textSize="@dimen/bigger_text_size"
tools:text="0123 456 789"/>
</RelativeLayout>

View File

@ -31,7 +31,7 @@
android:ellipsize="end"
android:gravity="center_vertical"
android:maxLines="1"
android:textSize="@dimen/bigger_text_size"
android:textSize="@dimen/big_text_size"
tools:text="John Doe"/>
</RelativeLayout>

View File

@ -15,8 +15,8 @@
android:alpha="0.5"
android:background="?attr/selectableItemBackground"
android:paddingBottom="@dimen/normal_margin"
android:paddingLeft="@dimen/medium_margin"
android:paddingRight="@dimen/medium_margin"
android:paddingLeft="@dimen/small_margin"
android:paddingRight="@dimen/small_margin"
android:paddingTop="@dimen/normal_margin"
android:text="@string/unknown"
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_email_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/normal_margin"
android:paddingTop="@dimen/normal_margin">
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/contact_email"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toLeftOf="@+id/contact_email_type"
android:layout_toStartOf="@+id/contact_email_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_email_type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/contact_email"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignTop="@+id/contact_email"
android:layout_centerVertical="true"
android:background="?attr/selectableItemBackground"
android:gravity="center"
android:paddingLeft="@dimen/medium_margin"
android:paddingRight="@dimen/medium_margin"
android:text="@string/home"
android:textSize="@dimen/bigger_text_size"/>
</RelativeLayout>

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_number_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/normal_margin"
android:paddingTop="@dimen/normal_margin">
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/contact_number"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toLeftOf="@+id/contact_number_type"
android:layout_toStartOf="@+id/contact_number_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_number_type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/contact_number"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignTop="@+id/contact_number"
android:layout_centerVertical="true"
android:background="?attr/selectableItemBackground"
android:gravity="center"
android:paddingLeft="@dimen/medium_margin"
android:paddingRight="@dimen/medium_margin"
android:text="@string/mobile"
android:textSize="@dimen/bigger_text_size"/>
</RelativeLayout>

View File

@ -6,14 +6,14 @@
android:icon="@drawable/ic_check"
android:title="@string/save"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/delete"
android:icon="@drawable/ic_delete"
android:title="@string/delete"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/share"
android:icon="@drawable/ic_share"
android:title="@string/share"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/delete"
android:icon="@drawable/ic_delete"
android:title="@string/delete"
app:showAsAction="ifRoom"/>
</menu>

View File

@ -0,0 +1,19 @@
<?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/edit"
android:icon="@drawable/ic_rename"
android:title="@string/edit_contact"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/share"
android:icon="@drawable/ic_share"
android:title="@string/share"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/delete"
android:icon="@drawable/ic_delete"
android:title="@string/delete"
app:showAsAction="ifRoom"/>
</menu>

View File

@ -5,6 +5,7 @@
<string name="inserting">Einfügen…</string>
<string name="updating">Aktualisiere…</string>
<string name="phone_storage">Gerätespeicher</string>
<string name="phone_storage_hidden">Gerätespeicher (nicht sichtbar für andere Apps)</string>
<string name="new_contact">Neuer Kontakt</string>
<string name="edit_contact">Kontakt bearbeiten</string>
@ -20,13 +21,11 @@
<string name="remove_photo">Foto entfernen</string>
<!-- Settings -->
<string name="call_contact_on_click">Kontakt bei Klick anrufen</string>
<string name="start_name_with_surname">Namen mit Nachnamen beginnen</string>
<string name="show_phone_numbers">Zeige Telefonnummern im Hauptmenü</string>
<string name="show_contact_thumbnails">Zeige Vorschaubilder für Kontakte</string>
<string name="on_contact_click">Beim Klicken auf den Kontakt</string>
<string name="call_contact">Kontakt anrufen</string>
<string name="open_contact">Kontakt bearbeiten</string>
<string name="view_contact">Kontaktdetails ansehen</string>
<!-- Emails -->

View File

@ -0,0 +1,86 @@
<resources>
<string name="app_name">Simple Contacts</string>
<string name="app_launcher_name">Contacts</string>
<string name="address">Adresse</string>
<string name="inserting">Ajout…</string>
<string name="updating">Mise à jour…</string>
<string name="phone_storage">Stockage du téléphone</string>
<string name="phone_storage_hidden">Stockage du téléphone (non visible par d\'autres applis)</string>
<string name="new_contact">Nouveau contact</string>
<string name="edit_contact">Modifier contact</string>
<string name="select_contact">Sélectionner un contact</string>
<string name="select_contacts">Sélectionner des contacts</string>
<string name="first_name">Prénom</string>
<string name="middle_name">Nom</string>
<string name="surname">Surnom</string>
<!-- Photo -->
<string name="take_photo">Prendre une photo</string>
<string name="choose_photo">Choisir une photo</string>
<string name="remove_photo">Supprimer la photo</string>
<!-- Settings -->
<string name="start_name_with_surname">Commencer le nom par le surnom</string>
<string name="show_phone_numbers">Afficher les numéros de téléphone sur l\'écran principal</string>
<string name="show_contact_thumbnails">Afficher les vignettes des contacts</string>
<string name="on_contact_click">Sur appui du contact</string>
<string name="call_contact">Appeler le contact</string>
<string name="view_contact">Voir les détails du contact</string>
<!-- Emails -->
<string name="email">E-mail</string>
<string name="home">Maison</string>
<string name="work">Travail</string>
<string name="other">Autre</string>
<!-- Phone numbers -->
<string name="number">Numéro</string>
<string name="mobile">Mobile</string>
<string name="main_number">Principal</string>
<string name="work_fax">Fax travail</string>
<string name="home_fax">Fax maison</string>
<string name="pager">Bipeur</string>
<string name="no_phone_number_found">Aucun numéro de téléphone n\'a été trouvé</string>
<!-- Events -->
<string name="birthday">Naissance</string>
<string name="anniversary">Anniversaire</string>
<!-- Favorites -->
<string name="no_favorites">Il semble que vous n\'ayez pas encore ajouté de contact favori.</string>
<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>
<!-- Search -->
<string name="search_contacts">Rechercher des contacts</string>
<string name="search_favorites">Rechercher des favoris</string>
<!-- Export / Import -->
<string name="import_contacts">Importer des contacts</string>
<string name="export_contacts">Exporter des contacts</string>
<string name="import_contacts_from_vcf">Importer des contacts depuis un fichier .vcf</string>
<string name="export_contacts_to_vcf">Exporter des contacts vers un fichier .vcf</string>
<string name="target_contact_source">Source du contact cible</string>
<string name="include_contact_sources">Inclure les sources du contact</string>
<string name="filename_without_vcf">Nom du fichier (sans .vcf)</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Une appli de contacts pour gérer vos contacts sans pubs.</string>
<string name="app_long_description">
Une appli simple pour créer et gérer vos contacts depuis n\'importe quelle source. Les contacts peuvent être stockés sur votre appareil mais aussi synchronisés via Google ou d\'autres comptes. Vous pouvez afficher vos contacts favoris dans une liste séparée.
Vous pouvez l\'utiliser pour gérer les e-mail et événements de vos contacts. Elle permet de trier/filter via de multiples paramètres, et même afficher le surnom en premier.
Aucune publicité ni de permission inutile. Elle est entièrement open source et vous permet de personnaliser les couleurs.
Cette application fait parti d\'un groupe d\'applications. Vous pouvez trouver le reste des applis sur http://www.simplemobiletools.com
</string>
<!--
Haven't found some strings? There's more at
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
-->
</resources>

View File

@ -5,6 +5,7 @@
<string name="inserting">등록중…</string>
<string name="updating">수정중…</string>
<string name="phone_storage">Phone storage</string>
<string name="phone_storage_hidden">Phone storage (not visible by other apps)</string>
<string name="new_contact">새로운 연락처</string>
<string name="edit_contact">연락처 수정</string>
@ -20,13 +21,11 @@
<string name="remove_photo">사진 삭제</string>
<!-- Settings -->
<string name="call_contact_on_click">클릭으로 전화걸기</string>
<string name="start_name_with_surname">성을 먼저 표시</string>
<string name="show_phone_numbers">메인 스크린에 전화번호 표시</string>
<string name="show_contact_thumbnails">Show contact thumbnails</string>
<string name="on_contact_click">On contact click</string>
<string name="call_contact">Call contact</string>
<string name="open_contact">Open contact editor</string>
<string name="view_contact">View contact details</string>
<!-- Emails -->

View File

@ -5,6 +5,7 @@
<string name="inserting">Inserting…</string>
<string name="updating">Updating…</string>
<string name="phone_storage">Phone storage</string>
<string name="phone_storage_hidden">Phone storage (not visible by other apps)</string>
<string name="new_contact">Novo contacto</string>
<string name="edit_contact">Editar contacto</string>
@ -20,13 +21,11 @@
<string name="remove_photo">Remove photo</string>
<!-- Settings -->
<string name="call_contact_on_click">Call contact on click</string>
<string name="start_name_with_surname">Start name with surname</string>
<string name="show_phone_numbers">Show phone numbers on the main screen</string>
<string name="show_contact_thumbnails">Show contact thumbnails</string>
<string name="on_contact_click">On contact click</string>
<string name="call_contact">Call contact</string>
<string name="open_contact">Open contact editor</string>
<string name="view_contact">View contact details</string>
<!-- Emails -->

View File

@ -5,6 +5,7 @@
<string name="inserting">Добавление…</string>
<string name="updating">Обновление…</string>
<string name="phone_storage">Память устройства</string>
<string name="phone_storage_hidden">Память устройства (не видна другим приложениям)</string>
<string name="new_contact">Новый контакт</string>
<string name="edit_contact">Редактировать контакт</string>
@ -20,13 +21,11 @@
<string name="remove_photo">Удалить фото</string>
<!-- Settings -->
<string name="call_contact_on_click">Вызывать контакт при нажатии</string>
<string name="start_name_with_surname">Показывать сначала фамилию</string>
<string name="show_phone_numbers">Показывать номера телефонов на главном экране</string>
<string name="show_contact_thumbnails">Показывать фото контакта</string>
<string name="on_contact_click">При нажатии на контакт</string>
<string name="call_contact">Позвонить контакту</string>
<string name="open_contact">Открыть редактор контактов</string>
<string name="view_contact">Просмотреть подробности о контакте</string>
<!-- Emails -->

View File

@ -5,6 +5,7 @@
<string name="inserting">Vytvára sa…</string>
<string name="updating">Upravuje sa…</string>
<string name="phone_storage">Úložisko mobilu</string>
<string name="phone_storage_hidden">Úložisko mobilu (neviditeľné pre ostatné apky)</string>
<string name="new_contact">Nový kontakt</string>
<string name="edit_contact">Upraviť kontakt</string>
@ -20,13 +21,11 @@
<string name="remove_photo">Odstrániť foto</string>
<!-- Settings -->
<string name="call_contact_on_click">Zavolať kontakt po kliknutí</string>
<string name="start_name_with_surname">Začať meno priezviskom</string>
<string name="show_phone_numbers">Zobraziť telefónne čísla na hlavnej obrazovke</string>
<string name="show_contact_thumbnails">Zobraziť obrázky kontaktov</string>
<string name="on_contact_click">Po kliknutí na kontakt</string>
<string name="call_contact">Zavolať kontakt</string>
<string name="open_contact">Otvoriť editor kontaktov</string>
<string name="view_contact">Zobraziť údaje kontaktu</string>
<!-- Emails -->

View File

@ -5,6 +5,7 @@
<string name="inserting">Lägger till…</string>
<string name="updating">Uppdaterar…</string>
<string name="phone_storage">Phone storage</string>
<string name="phone_storage_hidden">Phone storage (not visible by other apps)</string>
<string name="new_contact">Ny kontakt</string>
<string name="edit_contact">Redigera kontakt</string>
@ -20,13 +21,11 @@
<string name="remove_photo">Ta bort foto</string>
<!-- Settings -->
<string name="call_contact_on_click">Ring kontakter när jag trycker på dem</string>
<string name="start_name_with_surname">Visa efternamn först</string>
<string name="show_phone_numbers">Visa telefonnummer i huvudvyn</string>
<string name="show_contact_thumbnails">Show contact thumbnails</string>
<string name="on_contact_click">On contact click</string>
<string name="call_contact">Call contact</string>
<string name="open_contact">Open contact editor</string>
<string name="view_contact">View contact details</string>
<!-- Emails -->

View File

@ -3,5 +3,5 @@
<dimen name="contact_photo_size">80dp</dimen>
<dimen name="contact_actions_size">45dp</dimen>
<dimen name="contact_icons_size">40dp</dimen>
<dimen name="contact_item_height">48dp</dimen>
<dimen name="contact_item_height">52dp</dimen>
</resources>

View File

@ -5,6 +5,7 @@
<string name="inserting">Inserting…</string>
<string name="updating">Updating…</string>
<string name="phone_storage">Phone storage</string>
<string name="phone_storage_hidden">Phone storage (not visible by other apps)</string>
<string name="new_contact">New contact</string>
<string name="edit_contact">Edit contact</string>
@ -20,13 +21,11 @@
<string name="remove_photo">Remove photo</string>
<!-- Settings -->
<string name="call_contact_on_click">Call contact on click</string>
<string name="start_name_with_surname">Start name with surname</string>
<string name="show_phone_numbers">Show phone numbers on the main screen</string>
<string name="show_contact_thumbnails">Show contact thumbnails</string>
<string name="on_contact_click">On contact click</string>
<string name="call_contact">Call contact</string>
<string name="open_contact">Open contact editor</string>
<string name="view_contact">View contact details</string>
<!-- Emails -->