Merge pull request #15 from SimpleMobileTools/master

upd
This commit is contained in:
solokot 2018-08-24 23:41:25 +03:00 committed by GitHub
commit e7a8fbc59e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
89 changed files with 2223 additions and 860 deletions

View File

@ -1,6 +1,57 @@
Changelog Changelog
========== ==========
Version 4.2.2 *(2018-08-13)*
----------------------------
* Added an optional Nickname field
* Improved searching and sorting UTF8 characters
* Fixed updating Notes and Organization fields
Version 4.2.1 *(2018-08-05)*
----------------------------
* Added some stability and light theme UX fixes
Version 4.2.0 *(2018-08-04)*
----------------------------
* Added a Recent Calls tab
* Allow customizing which tabs are visible
* Added an optional call confirmation dialog
* Fixed some glitches related to company contacts
* Some other performance and stability improvements
Version 4.1.0 *(2018-07-16)*
----------------------------
* Fixed a couple issues related to importing contacts from .vcf files
* Couple other UX and stability improvements
Version 4.0.5 *(2018-07-05)*
----------------------------
* Make duplicate contact filtering more agressive
* Couple UX and stability improvements
Version 4.0.4 *(2018-06-19)*
----------------------------
* Make "Try filtering out duplicate contacts" more agressive
* Ignore hidden contact fields, do not wipe them
* Prefer the contacts Mobile number at sending batch SMS
* Added a couple stability improvements
Version 4.0.3 *(2018-05-13)*
----------------------------
* Show a couple additional contact sources
Version 4.0.2 *(2018-05-12)*
----------------------------
* Make sure all relevant contact sources are visible
Version 4.0.1 *(2018-05-09)* Version 4.0.1 *(2018-05-09)*
---------------------------- ----------------------------

View File

@ -3,15 +3,15 @@ apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-android-extensions'
android { android {
compileSdkVersion 27 compileSdkVersion 28
buildToolsVersion "27.0.3" buildToolsVersion "28.0.2"
defaultConfig { defaultConfig {
applicationId "com.simplemobiletools.contacts" applicationId "com.simplemobiletools.contacts"
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 27 targetSdkVersion 28
versionCode 21 versionCode 29
versionName "4.0.1" versionName "4.2.2"
setProperty("archivesBaseName", "contacts") setProperty("archivesBaseName", "contacts")
} }
@ -45,9 +45,11 @@ ext {
} }
dependencies { dependencies {
implementation 'com.simplemobiletools:commons:4.0.0' implementation 'com.simplemobiletools:commons:4.6.15'
implementation 'joda-time:joda-time:2.9.9' implementation 'joda-time:joda-time:2.9.9'
implementation 'com.facebook.stetho:stetho:1.5.0' implementation 'com.facebook.stetho:stetho:1.5.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.2'
compile 'com.googlecode.ez-vcard:ez-vcard:0.10.4'
debugImplementation "com.squareup.leakcanary:leakcanary-android:$leakCanaryVersion" debugImplementation "com.squareup.leakcanary:leakcanary-android:$leakCanaryVersion"
releaseImplementation "com.squareup.leakcanary:leakcanary-android-no-op:$leakCanaryVersion" releaseImplementation "com.squareup.leakcanary:leakcanary-android-no-op:$leakCanaryVersion"

View File

@ -0,0 +1,5 @@
# ez-vcard
-keep,includedescriptorclasses class ezvcard.property.** { *; }
-keep enum ezvcard.VCardVersion { *; }
-dontwarn ezvcard.io.json.**
-dontwarn freemarker.**

View File

@ -11,6 +11,8 @@
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.GET_ACCOUNTS"/> <uses-permission android:name="android.permission.GET_ACCOUNTS"/>
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS"/> <uses-permission android:name="android.permission.READ_SYNC_SETTINGS"/>
<uses-permission android:name="android.permission.READ_CALL_LOG"/>
<uses-permission android:name="android.permission.WRITE_CALL_LOG"/>
<uses-permission <uses-permission
android:name="android.permission.USE_FINGERPRINT" android:name="android.permission.USE_FINGERPRINT"
@ -26,11 +28,7 @@
<activity <activity
android:name=".activities.SplashActivity" android:name=".activities.SplashActivity"
android:theme="@style/SplashTheme"> android:theme="@style/SplashTheme"/>
<intent-filter>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity <activity
android:name=".activities.MainActivity"> android:name=".activities.MainActivity">
@ -191,19 +189,6 @@
android:resource="@xml/provider_paths"/> android:resource="@xml/provider_paths"/>
</provider> </provider>
<!-- Do not append ".Orange" to the default alias "name", it would remove the old homescreen launcher of users at upgrade -->
<activity-alias
android:name=".activities.SplashActivity"
android:enabled="true"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher"
android:targetActivity=".activities.SplashActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity-alias>
<activity-alias <activity-alias
android:name=".activities.SplashActivity.Red" android:name=".activities.SplashActivity.Red"
android:enabled="false" android:enabled="false"
@ -386,6 +371,18 @@
</intent-filter> </intent-filter>
</activity-alias> </activity-alias>
<activity-alias
android:name=".activities.SplashActivity.Orange"
android:enabled="true"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher"
android:targetActivity=".activities.SplashActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity-alias>
<activity-alias <activity-alias
android:name=".activities.SplashActivity.Deep_orange" android:name=".activities.SplashActivity.Deep_orange"
android:enabled="false" android:enabled="false"

View File

@ -4,7 +4,6 @@ import android.graphics.Bitmap
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.provider.ContactsContract import android.provider.ContactsContract
import android.widget.ImageView import android.widget.ImageView
import android.widget.TextView
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.bumptech.glide.load.DataSource import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.engine.DiskCacheStrategy import com.bumptech.glide.load.engine.DiskCacheStrategy
@ -17,7 +16,6 @@ import com.simplemobiletools.commons.dialogs.ConfirmationDialog
import com.simplemobiletools.commons.dialogs.RadioGroupDialog import com.simplemobiletools.commons.dialogs.RadioGroupDialog
import com.simplemobiletools.commons.extensions.getColoredBitmap import com.simplemobiletools.commons.extensions.getColoredBitmap
import com.simplemobiletools.commons.extensions.getContrastColor import com.simplemobiletools.commons.extensions.getContrastColor
import com.simplemobiletools.commons.helpers.getDateFormats
import com.simplemobiletools.commons.models.RadioItem import com.simplemobiletools.commons.models.RadioItem
import com.simplemobiletools.contacts.R import com.simplemobiletools.contacts.R
import com.simplemobiletools.contacts.extensions.config import com.simplemobiletools.contacts.extensions.config
@ -26,16 +24,11 @@ import com.simplemobiletools.contacts.extensions.sendSMSIntent
import com.simplemobiletools.contacts.extensions.shareContacts import com.simplemobiletools.contacts.extensions.shareContacts
import com.simplemobiletools.contacts.helpers.ContactsHelper import com.simplemobiletools.contacts.helpers.ContactsHelper
import com.simplemobiletools.contacts.models.Contact 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.* import java.util.*
abstract class ContactActivity : SimpleActivity() { abstract class ContactActivity : SimpleActivity() {
protected var contact: Contact? = null protected var contact: Contact? = null
protected var currentContactPhotoPath = "" protected var currentContactPhotoPath = ""
protected var showFields = 0
fun showPhotoPlaceholder(photoView: ImageView) { fun showPhotoPlaceholder(photoView: ImageView) {
val placeholder = resources.getColoredBitmap(R.drawable.ic_person, config.primaryColor.getContrastColor()) val placeholder = resources.getColoredBitmap(R.drawable.ic_person, config.primaryColor.getContrastColor())
@ -69,31 +62,6 @@ abstract class ContactActivity : SimpleActivity() {
}).into(photoView) }).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() { fun deleteContact() {
ConfirmationDialog(this) { ConfirmationDialog(this) {
if (contact != null) { if (contact != null) {
@ -104,9 +72,7 @@ abstract class ContactActivity : SimpleActivity() {
} }
fun shareContact() { fun shareContact() {
if (contact != null) { shareContacts(arrayListOf(contact!!))
shareContacts(arrayListOf(contact!!))
}
} }
fun trySendSMS() { fun trySendSMS() {

View File

@ -45,9 +45,6 @@ class EditContactActivity : ContactActivity() {
private val CHOOSE_PHOTO = 2 private val CHOOSE_PHOTO = 2
private val REMOVE_PHOTO = 3 private val REMOVE_PHOTO = 3
private val KEY_PHONE = "phone"
private val KEY_NAME = "name"
private var wasActivityInitialized = false private var wasActivityInitialized = false
private var lastPhotoIntentUri: Uri? = null private var lastPhotoIntentUri: Uri? = null
private var isSaving = false private var isSaving = false
@ -58,11 +55,11 @@ class EditContactActivity : ContactActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_edit_contact) setContentView(R.layout.activity_edit_contact)
supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_cross) supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_cross)
showFields = config.showContactFields
val action = intent.action val action = intent.action
isThirdPartyIntent = action == Intent.ACTION_EDIT || action == Intent.ACTION_INSERT_OR_EDIT || action == Intent.ACTION_INSERT isThirdPartyIntent = action == Intent.ACTION_EDIT || action == Intent.ACTION_INSERT_OR_EDIT || action == Intent.ACTION_INSERT
if (isThirdPartyIntent) { val isFromSimpleContacts = intent.getBooleanExtra(IS_FROM_SIMPLE_CONTACTS, false)
if (isThirdPartyIntent && !isFromSimpleContacts) {
handlePermission(PERMISSION_READ_CONTACTS) { handlePermission(PERMISSION_READ_CONTACTS) {
if (it) { if (it) {
handlePermission(PERMISSION_WRITE_CONTACTS) { handlePermission(PERMISSION_WRITE_CONTACTS) {
@ -94,6 +91,10 @@ class EditContactActivity : ContactActivity() {
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (contact == null) {
return true
}
when (item.itemId) { when (item.itemId) {
R.id.save -> saveContact() R.id.save -> saveContact()
R.id.share -> shareContact() R.id.share -> shareContact()
@ -232,7 +233,11 @@ class EditContactActivity : ContactActivity() {
Intent().apply { Intent().apply {
action = Intent.ACTION_EDIT action = Intent.ACTION_EDIT
data = getContactPublicUri(contact!!) data = getContactPublicUri(contact!!)
startActivity(this) if (resolveActivity(packageManager) != null) {
startActivity(this)
} else {
toast(R.string.no_app_found)
}
} }
} }
@ -264,6 +269,7 @@ class EditContactActivity : ContactActivity() {
} }
private fun setupFieldVisibility() { private fun setupFieldVisibility() {
val showFields = config.showContactFields
if (showFields and (SHOW_PREFIX_FIELD or SHOW_FIRST_NAME_FIELD or SHOW_MIDDLE_NAME_FIELD or SHOW_SURNAME_FIELD or SHOW_SUFFIX_FIELD) == 0) { if (showFields and (SHOW_PREFIX_FIELD or SHOW_FIRST_NAME_FIELD or SHOW_MIDDLE_NAME_FIELD or SHOW_SURNAME_FIELD or SHOW_SUFFIX_FIELD) == 0) {
contact_name_image.beInvisible() contact_name_image.beInvisible()
} }
@ -273,6 +279,7 @@ class EditContactActivity : ContactActivity() {
contact_middle_name.beVisibleIf(showFields and SHOW_MIDDLE_NAME_FIELD != 0) contact_middle_name.beVisibleIf(showFields and SHOW_MIDDLE_NAME_FIELD != 0)
contact_surname.beVisibleIf(showFields and SHOW_SURNAME_FIELD != 0) contact_surname.beVisibleIf(showFields and SHOW_SURNAME_FIELD != 0)
contact_suffix.beVisibleIf(showFields and SHOW_SUFFIX_FIELD != 0) contact_suffix.beVisibleIf(showFields and SHOW_SUFFIX_FIELD != 0)
contact_nickname.beVisibleIf(showFields and SHOW_NICKNAME_FIELD != 0)
contact_source.beVisibleIf(showFields and SHOW_CONTACT_SOURCE_FIELD != 0) contact_source.beVisibleIf(showFields and SHOW_CONTACT_SOURCE_FIELD != 0)
contact_source_image.beVisibleIf(showFields and SHOW_CONTACT_SOURCE_FIELD != 0) contact_source_image.beVisibleIf(showFields and SHOW_CONTACT_SOURCE_FIELD != 0)
@ -340,112 +347,99 @@ class EditContactActivity : ContactActivity() {
contact_middle_name.setText(middleName) contact_middle_name.setText(middleName)
contact_surname.setText(surname) contact_surname.setText(surname)
contact_suffix.setText(suffix) contact_suffix.setText(suffix)
contact_nickname.setText(nickname)
} }
} }
private fun setupPhoneNumbers() { private fun setupPhoneNumbers() {
if (showFields and SHOW_PHONE_NUMBERS_FIELD != 0) { contact!!.phoneNumbers.forEachIndexed { index, number ->
contact!!.phoneNumbers.forEachIndexed { index, number -> var numberHolder = contact_numbers_holder.getChildAt(index)
var numberHolder = contact_numbers_holder.getChildAt(index) if (numberHolder == null) {
if (numberHolder == null) { numberHolder = layoutInflater.inflate(R.layout.item_edit_phone_number, contact_numbers_holder, false)
numberHolder = layoutInflater.inflate(R.layout.item_edit_phone_number, contact_numbers_holder, false) contact_numbers_holder.addView(numberHolder)
contact_numbers_holder.addView(numberHolder) }
}
numberHolder!!.apply { numberHolder!!.apply {
contact_number.setText(number.value) contact_number.setText(number.value)
setupPhoneNumberTypePicker(contact_number_type, number.type) setupPhoneNumberTypePicker(contact_number_type, number.type)
}
} }
} }
} }
private fun setupEmails() { private fun setupEmails() {
if (showFields and SHOW_EMAILS_FIELD != 0) { contact!!.emails.forEachIndexed { index, email ->
contact!!.emails.forEachIndexed { index, email -> var emailHolder = contact_emails_holder.getChildAt(index)
var emailHolder = contact_emails_holder.getChildAt(index) if (emailHolder == null) {
if (emailHolder == null) { emailHolder = layoutInflater.inflate(R.layout.item_edit_email, contact_emails_holder, false)
emailHolder = layoutInflater.inflate(R.layout.item_edit_email, contact_emails_holder, false) contact_emails_holder.addView(emailHolder)
contact_emails_holder.addView(emailHolder) }
}
emailHolder!!.apply { emailHolder!!.apply {
contact_email.setText(email.value) contact_email.setText(email.value)
setupEmailTypePicker(contact_email_type, email.type) setupEmailTypePicker(contact_email_type, email.type)
}
} }
} }
} }
private fun setupAddresses() { private fun setupAddresses() {
if (showFields and SHOW_ADDRESSES_FIELD != 0) { contact!!.addresses.forEachIndexed { index, address ->
contact!!.addresses.forEachIndexed { index, address -> var addressHolder = contact_addresses_holder.getChildAt(index)
var addressHolder = contact_addresses_holder.getChildAt(index) if (addressHolder == null) {
if (addressHolder == null) { addressHolder = layoutInflater.inflate(R.layout.item_edit_address, contact_addresses_holder, false)
addressHolder = layoutInflater.inflate(R.layout.item_edit_address, contact_addresses_holder, false) contact_addresses_holder.addView(addressHolder)
contact_addresses_holder.addView(addressHolder) }
}
addressHolder!!.apply { addressHolder!!.apply {
contact_address.setText(address.value) contact_address.setText(address.value)
setupAddressTypePicker(contact_address_type, address.type) setupAddressTypePicker(contact_address_type, address.type)
}
} }
} }
} }
private fun setupNotes() { private fun setupNotes() {
if (showFields and SHOW_NOTES_FIELD != 0) { contact_notes.setText(contact!!.notes)
contact_notes.setText(contact!!.notes)
}
} }
private fun setupOrganization() { private fun setupOrganization() {
if (showFields and SHOW_ORGANIZATION_FIELD != 0) { contact_organization_company.setText(contact!!.organization.company)
contact_organization_company.setText(contact!!.organization.company) contact_organization_job_position.setText(contact!!.organization.jobPosition)
contact_organization_job_position.setText(contact!!.organization.jobPosition)
}
} }
private fun setupWebsites() { private fun setupWebsites() {
if (showFields and SHOW_WEBSITES_FIELD != 0) { contact!!.websites.forEachIndexed { index, website ->
contact!!.websites.forEachIndexed { index, website -> var websitesHolder = contact_websites_holder.getChildAt(index)
var websitesHolder = contact_websites_holder.getChildAt(index) if (websitesHolder == null) {
if (websitesHolder == null) { websitesHolder = layoutInflater.inflate(R.layout.item_edit_website, contact_websites_holder, false)
websitesHolder = layoutInflater.inflate(R.layout.item_edit_website, contact_websites_holder, false) contact_websites_holder.addView(websitesHolder)
contact_websites_holder.addView(websitesHolder)
}
websitesHolder!!.contact_website.setText(website)
} }
websitesHolder!!.contact_website.setText(website)
} }
} }
private fun setupEvents() { private fun setupEvents() {
if (showFields and SHOW_EVENTS_FIELD != 0) { contact!!.events.forEachIndexed { index, event ->
contact!!.events.forEachIndexed { index, event -> var eventHolder = contact_events_holder.getChildAt(index)
var eventHolder = contact_events_holder.getChildAt(index) if (eventHolder == null) {
if (eventHolder == null) { eventHolder = layoutInflater.inflate(R.layout.item_event, contact_events_holder, false)
eventHolder = layoutInflater.inflate(R.layout.item_event, contact_events_holder, false) contact_events_holder.addView(eventHolder)
contact_events_holder.addView(eventHolder) }
(eventHolder as ViewGroup).apply {
val contactEvent = contact_event.apply {
event.value.getDateTimeFromDateString(this)
tag = event.value
alpha = 1f
} }
(eventHolder as ViewGroup).apply { setupEventTypePicker(this, event.type)
val contactEvent = contact_event.apply {
getDateTime(event.value, this)
tag = event.value
alpha = 1f
}
setupEventTypePicker(this, event.type) contact_event_remove.apply {
beVisible()
contact_event_remove.apply { applyColorFilter(getAdjustedPrimaryColor())
beVisible() background.applyColorFilter(config.textColor)
applyColorFilter(getAdjustedPrimaryColor()) setOnClickListener {
background.applyColorFilter(config.textColor) resetContactEvent(contactEvent, this)
setOnClickListener {
resetContactEvent(contactEvent, this)
}
} }
} }
} }
@ -453,52 +447,50 @@ class EditContactActivity : ContactActivity() {
} }
private fun setupGroups() { private fun setupGroups() {
if (showFields and SHOW_GROUPS_FIELD != 0) { contact_groups_holder.removeAllViews()
contact_groups_holder.removeAllViews() val groups = contact!!.groups
val groups = contact!!.groups groups.forEachIndexed { index, group ->
groups.forEachIndexed { index, group -> var groupHolder = contact_groups_holder.getChildAt(index)
var groupHolder = contact_groups_holder.getChildAt(index) if (groupHolder == null) {
if (groupHolder == null) { groupHolder = layoutInflater.inflate(R.layout.item_edit_group, contact_groups_holder, false)
groupHolder = layoutInflater.inflate(R.layout.item_edit_group, contact_groups_holder, false) contact_groups_holder.addView(groupHolder)
contact_groups_holder.addView(groupHolder) }
(groupHolder as ViewGroup).apply {
contact_group.apply {
text = group.title
setTextColor(config.textColor)
tag = group.id
alpha = 1f
} }
(groupHolder as ViewGroup).apply { setOnClickListener {
contact_group.apply { showSelectGroupsDialog()
text = group.title }
setTextColor(config.textColor)
tag = group.id
alpha = 1f
}
contact_group_remove.apply {
beVisible()
applyColorFilter(getAdjustedPrimaryColor())
background.applyColorFilter(config.textColor)
setOnClickListener { setOnClickListener {
showSelectGroupsDialog() removeGroup(group.id)
}
contact_group_remove.apply {
beVisible()
applyColorFilter(getAdjustedPrimaryColor())
background.applyColorFilter(config.textColor)
setOnClickListener {
removeGroup(group.id)
}
} }
} }
} }
}
if (groups.isEmpty()) { if (groups.isEmpty()) {
layoutInflater.inflate(R.layout.item_edit_group, contact_groups_holder, false).apply { layoutInflater.inflate(R.layout.item_edit_group, contact_groups_holder, false).apply {
contact_group.apply { contact_group.apply {
alpha = 0.5f alpha = 0.5f
text = getString(R.string.no_groups) text = getString(R.string.no_groups)
setTextColor(config.textColor) setTextColor(config.textColor)
} }
contact_groups_holder.addView(this) contact_groups_holder.addView(this)
contact_group_remove.beGone() contact_group_remove.beGone()
setOnClickListener { setOnClickListener {
showSelectGroupsDialog() showSelectGroupsDialog()
}
} }
} }
} }
@ -513,8 +505,8 @@ class EditContactActivity : ContactActivity() {
supportActionBar?.title = resources.getString(R.string.new_contact) supportActionBar?.title = resources.getString(R.string.new_contact)
originalContactSource = if (hasContactPermissions()) config.lastUsedContactSource else SMT_PRIVATE originalContactSource = if (hasContactPermissions()) config.lastUsedContactSource else SMT_PRIVATE
val organization = Organization("", "") val organization = Organization("", "")
contact = Contact(0, "", "", "", "", "", "", ArrayList(), ArrayList(), ArrayList(), ArrayList(), originalContactSource, 0, 0, "", null, "", contact = Contact(0, "", "", "", "", "", "", "", ArrayList(), ArrayList(), ArrayList(), ArrayList(), originalContactSource, 0, 0, "",
ArrayList(), organization, ArrayList()) null, "", ArrayList(), organization, ArrayList())
contact_source.text = getPublicContactSource(contact!!.source) contact_source.text = getPublicContactSource(contact!!.source)
} }
@ -603,7 +595,7 @@ class EditContactActivity : ContactActivity() {
} }
} }
val date = getDateTime(eventField.tag?.toString() ?: "") val date = (eventField.tag?.toString() ?: "").getDateTimeFromDateString()
DatePickerDialog(this, getDialogTheme(), setDateListener, date.year, date.monthOfYear - 1, date.dayOfMonth).show() DatePickerDialog(this, getDialogTheme(), setDateListener, date.year, date.monthOfYear - 1, date.dayOfMonth).show()
} }
@ -713,7 +705,7 @@ class EditContactActivity : ContactActivity() {
} }
private fun saveContact() { private fun saveContact() {
if (isSaving || contact == null) { if (isSaving) {
return return
} }
@ -725,6 +717,7 @@ class EditContactActivity : ContactActivity() {
middleName = contact_middle_name.value middleName = contact_middle_name.value
surname = contact_surname.value surname = contact_surname.value
suffix = contact_suffix.value suffix = contact_suffix.value
nickname = contact_nickname.value
photoUri = currentContactPhotoPath photoUri = currentContactPhotoPath
phoneNumbers = getFilledPhoneNumbers() phoneNumbers = getFilledPhoneNumbers()
emails = getFilledEmails() emails = getFilledEmails()

View File

@ -3,12 +3,17 @@ package com.simplemobiletools.contacts.activities
import android.os.Bundle import android.os.Bundle
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.extensions.beVisibleIf
import com.simplemobiletools.commons.extensions.getAdjustedPrimaryColor
import com.simplemobiletools.commons.extensions.underlineText
import com.simplemobiletools.commons.extensions.updateTextColors
import com.simplemobiletools.contacts.R import com.simplemobiletools.contacts.R
import com.simplemobiletools.contacts.adapters.ContactsAdapter import com.simplemobiletools.contacts.adapters.ContactsAdapter
import com.simplemobiletools.contacts.dialogs.SelectContactsDialog import com.simplemobiletools.contacts.dialogs.SelectContactsDialog
import com.simplemobiletools.contacts.extensions.* import com.simplemobiletools.contacts.extensions.*
import com.simplemobiletools.contacts.helpers.* import com.simplemobiletools.contacts.helpers.ContactsHelper
import com.simplemobiletools.contacts.helpers.GROUP
import com.simplemobiletools.contacts.helpers.LOCATION_GROUP_CONTACTS
import com.simplemobiletools.contacts.interfaces.RefreshContactsListener import com.simplemobiletools.contacts.interfaces.RefreshContactsListener
import com.simplemobiletools.contacts.interfaces.RemoveFromGroupListener import com.simplemobiletools.contacts.interfaces.RemoveFromGroupListener
import com.simplemobiletools.contacts.models.Contact import com.simplemobiletools.contacts.models.Contact
@ -102,24 +107,13 @@ class GroupContactsActivity : SimpleActivity(), RemoveFromGroupListener, Refresh
val currAdapter = group_contacts_list.adapter val currAdapter = group_contacts_list.adapter
if (currAdapter == null) { if (currAdapter == null) {
ContactsAdapter(this, contacts, this, LOCATION_GROUP_CONTACTS, this, group_contacts_list, group_contacts_fastscroller) { ContactsAdapter(this, contacts, this, LOCATION_GROUP_CONTACTS, this, group_contacts_list, group_contacts_fastscroller) {
when (config.onContactClick) { contactClicked(it as Contact)
ON_CLICK_CALL_CONTACT -> {
val contact = it as Contact
if (contact.phoneNumbers.isNotEmpty()) {
tryStartCall(it)
} else {
toast(R.string.no_phone_number_found)
}
}
ON_CLICK_VIEW_CONTACT -> viewContact(it as Contact)
ON_CLICK_EDIT_CONTACT -> editContact(it as Contact)
}
}.apply { }.apply {
addVerticalDividers(true) addVerticalDividers(true)
group_contacts_list.adapter = this group_contacts_list.adapter = this
} }
group_contacts_fastscroller.setScrollTo(0) group_contacts_fastscroller.setScrollToY(0)
group_contacts_fastscroller.setViews(group_contacts_list) { group_contacts_fastscroller.setViews(group_contacts_list) {
val item = (group_contacts_list.adapter as ContactsAdapter).contactItems.getOrNull(it) val item = (group_contacts_list.adapter as ContactsAdapter).contactItems.getOrNull(it)
group_contacts_fastscroller.updateBubbleText(item?.getBubbleText() ?: "") group_contacts_fastscroller.updateBubbleText(item?.getBubbleText() ?: "")

View File

@ -1,15 +1,13 @@
package com.simplemobiletools.contacts.activities package com.simplemobiletools.contacts.activities
import android.Manifest
import android.app.SearchManager import android.app.SearchManager
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.drawable.ColorDrawable import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.support.v4.app.ActivityCompat import android.os.Handler
import android.support.v4.content.ContextCompat
import android.support.v4.view.MenuItemCompat import android.support.v4.view.MenuItemCompat
import android.support.v4.view.ViewPager import android.support.v4.view.ViewPager
import android.support.v7.widget.SearchView import android.support.v7.widget.SearchView
@ -30,6 +28,7 @@ import com.simplemobiletools.contacts.dialogs.ImportContactsDialog
import com.simplemobiletools.contacts.extensions.config import com.simplemobiletools.contacts.extensions.config
import com.simplemobiletools.contacts.extensions.dbHelper import com.simplemobiletools.contacts.extensions.dbHelper
import com.simplemobiletools.contacts.extensions.getTempFile import com.simplemobiletools.contacts.extensions.getTempFile
import com.simplemobiletools.contacts.fragments.MyViewPagerFragment
import com.simplemobiletools.contacts.helpers.* import com.simplemobiletools.contacts.helpers.*
import com.simplemobiletools.contacts.interfaces.RefreshContactsListener import com.simplemobiletools.contacts.interfaces.RefreshContactsListener
import com.simplemobiletools.contacts.models.Contact import com.simplemobiletools.contacts.models.Contact
@ -37,6 +36,7 @@ import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.fragment_contacts.* import kotlinx.android.synthetic.main.fragment_contacts.*
import kotlinx.android.synthetic.main.fragment_favorites.* import kotlinx.android.synthetic.main.fragment_favorites.*
import kotlinx.android.synthetic.main.fragment_groups.* import kotlinx.android.synthetic.main.fragment_groups.*
import kotlinx.android.synthetic.main.fragment_recents.*
import java.io.FileOutputStream import java.io.FileOutputStream
class MainActivity : SimpleActivity(), RefreshContactsListener { class MainActivity : SimpleActivity(), RefreshContactsListener {
@ -45,6 +45,7 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
private var werePermissionsHandled = false private var werePermissionsHandled = false
private var isFirstResume = true private var isFirstResume = true
private var isGettingContacts = false private var isGettingContacts = false
private var handledShowTabs = 0
private var storedTextColor = 0 private var storedTextColor = 0
private var storedBackgroundColor = 0 private var storedBackgroundColor = 0
@ -53,6 +54,7 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
private var storedShowPhoneNumbers = false private var storedShowPhoneNumbers = false
private var storedStartNameWithSurname = false private var storedStartNameWithSurname = false
private var storedFilterDuplicates = true private var storedFilterDuplicates = true
private var storedShowTabs = 0
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -63,26 +65,36 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
// just get a reference to the database to make sure it is created properly // just get a reference to the database to make sure it is created properly
dbHelper dbHelper
handlePermission(PERMISSION_READ_CALL_LOG) {
if (it) {
handlePermission(PERMISSION_WRITE_CALL_LOG) {
checkContactPermissions()
}
} else {
checkContactPermissions()
}
}
storeStateVariables()
checkWhatsNewDialog()
}
private fun checkContactPermissions() {
handlePermission(PERMISSION_READ_CONTACTS) { handlePermission(PERMISSION_READ_CONTACTS) {
werePermissionsHandled = true werePermissionsHandled = true
if (it) { if (it) {
handlePermission(PERMISSION_WRITE_CONTACTS) { handlePermission(PERMISSION_WRITE_CONTACTS) {
// workaround for upgrading from version 3.x to 4.x as we added a new permission from an already granted permissions group // workaround for upgrading from version 3.x to 4.x as we added a new permission from an already granted permissions group
val hasGetAccountsPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.GET_ACCOUNTS) == PackageManager.PERMISSION_GRANTED handlePermission(PERMISSION_GET_ACCOUNTS) {
if (!hasGetAccountsPermission) { storeLocalAccountData()
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.GET_ACCOUNTS), 34) initFragments()
} }
storeLocalAccountData()
initFragments()
} }
} else { } else {
storeLocalAccountData() storeLocalAccountData()
initFragments() initFragments()
} }
} }
storeStateVariables()
checkWhatsNewDialog()
} }
override fun onResume() { override fun onResume() {
@ -92,6 +104,12 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
return return
} }
if (storedShowTabs != config.showTabs) {
config.lastUsedViewPagerPage = 0
System.exit(0)
return
}
val configShowContactThumbnails = config.showContactThumbnails val configShowContactThumbnails = config.showContactThumbnails
if (storedShowContactThumbnails != configShowContactThumbnails) { if (storedShowContactThumbnails != configShowContactThumbnails) {
getAllFragments().forEach { getAllFragments().forEach {
@ -138,10 +156,10 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
initFragments() initFragments()
} else { } else {
refreshContacts(ALL_TABS_MASK) refreshContacts(ALL_TABS_MASK)
}
getAllFragments().forEach { getAllFragments().forEach {
it?.onActivityResume() it?.onActivityResume()
}
} }
} }
isFirstResume = false isFirstResume = false
@ -159,11 +177,12 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
override fun onCreateOptionsMenu(menu: Menu): Boolean { override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu, menu) menuInflater.inflate(R.menu.menu, menu)
val currentPage = viewpager?.currentItem val currentFragment = getCurrentFragment()
menu.apply { menu.apply {
findItem(R.id.search).isVisible = currentPage != LOCATION_GROUPS_TAB findItem(R.id.search).isVisible = currentFragment != groups_fragment && currentFragment != recents_fragment
findItem(R.id.sort).isVisible = currentPage != LOCATION_GROUPS_TAB findItem(R.id.sort).isVisible = currentFragment != groups_fragment && currentFragment != recents_fragment
findItem(R.id.filter).isVisible = currentPage != LOCATION_GROUPS_TAB findItem(R.id.filter).isVisible = currentFragment != groups_fragment
} }
setupSearch(menu) setupSearch(menu)
return true return true
@ -191,6 +210,7 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
storedShowPhoneNumbers = showPhoneNumbers storedShowPhoneNumbers = showPhoneNumbers
storedStartNameWithSurname = startNameWithSurname storedStartNameWithSurname = startNameWithSurname
storedFilterDuplicates = filterDuplicates storedFilterDuplicates = filterDuplicates
storedShowTabs = showTabs
} }
} }
@ -200,7 +220,7 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
(searchMenuItem!!.actionView as SearchView).apply { (searchMenuItem!!.actionView as SearchView).apply {
setSearchableInfo(searchManager.getSearchableInfo(componentName)) setSearchableInfo(searchManager.getSearchableInfo(componentName))
isSubmitButtonEnabled = false isSubmitButtonEnabled = false
queryHint = getString(if (viewpager.currentItem == 0) R.string.search_contacts else R.string.search_favorites) queryHint = getString(if (getCurrentFragment() == contacts_fragment) R.string.search_contacts else R.string.search_favorites)
setOnQueryTextListener(object : SearchView.OnQueryTextListener { setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String) = false override fun onQueryTextSubmit(query: String) = false
@ -228,13 +248,30 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
}) })
} }
private fun getCurrentFragment() = when (viewpager.currentItem) { private fun getCurrentFragment(): MyViewPagerFragment? {
0 -> contacts_fragment val showTabs = config.showTabs
1 -> favorites_fragment val fragments = arrayListOf<MyViewPagerFragment>()
else -> groups_fragment if (showTabs and CONTACTS_TAB_MASK != 0) {
fragments.add(contacts_fragment)
}
if (showTabs and FAVORITES_TAB_MASK != 0) {
fragments.add(favorites_fragment)
}
if (showTabs and RECENTS_TAB_MASK != 0) {
fragments.add(recents_fragment)
}
if (showTabs and GROUPS_TAB_MASK != 0) {
fragments.add(groups_fragment)
}
return fragments[viewpager.currentItem]
} }
private fun setupTabColors() { private fun setupTabColors() {
handledShowTabs = config.showTabs
val lastUsedPage = config.lastUsedViewPagerPage val lastUsedPage = config.lastUsedViewPagerPage
main_tabs_holder.apply { main_tabs_holder.apply {
background = ColorDrawable(config.backgroundColor) background = ColorDrawable(config.backgroundColor)
@ -250,10 +287,10 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
private fun storeLocalAccountData() { private fun storeLocalAccountData() {
if (config.localAccountType == "-1") { if (config.localAccountType == "-1") {
ContactsHelper(this).getContactSources { ContactsHelper(this).getContactSources { sources ->
var localAccountType = "" var localAccountType = ""
var localAccountName = "" var localAccountName = ""
it.forEach { sources.forEach {
if (localAccountTypes.contains(it.type)) { if (localAccountTypes.contains(it.type)) {
localAccountType = it.type localAccountType = it.type
localAccountName = it.name localAccountName = it.name
@ -266,11 +303,10 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
} }
} }
private fun getInactiveTabIndexes(activeIndex: Int) = arrayListOf(0, 1, 2).filter { it != activeIndex } private fun getInactiveTabIndexes(activeIndex: Int) = arrayListOf(0, 1, 2, 3).filter { it != activeIndex }
private fun initFragments() { private fun initFragments() {
refreshContacts(ALL_TABS_MASK) viewpager.offscreenPageLimit = 3
viewpager.offscreenPageLimit = 2
viewpager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener { viewpager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
override fun onPageScrollStateChanged(state: Int) { override fun onPageScrollStateChanged(state: Int) {
if (isSearchOpen) { if (isSearchOpen) {
@ -291,11 +327,19 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
} }
}) })
viewpager.onGlobalLayout {
refreshContacts(ALL_TABS_MASK)
}
main_tabs_holder.onTabSelectionChanged( main_tabs_holder.onTabSelectionChanged(
tabUnselectedAction = { tabUnselectedAction = {
it.icon?.applyColorFilter(config.textColor) it.icon?.applyColorFilter(config.textColor)
}, },
tabSelectedAction = { tabSelectedAction = {
if (isSearchOpen) {
getCurrentFragment()?.onSearchQueryChanged("")
searchMenuItem?.collapseActionView()
}
viewpager.currentItem = it.position viewpager.currentItem = it.position
it.icon?.applyColorFilter(getAdjustedPrimaryColor()) it.icon?.applyColorFilter(getAdjustedPrimaryColor())
} }
@ -305,6 +349,37 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
tryImportContactsFromFile(intent.data) tryImportContactsFromFile(intent.data)
intent.data = null intent.data = null
} }
main_tabs_holder.removeAllTabs()
var skippedTabs = 0
tabsList.forEachIndexed { index, value ->
if (config.showTabs and value == 0) {
skippedTabs++
} else {
main_tabs_holder.addTab(main_tabs_holder.newTab().setIcon(getTabIcon(index)), index - skippedTabs, config.lastUsedViewPagerPage == index - skippedTabs)
}
}
// selecting the proper tab sometimes glitches, add an extra selector to make sure we have it right
main_tabs_holder.onGlobalLayout {
Handler().postDelayed({
main_tabs_holder.getTabAt(config.lastUsedViewPagerPage)?.select()
invalidateOptionsMenu()
}, 100L)
}
main_tabs_holder.beVisibleIf(skippedTabs < 3)
}
private fun getTabIcon(position: Int): Drawable {
val drawableId = when (position) {
LOCATION_CONTACTS_TAB -> R.drawable.ic_person
LOCATION_FAVORITES_TAB -> R.drawable.ic_star_on
LOCATION_RECENTS_TAB -> R.drawable.ic_clock
else -> R.drawable.ic_group
}
return resources.getColoredDrawableWithColor(drawableId, config.textColor)
} }
private fun showSortingDialog() { private fun showSortingDialog() {
@ -354,10 +429,14 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
return return
} }
val inputStream = contentResolver.openInputStream(uri) try {
val out = FileOutputStream(tempFile) val inputStream = contentResolver.openInputStream(uri)
inputStream.copyTo(out) val out = FileOutputStream(tempFile)
showImportContactsDialog(tempFile.absolutePath) inputStream.copyTo(out)
showImportContactsDialog(tempFile.absolutePath)
} catch (e: Exception) {
showErrorToast(e)
}
} }
else -> toast(R.string.invalid_file_format) else -> toast(R.string.invalid_file_format)
} }
@ -375,13 +454,13 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
FilePickerDialog(this, pickFile = false, showFAB = true) { FilePickerDialog(this, pickFile = false, showFAB = true) {
ExportContactsDialog(this, it) { file, contactSources -> ExportContactsDialog(this, it) { file, contactSources ->
Thread { Thread {
ContactsHelper(this).getContacts { ContactsHelper(this).getContacts { allContacts ->
val contacts = it.filter { contactSources.contains(it.source) } val contacts = allContacts.filter { contactSources.contains(it.source) }
if (contacts.isEmpty()) { if (contacts.isEmpty()) {
toast(R.string.no_entries_for_exporting) toast(R.string.no_entries_for_exporting)
} else { } else {
VcfExporter().exportContacts(this, file, contacts as ArrayList<Contact>, true) { VcfExporter().exportContacts(this, file, contacts as ArrayList<Contact>, true) { result ->
toast(when (it) { toast(when (result) {
VcfExporter.ExportResult.EXPORT_OK -> R.string.exporting_successful VcfExporter.ExportResult.EXPORT_OK -> R.string.exporting_successful
VcfExporter.ExportResult.EXPORT_PARTIAL -> R.string.exporting_some_entries_failed VcfExporter.ExportResult.EXPORT_PARTIAL -> R.string.exporting_some_entries_failed
else -> R.string.exporting_failed else -> R.string.exporting_failed
@ -395,32 +474,33 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
} }
private fun launchAbout() { private fun launchAbout() {
val licenses = LICENSE_MULTISELECT or LICENSE_JODA or LICENSE_GLIDE or LICENSE_GSON or LICENSE_STETHO
val faqItems = arrayListOf( val faqItems = arrayListOf(
FAQItem(R.string.faq_1_title, R.string.faq_1_text), FAQItem(R.string.faq_1_title, R.string.faq_1_text),
FAQItem(R.string.faq_2_title_commons, R.string.faq_2_text_commons) FAQItem(R.string.faq_2_title_commons, R.string.faq_2_text_commons)
) )
startAboutActivity(R.string.app_name, LICENSE_MULTISELECT or LICENSE_JODA or LICENSE_GLIDE or LICENSE_GSON or LICENSE_STETHO, startAboutActivity(R.string.app_name, licenses, BuildConfig.VERSION_NAME, faqItems, true)
BuildConfig.VERSION_NAME, faqItems)
} }
override fun refreshContacts(refreshTabsMask: Int) { override fun refreshContacts(refreshTabsMask: Int) {
if (isActivityDestroyed() || isGettingContacts) { if (isActivityDestroyed() || isGettingContacts) {
return return
} }
isGettingContacts = true isGettingContacts = true
if (viewpager.adapter == null) {
viewpager.adapter = ViewPagerAdapter(this)
viewpager.currentItem = config.lastUsedViewPagerPage
}
ContactsHelper(this).getContacts { ContactsHelper(this).getContacts {
isGettingContacts = false isGettingContacts = false
if (isActivityDestroyed()) { if (isActivityDestroyed()) {
return@getContacts return@getContacts
} }
if (viewpager.adapter == null) {
viewpager.adapter = ViewPagerAdapter(this, it)
viewpager.currentItem = config.lastUsedViewPagerPage
}
if (refreshTabsMask and CONTACTS_TAB_MASK != 0) { if (refreshTabsMask and CONTACTS_TAB_MASK != 0) {
contacts_fragment?.refreshContacts(it) contacts_fragment?.refreshContacts(it)
} }
@ -429,6 +509,10 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
favorites_fragment?.refreshContacts(it) favorites_fragment?.refreshContacts(it)
} }
if (refreshTabsMask and RECENTS_TAB_MASK != 0) {
recents_fragment?.refreshContacts(it)
}
if (refreshTabsMask and GROUPS_TAB_MASK != 0) { if (refreshTabsMask and GROUPS_TAB_MASK != 0) {
if (refreshTabsMask == GROUPS_TAB_MASK) { if (refreshTabsMask == GROUPS_TAB_MASK) {
groups_fragment.skipHashComparing = true groups_fragment.skipHashComparing = true
@ -436,15 +520,25 @@ class MainActivity : SimpleActivity(), RefreshContactsListener {
groups_fragment?.refreshContacts(it) groups_fragment?.refreshContacts(it)
} }
} }
if (refreshTabsMask and RECENTS_TAB_MASK != 0) {
ContactsHelper(this).getRecents {
runOnUiThread {
recents_fragment?.updateRecentCalls(it)
}
}
}
} }
private fun getAllFragments() = arrayListOf(contacts_fragment, favorites_fragment, groups_fragment) private fun getAllFragments() = arrayListOf(contacts_fragment, favorites_fragment, recents_fragment, groups_fragment)
private fun checkWhatsNewDialog() { private fun checkWhatsNewDialog() {
arrayListOf<Release>().apply { arrayListOf<Release>().apply {
add(Release(10, R.string.release_10)) add(Release(10, R.string.release_10))
add(Release(11, R.string.release_11)) add(Release(11, R.string.release_11))
add(Release(16, R.string.release_16)) add(Release(16, R.string.release_16))
add(Release(27, R.string.release_27))
add(Release(29, R.string.release_29))
checkWhatsNew(this, BuildConfig.VERSION_CODE) checkWhatsNew(this, BuildConfig.VERSION_CODE)
} }
} }

View File

@ -3,10 +3,13 @@ package com.simplemobiletools.contacts.activities
import android.os.Bundle import android.os.Bundle
import com.simplemobiletools.commons.dialogs.RadioGroupDialog import com.simplemobiletools.commons.dialogs.RadioGroupDialog
import com.simplemobiletools.commons.extensions.beVisibleIf import com.simplemobiletools.commons.extensions.beVisibleIf
import com.simplemobiletools.commons.extensions.isThankYouInstalled
import com.simplemobiletools.commons.extensions.launchPurchaseThankYouIntent
import com.simplemobiletools.commons.extensions.updateTextColors import com.simplemobiletools.commons.extensions.updateTextColors
import com.simplemobiletools.commons.models.RadioItem import com.simplemobiletools.commons.models.RadioItem
import com.simplemobiletools.contacts.R import com.simplemobiletools.contacts.R
import com.simplemobiletools.contacts.dialogs.ManageVisibleFieldsDialog import com.simplemobiletools.contacts.dialogs.ManageVisibleFieldsDialog
import com.simplemobiletools.contacts.dialogs.ManageVisibleTabsDialog
import com.simplemobiletools.contacts.extensions.config import com.simplemobiletools.contacts.extensions.config
import com.simplemobiletools.contacts.helpers.ON_CLICK_CALL_CONTACT 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_EDIT_CONTACT
@ -23,8 +26,10 @@ class SettingsActivity : SimpleActivity() {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
setupPurchaseThankYou()
setupCustomizeColors() setupCustomizeColors()
setupManageShownContactFields() setupManageShownContactFields()
setupManageShownTabs()
setupUseEnglish() setupUseEnglish()
setupAvoidWhatsNew() setupAvoidWhatsNew()
setupShowInfoBubble() setupShowInfoBubble()
@ -32,10 +37,18 @@ class SettingsActivity : SimpleActivity() {
setupShowPhoneNumbers() setupShowPhoneNumbers()
setupStartNameWithSurname() setupStartNameWithSurname()
setupFilterDuplicates() setupFilterDuplicates()
setupShowCallConfirmation()
setupOnContactClick() setupOnContactClick()
updateTextColors(settings_holder) updateTextColors(settings_holder)
} }
private fun setupPurchaseThankYou() {
settings_purchase_thank_you_holder.beVisibleIf(config.appRunCount > 10 && !isThankYouInstalled())
settings_purchase_thank_you_holder.setOnClickListener {
launchPurchaseThankYouIntent()
}
}
private fun setupCustomizeColors() { private fun setupCustomizeColors() {
settings_customize_colors_holder.setOnClickListener { settings_customize_colors_holder.setOnClickListener {
startCustomizationActivity() startCustomizationActivity()
@ -48,6 +61,12 @@ class SettingsActivity : SimpleActivity() {
} }
} }
private fun setupManageShownTabs() {
settings_manage_tabs_holder.setOnClickListener {
ManageVisibleTabsDialog(this)
}
}
private fun setupUseEnglish() { private fun setupUseEnglish() {
settings_use_english_holder.beVisibleIf(config.wasUseEnglishToggled || Locale.getDefault().language != "en") settings_use_english_holder.beVisibleIf(config.wasUseEnglishToggled || Locale.getDefault().language != "en")
settings_use_english.isChecked = config.useEnglish settings_use_english.isChecked = config.useEnglish
@ -126,4 +145,12 @@ class SettingsActivity : SimpleActivity() {
ON_CLICK_VIEW_CONTACT -> R.string.view_contact ON_CLICK_VIEW_CONTACT -> R.string.view_contact
else -> R.string.edit_contact else -> R.string.edit_contact
}) })
private fun setupShowCallConfirmation() {
settings_show_call_confirmation.isChecked = config.showCallConfirmation
settings_show_call_confirmation_holder.setOnClickListener {
settings_show_call_confirmation.toggle()
config.showCallConfirmation = settings_show_call_confirmation.isChecked
}
}
} }

View File

@ -23,6 +23,7 @@ import kotlinx.android.synthetic.main.item_website.view.*
class ViewContactActivity : ContactActivity() { class ViewContactActivity : ContactActivity() {
private var isViewIntent = false private var isViewIntent = false
private var showFields = 0
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -57,6 +58,10 @@ class ViewContactActivity : ContactActivity() {
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (contact == null) {
return true
}
when (item.itemId) { when (item.itemId) {
R.id.edit -> editContact(contact!!) R.id.edit -> editContact(contact!!)
R.id.share -> shareContact() R.id.share -> shareContact()
@ -160,7 +165,11 @@ class ViewContactActivity : ContactActivity() {
Intent().apply { Intent().apply {
action = ContactsContract.QuickContact.ACTION_QUICK_CONTACT action = ContactsContract.QuickContact.ACTION_QUICK_CONTACT
data = getContactPublicUri(contact!!) data = getContactPublicUri(contact!!)
startActivity(this) if (resolveActivity(packageManager) != null) {
startActivity(this)
} else {
toast(R.string.no_app_found)
}
} }
} }
@ -193,7 +202,11 @@ class ViewContactActivity : ContactActivity() {
contact_suffix.text = suffix contact_suffix.text = suffix
contact_suffix.beVisibleIf(suffix.isNotEmpty() && showFields and SHOW_SUFFIX_FIELD != 0) contact_suffix.beVisibleIf(suffix.isNotEmpty() && showFields and SHOW_SUFFIX_FIELD != 0)
if (contact_prefix.isGone() && contact_first_name.isGone() && contact_middle_name.isGone() && contact_surname.isGone() && contact_suffix.isGone()) { contact_nickname.text = nickname
contact_nickname.beVisibleIf(nickname.isNotEmpty() && showFields and SHOW_NICKNAME_FIELD != 0)
if (contact_prefix.isGone() && contact_first_name.isGone() && contact_middle_name.isGone() && contact_surname.isGone() && contact_suffix.isGone()
&& contact_nickname.isGone()) {
contact_name_image.beInvisible() contact_name_image.beInvisible()
(contact_photo.layoutParams as RelativeLayout.LayoutParams).bottomMargin = resources.getDimension(R.dimen.medium_margin).toInt() (contact_photo.layoutParams as RelativeLayout.LayoutParams).bottomMargin = resources.getDimension(R.dimen.medium_margin).toInt()
} }
@ -280,7 +293,7 @@ class ViewContactActivity : ContactActivity() {
layoutInflater.inflate(R.layout.item_event, contact_events_holder, false).apply { layoutInflater.inflate(R.layout.item_event, contact_events_holder, false).apply {
contact_events_holder.addView(this) contact_events_holder.addView(this)
contact_event.alpha = 1f contact_event.alpha = 1f
getDateTime(it.value, contact_event) it.value.getDateTimeFromDateString(contact_event)
contact_event_type.setText(getEventTextId(it.type)) contact_event_type.setText(getEventTextId(it.type))
contact_event_remove.beGone() contact_event_remove.beGone()
} }

View File

@ -70,10 +70,10 @@ class ContactsAdapter(activity: SimpleActivity, var contactItems: ArrayList<Cont
} }
} }
override fun prepareItemSelection(view: View) {} override fun prepareItemSelection(viewHolder: ViewHolder) {}
override fun markItemSelection(select: Boolean, view: View?) { override fun markViewHolderSelection(select: Boolean, viewHolder: ViewHolder?) {
view?.contact_frame?.isSelected = select viewHolder?.itemView?.contact_frame?.isSelected = select
} }
override fun actionItemPressed(id: Int) { override fun actionItemPressed(id: Int) {
@ -96,6 +96,8 @@ class ContactsAdapter(activity: SimpleActivity, var contactItems: ArrayList<Cont
override fun getSelectableItemCount() = contactItems.size override fun getSelectableItemCount() = contactItems.size
override fun getIsItemSelectable(position: Int) = true
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val layout = if (showPhoneNumbers) R.layout.item_contact_with_number else R.layout.item_contact_without_number val layout = if (showPhoneNumbers) R.layout.item_contact_with_number else R.layout.item_contact_without_number
return createViewHolder(layout, parent) return createViewHolder(layout, parent)
@ -103,7 +105,7 @@ class ContactsAdapter(activity: SimpleActivity, var contactItems: ArrayList<Cont
override fun onBindViewHolder(holder: MyRecyclerViewAdapter.ViewHolder, position: Int) { override fun onBindViewHolder(holder: MyRecyclerViewAdapter.ViewHolder, position: Int) {
val contact = contactItems[position] val contact = contactItems[position]
val view = holder.bindView(contact, true) { itemView, layoutPosition -> val view = holder.bindView(contact, true, true) { itemView, layoutPosition ->
setupView(itemView, contact) setupView(itemView, contact)
} }
bindViewHolder(holder, position, view) bindViewHolder(holder, position, view)
@ -145,7 +147,10 @@ class ContactsAdapter(activity: SimpleActivity, var contactItems: ArrayList<Cont
val contactsToRemove = ArrayList<Contact>() val contactsToRemove = ArrayList<Contact>()
selectedPositions.sortedDescending().forEach { selectedPositions.sortedDescending().forEach {
contactsToRemove.add(contactItems[it]) val contact = contactItems.getOrNull(it)
if (contact != null) {
contactsToRemove.add(contact)
}
} }
contactItems.removeAll(contactsToRemove) contactItems.removeAll(contactsToRemove)
@ -155,7 +160,7 @@ class ContactsAdapter(activity: SimpleActivity, var contactItems: ArrayList<Cont
finishActMode() finishActMode()
} else { } else {
removeSelectedItems() removeSelectedItems()
refreshListener?.refreshContacts(FAVORITES_TAB_MASK) refreshListener?.refreshContacts(CONTACTS_TAB_MASK or FAVORITES_TAB_MASK)
} }
} }
@ -250,8 +255,7 @@ class ContactsAdapter(activity: SimpleActivity, var contactItems: ArrayList<Cont
private fun setupView(view: View, contact: Contact) { private fun setupView(view: View, contact: Contact) {
view.apply { view.apply {
val fullName = contact.getFullName() val fullName = contact.getFullName()
val nameText = if (textToHighlight.isEmpty()) fullName else fullName.highlightTextPart(textToHighlight, adjustedPrimaryColor) contact_name.text = if (textToHighlight.isEmpty()) fullName else fullName.highlightTextPart(textToHighlight, adjustedPrimaryColor)
contact_name.text = nameText
contact_name.setTextColor(textColor) contact_name.setTextColor(textColor)
contact_name.setPadding(if (showContactThumbnails) smallPadding else bigPadding, smallPadding, smallPadding, 0) contact_name.setPadding(if (showContactThumbnails) smallPadding else bigPadding, smallPadding, smallPadding, 0)

View File

@ -37,14 +37,14 @@ class GroupsAdapter(activity: SimpleActivity, var groups: ArrayList<Group>, val
override fun prepareActionMode(menu: Menu) { override fun prepareActionMode(menu: Menu) {
menu.apply { menu.apply {
findItem(R.id.cab_edit).isVisible = isOneItemSelected() findItem(R.id.cab_rename).isVisible = isOneItemSelected()
} }
} }
override fun prepareItemSelection(view: View) {} override fun prepareItemSelection(viewHolder: ViewHolder) {}
override fun markItemSelection(select: Boolean, view: View?) { override fun markViewHolderSelection(select: Boolean, viewHolder: ViewHolder?) {
view?.group_frame?.isSelected = select viewHolder?.itemView?.group_frame?.isSelected = select
} }
override fun actionItemPressed(id: Int) { override fun actionItemPressed(id: Int) {
@ -53,7 +53,7 @@ class GroupsAdapter(activity: SimpleActivity, var groups: ArrayList<Group>, val
} }
when (id) { when (id) {
R.id.cab_edit -> editGroup() R.id.cab_rename -> renameGroup()
R.id.cab_select_all -> selectAll() R.id.cab_select_all -> selectAll()
R.id.cab_delete -> askConfirmDelete() R.id.cab_delete -> askConfirmDelete()
} }
@ -61,11 +61,13 @@ class GroupsAdapter(activity: SimpleActivity, var groups: ArrayList<Group>, val
override fun getSelectableItemCount() = groups.size override fun getSelectableItemCount() = groups.size
override fun getIsItemSelectable(position: Int) = true
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = createViewHolder(R.layout.item_group, parent) override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = createViewHolder(R.layout.item_group, parent)
override fun onBindViewHolder(holder: MyRecyclerViewAdapter.ViewHolder, position: Int) { override fun onBindViewHolder(holder: MyRecyclerViewAdapter.ViewHolder, position: Int) {
val group = groups[position] val group = groups[position]
val view = holder.bindView(group, true) { itemView, layoutPosition -> val view = holder.bindView(group, true, true) { itemView, layoutPosition ->
setupView(itemView, group) setupView(itemView, group)
} }
bindViewHolder(holder, position, view) bindViewHolder(holder, position, view)
@ -80,7 +82,7 @@ class GroupsAdapter(activity: SimpleActivity, var groups: ArrayList<Group>, val
fastScroller?.measureRecyclerView() fastScroller?.measureRecyclerView()
} }
private fun editGroup() { private fun renameGroup() {
RenameGroupDialog(activity, groups[selectedPositions.first()]) { RenameGroupDialog(activity, groups[selectedPositions.first()]) {
finishActMode() finishActMode()
refreshListener?.refreshContacts(GROUPS_TAB_MASK) refreshListener?.refreshContacts(GROUPS_TAB_MASK)
@ -89,11 +91,11 @@ class GroupsAdapter(activity: SimpleActivity, var groups: ArrayList<Group>, val
private fun askConfirmDelete() { private fun askConfirmDelete() {
ConfirmationDialog(activity) { ConfirmationDialog(activity) {
deleteContacts() deleteGroups()
} }
} }
private fun deleteContacts() { private fun deleteGroups() {
if (selectedPositions.isEmpty()) { if (selectedPositions.isEmpty()) {
return return
} }

View File

@ -0,0 +1,119 @@
package com.simplemobiletools.contacts.adapters
import android.view.Menu
import android.view.View
import android.view.ViewGroup
import com.simplemobiletools.commons.adapters.MyRecyclerViewAdapter
import com.simplemobiletools.commons.dialogs.ConfirmationDialog
import com.simplemobiletools.commons.extensions.beVisibleIf
import com.simplemobiletools.commons.views.FastScroller
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.helpers.ContactsHelper
import com.simplemobiletools.contacts.helpers.RECENTS_TAB_MASK
import com.simplemobiletools.contacts.interfaces.RefreshContactsListener
import com.simplemobiletools.contacts.models.RecentCall
import kotlinx.android.synthetic.main.item_recent_call.view.*
import java.util.*
class RecentCallsAdapter(activity: SimpleActivity, var recentCalls: ArrayList<RecentCall>, val refreshListener: RefreshContactsListener?, recyclerView: MyRecyclerView,
fastScroller: FastScroller, itemClick: (Any) -> Unit) : MyRecyclerViewAdapter(activity, recyclerView, fastScroller, itemClick) {
private val showPhoneNumbers = activity.config.showPhoneNumbers
init {
setupDragListener(true)
}
override fun getActionMenuId() = R.menu.cab_recent_calls
override fun prepareActionMode(menu: Menu) {}
override fun prepareItemSelection(viewHolder: ViewHolder) {}
override fun markViewHolderSelection(select: Boolean, viewHolder: ViewHolder?) {
viewHolder?.itemView?.recent_call_frame?.isSelected = select
}
override fun actionItemPressed(id: Int) {
if (selectedPositions.isEmpty()) {
return
}
when (id) {
R.id.cab_select_all -> selectAll()
R.id.cab_delete -> askConfirmDelete()
}
}
override fun getSelectableItemCount() = recentCalls.size
override fun getIsItemSelectable(position: Int) = true
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = createViewHolder(R.layout.item_recent_call, parent)
override fun onBindViewHolder(holder: MyRecyclerViewAdapter.ViewHolder, position: Int) {
val recentCall = recentCalls[position]
val view = holder.bindView(recentCall, true, true) { itemView, layoutPosition ->
setupView(itemView, recentCall)
}
bindViewHolder(holder, position, view)
}
override fun getItemCount() = recentCalls.size
fun updateItems(newItems: ArrayList<RecentCall>) {
recentCalls = newItems
notifyDataSetChanged()
finishActMode()
fastScroller?.measureRecyclerView()
}
private fun askConfirmDelete() {
ConfirmationDialog(activity) {
deleteRecentCalls()
}
}
private fun deleteRecentCalls() {
if (selectedPositions.isEmpty()) {
return
}
val callsToRemove = ArrayList<RecentCall>()
selectedPositions.sortedDescending().forEach {
val call = recentCalls[it]
callsToRemove.add(call)
}
ContactsHelper(activity).removeRecentCalls(callsToRemove.map { it.id } as ArrayList<Int>)
recentCalls.removeAll(callsToRemove)
if (recentCalls.isEmpty()) {
refreshListener?.refreshContacts(RECENTS_TAB_MASK)
finishActMode()
} else {
removeSelectedItems()
}
}
private fun setupView(view: View, recentCall: RecentCall) {
view.apply {
recent_call_name.apply {
text = recentCall.name ?: recentCall.number
setTextColor(textColor)
}
recent_call_number.apply {
beVisibleIf(showPhoneNumbers && recentCall.name != null)
text = recentCall.number
setTextColor(textColor)
}
recent_call_date_time.apply {
text = recentCall.dateTime
setTextColor(textColor)
}
}
}
}

View File

@ -5,19 +5,22 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import com.simplemobiletools.contacts.R import com.simplemobiletools.contacts.R
import com.simplemobiletools.contacts.activities.MainActivity import com.simplemobiletools.contacts.activities.MainActivity
import com.simplemobiletools.contacts.extensions.config
import com.simplemobiletools.contacts.fragments.MyViewPagerFragment import com.simplemobiletools.contacts.fragments.MyViewPagerFragment
import com.simplemobiletools.contacts.models.Contact import com.simplemobiletools.contacts.helpers.*
class ViewPagerAdapter(val activity: MainActivity, val contacts: ArrayList<Contact>) : PagerAdapter() { class ViewPagerAdapter(val activity: MainActivity) : PagerAdapter() {
private val showTabs = activity.config.showTabs
override fun instantiateItem(container: ViewGroup, position: Int): Any { override fun instantiateItem(container: ViewGroup, position: Int): Any {
val layout = getFragment(position) val layout = getFragment(position)
val view = activity.layoutInflater.inflate(layout, container, false) val view = activity.layoutInflater.inflate(layout, container, false)
container.addView(view) container.addView(view)
(view as MyViewPagerFragment).apply { (view as MyViewPagerFragment).apply {
setupFragment(activity) setupFragment(activity)
refreshContacts(contacts)
} }
return view return view
} }
@ -25,12 +28,28 @@ class ViewPagerAdapter(val activity: MainActivity, val contacts: ArrayList<Conta
container.removeView(item as View) container.removeView(item as View)
} }
override fun getCount() = 3 override fun getCount() = tabsList.filter { it and showTabs != 0 }.size
override fun isViewFromObject(view: View, item: Any) = view == item override fun isViewFromObject(view: View, item: Any) = view == item
private fun getFragment(position: Int) = when (position) { private fun getFragment(position: Int): Int {
0 -> R.layout.fragment_contacts val fragments = arrayListOf<Int>()
1 -> R.layout.fragment_favorites if (showTabs and CONTACTS_TAB_MASK != 0) {
else -> R.layout.fragment_groups fragments.add(R.layout.fragment_contacts)
}
if (showTabs and FAVORITES_TAB_MASK != 0) {
fragments.add(R.layout.fragment_favorites)
}
if (showTabs and RECENTS_TAB_MASK != 0) {
fragments.add(R.layout.fragment_recents)
}
if (showTabs and GROUPS_TAB_MASK != 0) {
fragments.add(R.layout.fragment_groups)
}
return fragments[position]
} }
} }

View File

@ -0,0 +1,33 @@
package com.simplemobiletools.contacts.dialogs
import android.support.v7.app.AlertDialog
import android.view.animation.AnimationUtils
import com.simplemobiletools.commons.activities.BaseSimpleActivity
import com.simplemobiletools.commons.extensions.applyColorFilter
import com.simplemobiletools.commons.extensions.setupDialogStuff
import com.simplemobiletools.contacts.R
import com.simplemobiletools.contacts.extensions.config
import com.simplemobiletools.contacts.models.Contact
import kotlinx.android.synthetic.main.dialog_call_confirmation.view.*
class CallConfirmationDialog(val activity: BaseSimpleActivity, val contact: Contact, private val callback: () -> Unit) {
private var view = activity.layoutInflater.inflate(R.layout.dialog_call_confirmation, null)
init {
view.call_confirm_phone.applyColorFilter(activity.config.textColor)
AlertDialog.Builder(activity)
.setNegativeButton(R.string.cancel, null)
.create().apply {
val title = String.format(activity.getString(R.string.call_person), contact.getFullName())
activity.setupDialogStuff(view, this, titleText = title) {
view.call_confirm_phone.apply {
startAnimation(AnimationUtils.loadAnimation(activity, R.anim.pulsing_animation))
setOnClickListener {
callback.invoke()
dismiss()
}
}
}
}
}
}

View File

@ -19,6 +19,7 @@ class ManageVisibleFieldsDialog(val activity: BaseSimpleActivity) {
put(SHOW_MIDDLE_NAME_FIELD, R.id.manage_visible_fields_middle_name) put(SHOW_MIDDLE_NAME_FIELD, R.id.manage_visible_fields_middle_name)
put(SHOW_SURNAME_FIELD, R.id.manage_visible_fields_surname) put(SHOW_SURNAME_FIELD, R.id.manage_visible_fields_surname)
put(SHOW_SUFFIX_FIELD, R.id.manage_visible_fields_suffix) put(SHOW_SUFFIX_FIELD, R.id.manage_visible_fields_suffix)
put(SHOW_NICKNAME_FIELD, R.id.manage_visible_fields_nickname)
put(SHOW_PHONE_NUMBERS_FIELD, R.id.manage_visible_fields_phone_numbers) put(SHOW_PHONE_NUMBERS_FIELD, R.id.manage_visible_fields_phone_numbers)
put(SHOW_EMAILS_FIELD, R.id.manage_visible_fields_emails) put(SHOW_EMAILS_FIELD, R.id.manage_visible_fields_emails)
put(SHOW_ADDRESSES_FIELD, R.id.manage_visible_fields_addresses) put(SHOW_ADDRESSES_FIELD, R.id.manage_visible_fields_addresses)
@ -36,7 +37,7 @@ class ManageVisibleFieldsDialog(val activity: BaseSimpleActivity) {
} }
AlertDialog.Builder(activity) AlertDialog.Builder(activity)
.setPositiveButton(R.string.ok, { dialog, which -> dialogConfirmed() }) .setPositiveButton(R.string.ok) { dialog, which -> dialogConfirmed() }
.setNegativeButton(R.string.cancel, null) .setNegativeButton(R.string.cancel, null)
.create().apply { .create().apply {
activity.setupDialogStuff(view, this) activity.setupDialogStuff(view, this)

View File

@ -0,0 +1,50 @@
package com.simplemobiletools.contacts.dialogs
import android.support.v7.app.AlertDialog
import com.simplemobiletools.commons.activities.BaseSimpleActivity
import com.simplemobiletools.commons.extensions.setupDialogStuff
import com.simplemobiletools.commons.views.MyAppCompatCheckbox
import com.simplemobiletools.contacts.R
import com.simplemobiletools.contacts.extensions.config
import com.simplemobiletools.contacts.helpers.*
class ManageVisibleTabsDialog(val activity: BaseSimpleActivity) {
private var view = activity.layoutInflater.inflate(R.layout.dialog_manage_visible_tabs, null)
private val tabs = LinkedHashMap<Int, Int>()
init {
tabs.apply {
put(CONTACTS_TAB_MASK, R.id.manage_visible_tabs_contacts)
put(FAVORITES_TAB_MASK, R.id.manage_visible_tabs_favorites)
put(RECENTS_TAB_MASK, R.id.manage_visible_tabs_recents)
put(GROUPS_TAB_MASK, R.id.manage_visible_tabs_groups)
}
val showTabs = activity.config.showTabs
for ((key, value) in tabs) {
view.findViewById<MyAppCompatCheckbox>(value).isChecked = showTabs and key != 0
}
AlertDialog.Builder(activity)
.setPositiveButton(R.string.ok) { dialog, which -> dialogConfirmed() }
.setNegativeButton(R.string.cancel, null)
.create().apply {
activity.setupDialogStuff(view, this)
}
}
private fun dialogConfirmed() {
var result = 0
for ((key, value) in tabs) {
if (view.findViewById<MyAppCompatCheckbox>(value).isChecked) {
result += key
}
}
if (result == 0) {
result = ALL_TABS_MASK
}
activity.config.showTabs = result
}
}

View File

@ -14,9 +14,8 @@ import com.simplemobiletools.commons.models.RadioItem
import com.simplemobiletools.contacts.BuildConfig import com.simplemobiletools.contacts.BuildConfig
import com.simplemobiletools.contacts.R import com.simplemobiletools.contacts.R
import com.simplemobiletools.contacts.activities.SimpleActivity import com.simplemobiletools.contacts.activities.SimpleActivity
import com.simplemobiletools.contacts.helpers.ContactsHelper import com.simplemobiletools.contacts.dialogs.CallConfirmationDialog
import com.simplemobiletools.contacts.helpers.SMT_PRIVATE import com.simplemobiletools.contacts.helpers.*
import com.simplemobiletools.contacts.helpers.VcfExporter
import com.simplemobiletools.contacts.models.Contact import com.simplemobiletools.contacts.models.Contact
import com.simplemobiletools.contacts.models.ContactSource import com.simplemobiletools.contacts.models.ContactSource
import java.io.File import java.io.File
@ -36,6 +35,16 @@ fun SimpleActivity.startCallIntent(recipient: String) {
} }
fun SimpleActivity.tryStartCall(contact: Contact) { fun SimpleActivity.tryStartCall(contact: Contact) {
if (config.showCallConfirmation) {
CallConfirmationDialog(this, contact) {
startCall(contact)
}
} else {
startCall(contact)
}
}
fun SimpleActivity.startCall(contact: Contact) {
val numbers = contact.phoneNumbers val numbers = contact.phoneNumbers
if (numbers.size == 1) { if (numbers.size == 1) {
startCallIntent(numbers.first().value) startCallIntent(numbers.first().value)
@ -105,10 +114,10 @@ fun BaseSimpleActivity.shareContacts(contacts: ArrayList<Contact>) {
fun BaseSimpleActivity.sendSMSToContacts(contacts: ArrayList<Contact>) { fun BaseSimpleActivity.sendSMSToContacts(contacts: ArrayList<Contact>) {
val numbers = StringBuilder() val numbers = StringBuilder()
contacts.forEach { contacts.forEach {
it.phoneNumbers.forEach { val number = it.phoneNumbers.firstOrNull { it.type == ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE }
if (it.value.isNotEmpty()) { ?: it.phoneNumbers.firstOrNull()
numbers.append("${it.value};") if (number != null) {
} numbers.append("${number.value};")
} }
val uriString = "smsto:${numbers.toString().trimEnd(';')}" val uriString = "smsto:${numbers.toString().trimEnd(';')}"
@ -191,3 +200,17 @@ fun Activity.getVisibleContactSources(): ArrayList<String> {
sourceNames.removeAll(config.ignoredContactSources) sourceNames.removeAll(config.ignoredContactSources)
return sourceNames return sourceNames
} }
fun SimpleActivity.contactClicked(contact: Contact) {
when (config.onContactClick) {
ON_CLICK_CALL_CONTACT -> {
if (contact.phoneNumbers.isNotEmpty()) {
tryStartCall(contact)
} else {
toast(R.string.no_phone_number_found)
}
}
ON_CLICK_VIEW_CONTACT -> viewContact(contact)
ON_CLICK_EDIT_CONTACT -> editContact(contact)
}
}

View File

@ -0,0 +1,34 @@
package com.simplemobiletools.contacts.extensions
import android.widget.TextView
import com.simplemobiletools.commons.helpers.getDateFormats
import org.joda.time.DateTime
import org.joda.time.format.DateTimeFormat
import java.text.DateFormat
import java.text.SimpleDateFormat
import java.util.*
fun String.getDateTimeFromDateString(viewToUpdate: TextView? = null): DateTime {
val dateFormats = getDateFormats()
var date = DateTime()
for (format in dateFormats) {
try {
date = DateTime.parse(this, 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
}

View File

@ -15,7 +15,10 @@ import com.simplemobiletools.contacts.activities.MainActivity
import com.simplemobiletools.contacts.activities.SimpleActivity import com.simplemobiletools.contacts.activities.SimpleActivity
import com.simplemobiletools.contacts.adapters.ContactsAdapter import com.simplemobiletools.contacts.adapters.ContactsAdapter
import com.simplemobiletools.contacts.adapters.GroupsAdapter import com.simplemobiletools.contacts.adapters.GroupsAdapter
import com.simplemobiletools.contacts.extensions.* import com.simplemobiletools.contacts.adapters.RecentCallsAdapter
import com.simplemobiletools.contacts.extensions.config
import com.simplemobiletools.contacts.extensions.contactClicked
import com.simplemobiletools.contacts.extensions.getVisibleContactSources
import com.simplemobiletools.contacts.helpers.* import com.simplemobiletools.contacts.helpers.*
import com.simplemobiletools.contacts.models.Contact import com.simplemobiletools.contacts.models.Contact
import com.simplemobiletools.contacts.models.Group import com.simplemobiletools.contacts.models.Group
@ -47,21 +50,28 @@ abstract class MyViewPagerFragment(context: Context, attributeSet: AttributeSet)
fragment_placeholder_2.underlineText() fragment_placeholder_2.underlineText()
updateViewStuff() updateViewStuff()
if (this is FavoritesFragment) { when {
fragment_placeholder.text = activity.getString(R.string.no_favorites) this is FavoritesFragment -> {
fragment_placeholder_2.text = activity.getString(R.string.add_favorites) fragment_placeholder.text = activity.getString(R.string.no_favorites)
} else if (this is GroupsFragment) { fragment_placeholder_2.text = activity.getString(R.string.add_favorites)
fragment_placeholder.text = activity.getString(R.string.no_group_created) }
fragment_placeholder_2.text = activity.getString(R.string.create_group) this is GroupsFragment -> {
fragment_placeholder.text = activity.getString(R.string.no_group_created)
fragment_placeholder_2.text = activity.getString(R.string.create_group)
}
this is RecentsFragment -> {
fragment_fab.beGone()
fragment_placeholder_2.text = activity.getString(R.string.request_the_required_permissions)
}
} }
} }
} }
fun textColorChanged(color: Int) { fun textColorChanged(color: Int) {
if (this is GroupsFragment) { when {
(fragment_list.adapter as GroupsAdapter).updateTextColor(color) this is GroupsFragment -> (fragment_list.adapter as GroupsAdapter).updateTextColor(color)
} else { this is RecentsFragment -> (fragment_list.adapter as RecentCallsAdapter).updateTextColor(color)
(fragment_list.adapter as ContactsAdapter).apply { else -> (fragment_list.adapter as ContactsAdapter).apply {
updateTextColor(color) updateTextColor(color)
initDrawables() initDrawables()
} }
@ -77,7 +87,7 @@ abstract class MyViewPagerFragment(context: Context, attributeSet: AttributeSet)
} }
fun startNameWithSurnameChanged(startNameWithSurname: Boolean) { fun startNameWithSurnameChanged(startNameWithSurname: Boolean) {
if (this !is GroupsFragment) { if (this !is GroupsFragment && this !is RecentsFragment) {
(fragment_list.adapter as ContactsAdapter).apply { (fragment_list.adapter as ContactsAdapter).apply {
config.sorting = if (startNameWithSurname) SORT_BY_SURNAME else SORT_BY_FIRST_NAME config.sorting = if (startNameWithSurname) SORT_BY_SURNAME else SORT_BY_FIRST_NAME
this@MyViewPagerFragment.activity!!.refreshContacts(CONTACTS_TAB_MASK or FAVORITES_TAB_MASK) this@MyViewPagerFragment.activity!!.refreshContacts(CONTACTS_TAB_MASK or FAVORITES_TAB_MASK)
@ -86,6 +96,13 @@ abstract class MyViewPagerFragment(context: Context, attributeSet: AttributeSet)
} }
fun refreshContacts(contacts: ArrayList<Contact>) { fun refreshContacts(contacts: ArrayList<Contact>) {
if ((config.showTabs and CONTACTS_TAB_MASK == 0 && this is ContactsFragment) ||
(config.showTabs and FAVORITES_TAB_MASK == 0 && this is FavoritesFragment) ||
(config.showTabs and RECENTS_TAB_MASK == 0 && this is RecentsFragment) ||
(config.showTabs and GROUPS_TAB_MASK == 0 && this is GroupsFragment)) {
return
}
if (config.lastUsedContactSource.isEmpty()) { if (config.lastUsedContactSource.isEmpty()) {
val grouped = contacts.groupBy { it.source }.maxWith(compareBy { it.value.size }) val grouped = contacts.groupBy { it.source }.maxWith(compareBy { it.value.size })
config.lastUsedContactSource = grouped?.key ?: "" config.lastUsedContactSource = grouped?.key ?: ""
@ -99,6 +116,7 @@ abstract class MyViewPagerFragment(context: Context, attributeSet: AttributeSet)
val filtered = when { val filtered = when {
this is GroupsFragment -> contacts this is GroupsFragment -> contacts
this is FavoritesFragment -> contacts.filter { it.starred == 1 } as ArrayList<Contact> this is FavoritesFragment -> contacts.filter { it.starred == 1 } as ArrayList<Contact>
this is RecentsFragment -> ArrayList()
else -> { else -> {
val contactSources = activity!!.getVisibleContactSources() val contactSources = activity!!.getVisibleContactSources()
contacts.filter { contactSources.contains(it.source) } as ArrayList<Contact> contacts.filter { contactSources.contains(it.source) } as ArrayList<Contact>
@ -117,9 +135,13 @@ abstract class MyViewPagerFragment(context: Context, attributeSet: AttributeSet)
private fun setupContacts(contacts: ArrayList<Contact>) { private fun setupContacts(contacts: ArrayList<Contact>) {
if (this is GroupsFragment) { if (this is GroupsFragment) {
setupGroupsAdapter(contacts) setupGroupsAdapter(contacts)
} else { } else if (this !is RecentsFragment) {
setupContactsFavoritesAdapter(contacts) setupContactsFavoritesAdapter(contacts)
} }
if (this is ContactsFragment || this is FavoritesFragment) {
contactsIgnoringSearch = (fragment_list?.adapter as? ContactsAdapter)?.contactItems ?: ArrayList()
}
} }
private fun setupGroupsAdapter(contacts: ArrayList<Contact>) { private fun setupGroupsAdapter(contacts: ArrayList<Contact>) {
@ -150,7 +172,7 @@ abstract class MyViewPagerFragment(context: Context, attributeSet: AttributeSet)
fragment_list.adapter = this fragment_list.adapter = this
} }
fragment_fastscroller.setScrollTo(0) fragment_fastscroller.setScrollToY(0)
fragment_fastscroller.setViews(fragment_list) { fragment_fastscroller.setViews(fragment_list) {
val item = (fragment_list.adapter as GroupsAdapter).groups.getOrNull(it) val item = (fragment_list.adapter as GroupsAdapter).groups.getOrNull(it)
fragment_fastscroller.updateBubbleText(item?.getBubbleText() ?: "") fragment_fastscroller.updateBubbleText(item?.getBubbleText() ?: "")
@ -164,33 +186,19 @@ abstract class MyViewPagerFragment(context: Context, attributeSet: AttributeSet)
} }
private fun setupContactsFavoritesAdapter(contacts: ArrayList<Contact>) { private fun setupContactsFavoritesAdapter(contacts: ArrayList<Contact>) {
fragment_placeholder_2.beVisibleIf(contacts.isEmpty()) setupViewVisibility(contacts)
fragment_placeholder.beVisibleIf(contacts.isEmpty())
fragment_list.beVisibleIf(contacts.isNotEmpty())
val currAdapter = fragment_list.adapter val currAdapter = fragment_list.adapter
if (currAdapter == null || forceListRedraw) { if (currAdapter == null || forceListRedraw) {
forceListRedraw = false forceListRedraw = false
val location = if (this is FavoritesFragment) LOCATION_FAVORITES_TAB else LOCATION_CONTACTS_TAB val location = if (this is FavoritesFragment) LOCATION_FAVORITES_TAB else LOCATION_CONTACTS_TAB
ContactsAdapter(activity as SimpleActivity, contacts, activity, location, null, fragment_list, fragment_fastscroller) { ContactsAdapter(activity as SimpleActivity, contacts, activity, location, null, fragment_list, fragment_fastscroller) {
when (config.onContactClick) { activity?.contactClicked(it as Contact)
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)
}
}
ON_CLICK_VIEW_CONTACT -> context!!.viewContact(it as Contact)
ON_CLICK_EDIT_CONTACT -> context!!.editContact(it as Contact)
}
}.apply { }.apply {
addVerticalDividers(true) addVerticalDividers(true)
fragment_list.adapter = this fragment_list.adapter = this
} }
fragment_fastscroller.setScrollTo(0) fragment_fastscroller.setScrollToY(0)
fragment_fastscroller.setViews(fragment_list) { fragment_fastscroller.setViews(fragment_list) {
val item = (fragment_list.adapter as ContactsAdapter).contactItems.getOrNull(it) val item = (fragment_list.adapter as ContactsAdapter).contactItems.getOrNull(it)
fragment_fastscroller.updateBubbleText(item?.getBubbleText() ?: "") fragment_fastscroller.updateBubbleText(item?.getBubbleText() ?: "")
@ -211,7 +219,7 @@ abstract class MyViewPagerFragment(context: Context, attributeSet: AttributeSet)
showContactThumbnails = showThumbnails showContactThumbnails = showThumbnails
notifyDataSetChanged() notifyDataSetChanged()
} }
} else { } else if (this !is RecentsFragment) {
(fragment_list.adapter as? ContactsAdapter)?.apply { (fragment_list.adapter as? ContactsAdapter)?.apply {
showContactThumbnails = showThumbnails showContactThumbnails = showThumbnails
notifyDataSetChanged() notifyDataSetChanged()
@ -228,38 +236,44 @@ abstract class MyViewPagerFragment(context: Context, attributeSet: AttributeSet)
} }
fun onSearchQueryChanged(text: String) { fun onSearchQueryChanged(text: String) {
val shouldNormalize = text.normalizeString() == text
(fragment_list.adapter as? ContactsAdapter)?.apply { (fragment_list.adapter as? ContactsAdapter)?.apply {
val filtered = contactsIgnoringSearch.filter { val filtered = contactsIgnoringSearch.filter {
it.getFullName().contains(text, true) || getProperText(it.getFullName(), shouldNormalize).contains(text, true) ||
getProperText(it.nickname, shouldNormalize).contains(text, true) ||
it.phoneNumbers.any { it.value.contains(text, true) } || it.phoneNumbers.any { it.value.contains(text, true) } ||
it.emails.any { it.value.contains(text, true) } || it.emails.any { it.value.contains(text, true) } ||
it.addresses.any { it.value.contains(text, true) } || it.addresses.any { getProperText(it.value, shouldNormalize).contains(text, true) } ||
it.notes.contains(text, true) || getProperText(it.notes, shouldNormalize).contains(text, true) ||
it.organization.company.contains(text, true) || getProperText(it.organization.company, shouldNormalize).contains(text, true) ||
it.organization.jobPosition.contains(text, true) || getProperText(it.organization.jobPosition, shouldNormalize).contains(text, true) ||
it.websites.any { it.contains(text, true) } it.websites.any { it.contains(text, true) }
} as ArrayList } as ArrayList
Contact.sorting = config.sorting Contact.sorting = config.sorting
Contact.startWithSurname = config.startNameWithSurname Contact.startWithSurname = config.startNameWithSurname
filtered.sort() filtered.sort()
filtered.sortBy { !it.getFullName().startsWith(text, true) } filtered.sortBy { !getProperText(it.getFullName(), shouldNormalize).startsWith(text, true) }
if (filtered.isEmpty() && this@MyViewPagerFragment is FavoritesFragment) { if (filtered.isEmpty() && this@MyViewPagerFragment is FavoritesFragment) {
fragment_placeholder.text = activity.getString(R.string.no_items_found) fragment_placeholder.text = activity.getString(R.string.no_items_found)
} }
fragment_placeholder.beVisibleIf(filtered.isEmpty()) fragment_placeholder.beVisibleIf(filtered.isEmpty())
updateItems(filtered, text) updateItems(filtered, text.normalizeString())
} }
} }
private fun getProperText(text: String, shouldNormalize: Boolean) = if (shouldNormalize) text.normalizeString() else text
fun onSearchOpened() { fun onSearchOpened() {
contactsIgnoringSearch = (fragment_list?.adapter as? ContactsAdapter)?.contactItems ?: ArrayList() contactsIgnoringSearch = (fragment_list?.adapter as? ContactsAdapter)?.contactItems ?: ArrayList()
} }
fun onSearchClosed() { fun onSearchClosed() {
(fragment_list.adapter as? ContactsAdapter)?.updateItems(contactsIgnoringSearch) (fragment_list.adapter as? ContactsAdapter)?.updateItems(contactsIgnoringSearch)
setupViewVisibility(contactsIgnoringSearch)
if (this is FavoritesFragment) { if (this is FavoritesFragment) {
fragment_placeholder.text = activity?.getString(R.string.no_favorites) fragment_placeholder.text = activity?.getString(R.string.no_favorites)
} }
@ -272,6 +286,12 @@ abstract class MyViewPagerFragment(context: Context, attributeSet: AttributeSet)
fragment_placeholder_2.setTextColor(context.getAdjustedPrimaryColor()) fragment_placeholder_2.setTextColor(context.getAdjustedPrimaryColor())
} }
private fun setupViewVisibility(contacts: ArrayList<Contact>) {
fragment_placeholder_2.beVisibleIf(contacts.isEmpty())
fragment_placeholder.beVisibleIf(contacts.isEmpty())
fragment_list.beVisibleIf(contacts.isNotEmpty())
}
abstract fun fabClicked() abstract fun fabClicked()
abstract fun placeholderClicked() abstract fun placeholderClicked()

View File

@ -0,0 +1,79 @@
package com.simplemobiletools.contacts.fragments
import android.content.Context
import android.content.Intent
import android.util.AttributeSet
import com.simplemobiletools.commons.extensions.beVisibleIf
import com.simplemobiletools.commons.extensions.hasPermission
import com.simplemobiletools.commons.extensions.isActivityDestroyed
import com.simplemobiletools.commons.helpers.PERMISSION_READ_CALL_LOG
import com.simplemobiletools.commons.helpers.PERMISSION_WRITE_CALL_LOG
import com.simplemobiletools.contacts.activities.EditContactActivity
import com.simplemobiletools.contacts.adapters.RecentCallsAdapter
import com.simplemobiletools.contacts.extensions.contactClicked
import com.simplemobiletools.contacts.helpers.IS_FROM_SIMPLE_CONTACTS
import com.simplemobiletools.contacts.helpers.KEY_PHONE
import com.simplemobiletools.contacts.helpers.PHONE_NUMBER_PATTERN
import com.simplemobiletools.contacts.helpers.RECENTS_TAB_MASK
import com.simplemobiletools.contacts.models.Contact
import com.simplemobiletools.contacts.models.RecentCall
import kotlinx.android.synthetic.main.fragment_layout.view.*
class RecentsFragment(context: Context, attributeSet: AttributeSet) : MyViewPagerFragment(context, attributeSet) {
override fun fabClicked() {}
override fun placeholderClicked() {
activity!!.handlePermission(PERMISSION_WRITE_CALL_LOG) {
if (it) {
activity!!.handlePermission(PERMISSION_READ_CALL_LOG) {
activity?.refreshContacts(RECENTS_TAB_MASK)
}
}
}
}
fun updateRecentCalls(recentCalls: ArrayList<RecentCall>) {
if (activity == null || activity!!.isActivityDestroyed()) {
return
}
fragment_placeholder.beVisibleIf(recentCalls.isEmpty())
fragment_placeholder_2.beVisibleIf(recentCalls.isEmpty() && !activity!!.hasPermission(PERMISSION_WRITE_CALL_LOG))
fragment_list.beVisibleIf(recentCalls.isNotEmpty())
val currAdapter = fragment_list.adapter
if (currAdapter == null) {
RecentCallsAdapter(activity!!, recentCalls, activity, fragment_list, fragment_fastscroller) {
val recentCall = (it as RecentCall).number.replace(PHONE_NUMBER_PATTERN.toRegex(), "")
var selectedContact: Contact? = null
for (contact in allContacts) {
if (contact.phoneNumbers.any { it.value.replace(PHONE_NUMBER_PATTERN.toRegex(), "") == recentCall }) {
selectedContact = contact
break
}
}
if (selectedContact != null) {
activity?.contactClicked(selectedContact)
} else {
Intent(context, EditContactActivity::class.java).apply {
action = Intent.ACTION_INSERT
putExtra(KEY_PHONE, recentCall)
putExtra(IS_FROM_SIMPLE_CONTACTS, true)
context.startActivity(this)
}
}
}.apply {
addVerticalDividers(true)
fragment_list.adapter = this
}
fragment_fastscroller.setViews(fragment_list) {
val item = (fragment_list.adapter as RecentCallsAdapter).recentCalls.getOrNull(it)
fragment_fastscroller.updateBubbleText(item?.name ?: item?.number ?: "")
}
} else {
(currAdapter as RecentCallsAdapter).updateItems(recentCalls)
}
}
}

View File

@ -48,4 +48,12 @@ class Config(context: Context) : BaseConfig(context) {
var filterDuplicates: Boolean var filterDuplicates: Boolean
get() = prefs.getBoolean(FILTER_DUPLICATES, true) get() = prefs.getBoolean(FILTER_DUPLICATES, true)
set(filterDuplicates) = prefs.edit().putBoolean(FILTER_DUPLICATES, filterDuplicates).apply() set(filterDuplicates) = prefs.edit().putBoolean(FILTER_DUPLICATES, filterDuplicates).apply()
var showTabs: Int
get() = prefs.getInt(SHOW_TABS, ALL_TABS_MASK)
set(showTabs) = prefs.edit().putInt(SHOW_TABS, showTabs).apply()
var showCallConfirmation: Boolean
get() = prefs.getBoolean(SHOW_CALL_CONFIRMATION, false)
set(showCallConfirmation) = prefs.edit().putBoolean(SHOW_CALL_CONFIRMATION, showCallConfirmation).apply()
} }

View File

@ -12,23 +12,39 @@ const val LOCAL_ACCOUNT_NAME = "local_account_name"
const val LOCAL_ACCOUNT_TYPE = "local_account_type" const val LOCAL_ACCOUNT_TYPE = "local_account_type"
const val ON_CONTACT_CLICK = "on_contact_click" const val ON_CONTACT_CLICK = "on_contact_click"
const val SHOW_CONTACT_FIELDS = "show_contact_fields" const val SHOW_CONTACT_FIELDS = "show_contact_fields"
const val SHOW_TABS = "show_tabs"
const val FILTER_DUPLICATES = "filter_duplicates" const val FILTER_DUPLICATES = "filter_duplicates"
const val SHOW_CALL_CONFIRMATION = "show_call_confirmation"
const val CONTACT_ID = "contact_id" const val CONTACT_ID = "contact_id"
const val SMT_PRIVATE = "smt_private" // used at the contact source of local contacts hidden from other apps const val SMT_PRIVATE = "smt_private" // used at the contact source of local contacts hidden from other apps
const val IS_PRIVATE = "is_private" const val IS_PRIVATE = "is_private"
const val GROUP = "group" const val GROUP = "group"
const val FIRST_GROUP_ID = 10000 const val FIRST_GROUP_ID = 10000
const val PHONE_NUMBER_PATTERN = "\\D+"
const val IS_FROM_SIMPLE_CONTACTS = "is_from_simple_contacts"
// extras used at third party intents
const val KEY_PHONE = "phone"
const val KEY_NAME = "name"
const val LOCATION_CONTACTS_TAB = 0 const val LOCATION_CONTACTS_TAB = 0
const val LOCATION_FAVORITES_TAB = 1 const val LOCATION_FAVORITES_TAB = 1
const val LOCATION_GROUPS_TAB = 2 const val LOCATION_RECENTS_TAB = 2
const val LOCATION_GROUP_CONTACTS = 3 const val LOCATION_GROUPS_TAB = 3
const val LOCATION_GROUP_CONTACTS = 4
const val CONTACTS_TAB_MASK = 1 const val CONTACTS_TAB_MASK = 1
const val FAVORITES_TAB_MASK = 2 const val FAVORITES_TAB_MASK = 2
const val GROUPS_TAB_MASK = 4 const val RECENTS_TAB_MASK = 4
const val ALL_TABS_MASK = 7 const val GROUPS_TAB_MASK = 8
const val ALL_TABS_MASK = 15
val tabsList = arrayListOf(CONTACTS_TAB_MASK,
FAVORITES_TAB_MASK,
RECENTS_TAB_MASK,
GROUPS_TAB_MASK
)
// contact photo changes // contact photo changes
const val PHOTO_ADDED = 1 const val PHOTO_ADDED = 1
@ -36,25 +52,6 @@ const val PHOTO_REMOVED = 2
const val PHOTO_CHANGED = 3 const val PHOTO_CHANGED = 3
const val PHOTO_UNCHANGED = 4 const val PHOTO_UNCHANGED = 4
// export/import
const val BEGIN_VCARD = "BEGIN:VCARD"
const val END_VCARD = "END:VCARD"
const val N = "N"
const val TEL = "TEL"
const val BDAY = "BDAY:"
const val ANNIVERSARY = "ANNIVERSARY:"
const val PHOTO = "PHOTO"
const val EMAIL = "EMAIL"
const val ADR = "ADR"
const val NOTE = "NOTE:"
const val ORG = "ORG:"
const val TITLE = "TITLE:"
const val URL = "URL:"
const val ENCODING = "ENCODING"
const val BASE64 = "BASE64"
const val JPEG = "JPEG"
const val VERSION_2_1 = "VERSION:2.1"
// phone number/email types // phone number/email types
const val CELL = "CELL" const val CELL = "CELL"
const val WORK = "WORK" const val WORK = "WORK"
@ -66,7 +63,6 @@ const val WORK_FAX = "WORK;FAX"
const val HOME_FAX = "HOME;FAX" const val HOME_FAX = "HOME;FAX"
const val PAGER = "PAGER" const val PAGER = "PAGER"
const val MOBILE = "MOBILE" const val MOBILE = "MOBILE"
const val VOICE = "VOICE"
const val ON_CLICK_CALL_CONTACT = 1 const val ON_CLICK_CALL_CONTACT = 1
const val ON_CLICK_VIEW_CONTACT = 2 const val ON_CLICK_VIEW_CONTACT = 2
@ -87,6 +83,7 @@ const val SHOW_ORGANIZATION_FIELD = 1024
const val SHOW_GROUPS_FIELD = 2048 const val SHOW_GROUPS_FIELD = 2048
const val SHOW_CONTACT_SOURCE_FIELD = 4096 const val SHOW_CONTACT_SOURCE_FIELD = 4096
const val SHOW_WEBSITES_FIELD = 8192 const val SHOW_WEBSITES_FIELD = 8192
const val SHOW_NICKNAME_FIELD = 16384
const val DEFAULT_EMAIL_TYPE = CommonDataKinds.Email.TYPE_HOME const val DEFAULT_EMAIL_TYPE = CommonDataKinds.Email.TYPE_HOME
const val DEFAULT_PHONE_NUMBER_TYPE = CommonDataKinds.Phone.TYPE_MOBILE const val DEFAULT_PHONE_NUMBER_TYPE = CommonDataKinds.Phone.TYPE_MOBILE

View File

@ -1,26 +1,29 @@
package com.simplemobiletools.contacts.helpers package com.simplemobiletools.contacts.helpers
import android.accounts.Account
import android.accounts.AccountManager import android.accounts.AccountManager
import android.annotation.SuppressLint
import android.app.Activity import android.app.Activity
import android.content.* import android.content.*
import android.database.Cursor import android.database.Cursor
import android.graphics.Bitmap import android.graphics.Bitmap
import android.net.Uri import android.net.Uri
import android.provider.CallLog
import android.provider.ContactsContract import android.provider.ContactsContract
import android.provider.ContactsContract.CommonDataKinds import android.provider.ContactsContract.CommonDataKinds
import android.provider.ContactsContract.CommonDataKinds.Nickname
import android.provider.ContactsContract.CommonDataKinds.Note import android.provider.ContactsContract.CommonDataKinds.Note
import android.provider.MediaStore import android.provider.MediaStore
import android.text.TextUtils import android.text.TextUtils
import android.util.SparseArray import android.util.SparseArray
import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.SORT_BY_FIRST_NAME import com.simplemobiletools.commons.helpers.*
import com.simplemobiletools.commons.helpers.SORT_BY_MIDDLE_NAME
import com.simplemobiletools.commons.helpers.SORT_BY_SURNAME
import com.simplemobiletools.commons.helpers.SORT_DESCENDING
import com.simplemobiletools.contacts.R import com.simplemobiletools.contacts.R
import com.simplemobiletools.contacts.extensions.* import com.simplemobiletools.contacts.extensions.*
import com.simplemobiletools.contacts.models.* import com.simplemobiletools.contacts.models.*
import com.simplemobiletools.contacts.overloads.times import com.simplemobiletools.contacts.overloads.times
import java.text.SimpleDateFormat
import java.util.*
class ContactsHelper(val activity: Activity) { class ContactsHelper(val activity: Activity) {
private val BATCH_SIZE = 100 private val BATCH_SIZE = 100
@ -61,6 +64,33 @@ class ContactsHelper(val activity: Activity) {
}.start() }.start()
} }
private fun getContentResolverAccounts(): HashSet<ContactSource> {
val uri = ContactsContract.Data.CONTENT_URI
val projection = arrayOf(
ContactsContract.RawContacts.ACCOUNT_NAME,
ContactsContract.RawContacts.ACCOUNT_TYPE
)
val sources = HashSet<ContactSource>()
var cursor: Cursor? = null
try {
cursor = activity.contentResolver.query(uri, projection, null, null, null)
if (cursor?.moveToFirst() == true) {
do {
val name = cursor.getStringValue(ContactsContract.RawContacts.ACCOUNT_NAME) ?: ""
val type = cursor.getStringValue(ContactsContract.RawContacts.ACCOUNT_TYPE) ?: ""
val source = ContactSource(name, type)
sources.add(source)
} while (cursor.moveToNext())
}
} catch (e: Exception) {
} finally {
cursor?.close()
}
return sources
}
private fun getDeviceContacts(contacts: SparseArray<Contact>) { private fun getDeviceContacts(contacts: SparseArray<Contact>) {
if (!activity.hasContactPermissions()) { if (!activity.hasContactPermissions()) {
return return
@ -83,6 +113,7 @@ class ContactsHelper(val activity: Activity) {
val middleName = cursor.getStringValue(CommonDataKinds.StructuredName.MIDDLE_NAME) ?: "" val middleName = cursor.getStringValue(CommonDataKinds.StructuredName.MIDDLE_NAME) ?: ""
val surname = cursor.getStringValue(CommonDataKinds.StructuredName.FAMILY_NAME) ?: "" val surname = cursor.getStringValue(CommonDataKinds.StructuredName.FAMILY_NAME) ?: ""
val suffix = cursor.getStringValue(CommonDataKinds.StructuredName.SUFFIX) ?: "" val suffix = cursor.getStringValue(CommonDataKinds.StructuredName.SUFFIX) ?: ""
val nickname = ""
val photoUri = cursor.getStringValue(CommonDataKinds.StructuredName.PHOTO_URI) ?: "" val photoUri = cursor.getStringValue(CommonDataKinds.StructuredName.PHOTO_URI) ?: ""
val number = ArrayList<PhoneNumber>() // proper value is obtained below val number = ArrayList<PhoneNumber>() // proper value is obtained below
val emails = ArrayList<Email>() val emails = ArrayList<Email>()
@ -96,8 +127,8 @@ class ContactsHelper(val activity: Activity) {
val groups = ArrayList<Group>() val groups = ArrayList<Group>()
val organization = Organization("", "") val organization = Organization("", "")
val websites = ArrayList<String>() val websites = ArrayList<String>()
val contact = Contact(id, prefix, firstName, middleName, surname, suffix, photoUri, number, emails, addresses, events, val contact = Contact(id, prefix, firstName, middleName, surname, suffix, nickname, photoUri, number, emails, addresses,
accountName, starred, contactId, thumbnailUri, null, notes, groups, organization, websites) events, accountName, starred, contactId, thumbnailUri, null, notes, groups, organization, websites)
contacts.put(id, contact) contacts.put(id, contact)
} while (cursor.moveToNext()) } while (cursor.moveToNext())
@ -115,6 +146,13 @@ class ContactsHelper(val activity: Activity) {
contacts[key]?.phoneNumbers = phoneNumbers.valueAt(i) contacts[key]?.phoneNumbers = phoneNumbers.valueAt(i)
} }
val nicknames = getNicknames()
size = nicknames.size()
for (i in 0 until size) {
val key = nicknames.keyAt(i)
contacts[key]?.nickname = nicknames.valueAt(i)
}
val emails = getEmails() val emails = getEmails()
size = emails.size() size = emails.size()
for (i in 0 until size) { for (i in 0 until size) {
@ -196,6 +234,36 @@ class ContactsHelper(val activity: Activity) {
return phoneNumbers return phoneNumbers
} }
private fun getNicknames(contactId: Int? = null): SparseArray<String> {
val nicknames = SparseArray<String>()
val uri = ContactsContract.Data.CONTENT_URI
val projection = arrayOf(
ContactsContract.Data.RAW_CONTACT_ID,
Nickname.NAME
)
val selection = getSourcesSelection(true, contactId != null)
val selectionArgs = getSourcesSelectionArgs(Nickname.CONTENT_ITEM_TYPE, contactId)
var cursor: Cursor? = null
try {
cursor = activity.contentResolver.query(uri, projection, selection, selectionArgs, null)
if (cursor?.moveToFirst() == true) {
do {
val id = cursor.getIntValue(ContactsContract.Data.RAW_CONTACT_ID)
val nickname = cursor.getStringValue(Nickname.NAME) ?: continue
nicknames.put(id, nickname)
} while (cursor.moveToNext())
}
} catch (e: Exception) {
activity.showErrorToast(e)
} finally {
cursor?.close()
}
return nicknames
}
private fun getEmails(contactId: Int? = null): SparseArray<ArrayList<Email>> { private fun getEmails(contactId: Int? = null): SparseArray<ArrayList<Email>> {
val emails = SparseArray<ArrayList<Email>>() val emails = SparseArray<ArrayList<Email>>()
val uri = CommonDataKinds.Email.CONTENT_URI val uri = CommonDataKinds.Email.CONTENT_URI
@ -251,7 +319,7 @@ class ContactsHelper(val activity: Activity) {
if (cursor?.moveToFirst() == true) { if (cursor?.moveToFirst() == true) {
do { do {
val id = cursor.getIntValue(ContactsContract.Data.RAW_CONTACT_ID) val id = cursor.getIntValue(ContactsContract.Data.RAW_CONTACT_ID)
val address = cursor.getStringValue(CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS) ?: "" val address = cursor.getStringValue(CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS) ?: continue
val type = cursor.getIntValue(CommonDataKinds.StructuredPostal.TYPE) val type = cursor.getIntValue(CommonDataKinds.StructuredPostal.TYPE)
if (addresses[id] == null) { if (addresses[id] == null) {
@ -324,7 +392,7 @@ class ContactsHelper(val activity: Activity) {
if (cursor?.moveToFirst() == true) { if (cursor?.moveToFirst() == true) {
do { do {
val id = cursor.getIntValue(ContactsContract.Data.RAW_CONTACT_ID) val id = cursor.getIntValue(ContactsContract.Data.RAW_CONTACT_ID)
val note = cursor.getStringValue(CommonDataKinds.Note.NOTE) ?: continue val note = cursor.getStringValue(Note.NOTE) ?: continue
notes.put(id, note) notes.put(id, note)
} while (cursor.moveToNext()) } while (cursor.moveToNext())
} }
@ -355,8 +423,12 @@ class ContactsHelper(val activity: Activity) {
if (cursor?.moveToFirst() == true) { if (cursor?.moveToFirst() == true) {
do { do {
val id = cursor.getIntValue(ContactsContract.Data.RAW_CONTACT_ID) val id = cursor.getIntValue(ContactsContract.Data.RAW_CONTACT_ID)
val company = cursor.getStringValue(CommonDataKinds.Organization.COMPANY) ?: continue val company = cursor.getStringValue(CommonDataKinds.Organization.COMPANY) ?: ""
val title = cursor.getStringValue(CommonDataKinds.Organization.TITLE) ?: continue val title = cursor.getStringValue(CommonDataKinds.Organization.TITLE) ?: ""
if (company.isEmpty() && title.isEmpty()) {
continue
}
val organization = Organization(company, title) val organization = Organization(company, title)
organizations.put(id, organization) organizations.put(id, organization)
} while (cursor.moveToNext()) } while (cursor.moveToNext())
@ -621,6 +693,7 @@ class ContactsHelper(val activity: Activity) {
val middleName = cursor.getStringValue(CommonDataKinds.StructuredName.MIDDLE_NAME) ?: "" val middleName = cursor.getStringValue(CommonDataKinds.StructuredName.MIDDLE_NAME) ?: ""
val surname = cursor.getStringValue(CommonDataKinds.StructuredName.FAMILY_NAME) ?: "" val surname = cursor.getStringValue(CommonDataKinds.StructuredName.FAMILY_NAME) ?: ""
val suffix = cursor.getStringValue(CommonDataKinds.StructuredName.SUFFIX) ?: "" val suffix = cursor.getStringValue(CommonDataKinds.StructuredName.SUFFIX) ?: ""
val nickname = getNicknames(id)[id] ?: ""
val photoUri = cursor.getStringValue(CommonDataKinds.Phone.PHOTO_URI) ?: "" val photoUri = cursor.getStringValue(CommonDataKinds.Phone.PHOTO_URI) ?: ""
val number = getPhoneNumbers(id)[id] ?: ArrayList() val number = getPhoneNumbers(id)[id] ?: ArrayList()
val emails = getEmails(id)[id] ?: ArrayList() val emails = getEmails(id)[id] ?: ArrayList()
@ -634,8 +707,8 @@ class ContactsHelper(val activity: Activity) {
val thumbnailUri = cursor.getStringValue(CommonDataKinds.StructuredName.PHOTO_THUMBNAIL_URI) ?: "" val thumbnailUri = cursor.getStringValue(CommonDataKinds.StructuredName.PHOTO_THUMBNAIL_URI) ?: ""
val organization = getOrganizations(id)[id] ?: Organization("", "") val organization = getOrganizations(id)[id] ?: Organization("", "")
val websites = getWebsites(id)[id] ?: ArrayList() val websites = getWebsites(id)[id] ?: ArrayList()
return Contact(id, prefix, firstName, middleName, surname, suffix, photoUri, number, emails, addresses, events, accountName, return Contact(id, prefix, firstName, middleName, surname, suffix, nickname, photoUri, number, emails, addresses, events,
starred, contactId, thumbnailUri, null, notes, groups, organization, websites) accountName, starred, contactId, thumbnailUri, null, notes, groups, organization, websites)
} }
} finally { } finally {
cursor?.close() cursor?.close()
@ -662,14 +735,19 @@ class ContactsHelper(val activity: Activity) {
return sources return sources
} }
val accountManager = AccountManager.get(activity) val accounts = AccountManager.get(activity).accounts
accountManager.accounts.filter { it.name.contains("@") || localAccountTypes.contains(it.type) }.forEach { accounts.forEach {
if (ContentResolver.getIsSyncable(it, ContactsContract.AUTHORITY) == 1) { if (ContentResolver.getIsSyncable(it, ContactsContract.AUTHORITY) == 1) {
val contactSource = ContactSource(it.name, it.type) val contactSource = ContactSource(it.name, it.type)
sources.add(contactSource) sources.add(contactSource)
} }
} }
val contentResolverAccounts = getContentResolverAccounts().filter {
it.name.isNotEmpty() && it.type.isNotEmpty() && !accounts.contains(Account(it.name, it.type))
}
sources.addAll(contentResolverAccounts)
if (sources.isEmpty() && activity.config.localAccountName.isEmpty() && activity.config.localAccountType.isEmpty()) { if (sources.isEmpty() && activity.config.localAccountName.isEmpty() && activity.config.localAccountType.isEmpty()) {
sources.add(ContactSource("", "")) sources.add(ContactSource("", ""))
} }
@ -747,6 +825,22 @@ class ContactsHelper(val activity: Activity) {
operations.add(build()) operations.add(build())
} }
// delete nickname
ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI).apply {
val selection = "${ContactsContract.Data.RAW_CONTACT_ID} = ? AND ${ContactsContract.Data.MIMETYPE} = ? "
val selectionArgs = arrayOf(contact.id.toString(), Nickname.CONTENT_ITEM_TYPE)
withSelection(selection, selectionArgs)
operations.add(build())
}
// add nickname
ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
withValue(ContactsContract.Data.RAW_CONTACT_ID, contact.id)
withValue(ContactsContract.Data.MIMETYPE, Nickname.CONTENT_ITEM_TYPE)
withValue(Nickname.NAME, contact.nickname)
operations.add(build())
}
// delete phone numbers // delete phone numbers
ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI).apply { ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI).apply {
val selection = "${ContactsContract.Data.RAW_CONTACT_ID} = ? AND ${ContactsContract.Data.MIMETYPE} = ? " val selection = "${ContactsContract.Data.RAW_CONTACT_ID} = ? AND ${ContactsContract.Data.MIMETYPE} = ? "
@ -823,20 +917,34 @@ class ContactsHelper(val activity: Activity) {
} }
} }
// notes // delete notes
ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI).apply { ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI).apply {
val selection = "${ContactsContract.Data.RAW_CONTACT_ID} = ? AND ${ContactsContract.Data.MIMETYPE} = ?" val selection = "${ContactsContract.Data.RAW_CONTACT_ID} = ? AND ${ContactsContract.Data.MIMETYPE} = ? "
val selectionArgs = arrayOf(contact.id.toString(), Note.CONTENT_ITEM_TYPE) val selectionArgs = arrayOf(contact.id.toString(), Note.CONTENT_ITEM_TYPE)
withSelection(selection, selectionArgs) withSelection(selection, selectionArgs)
operations.add(build())
}
// add notes
ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
withValue(ContactsContract.Data.RAW_CONTACT_ID, contact.id)
withValue(ContactsContract.Data.MIMETYPE, Note.CONTENT_ITEM_TYPE)
withValue(Note.NOTE, contact.notes) withValue(Note.NOTE, contact.notes)
operations.add(build()) operations.add(build())
} }
// organization // delete organization
ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI).apply { ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI).apply {
val selection = "${ContactsContract.Data.RAW_CONTACT_ID} = ? AND ${ContactsContract.Data.MIMETYPE} = ?" val selection = "${ContactsContract.Data.RAW_CONTACT_ID} = ? AND ${ContactsContract.Data.MIMETYPE} = ? "
val selectionArgs = arrayOf(contact.id.toString(), CommonDataKinds.Organization.CONTENT_ITEM_TYPE) val selectionArgs = arrayOf(contact.id.toString(), CommonDataKinds.Organization.CONTENT_ITEM_TYPE)
withSelection(selection, selectionArgs) withSelection(selection, selectionArgs)
operations.add(build())
}
// add organization
ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
withValue(ContactsContract.Data.RAW_CONTACT_ID, contact.id)
withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Organization.CONTENT_ITEM_TYPE)
withValue(CommonDataKinds.Organization.COMPANY, contact.organization.company) withValue(CommonDataKinds.Organization.COMPANY, contact.organization.company)
withValue(CommonDataKinds.Organization.TYPE, DEFAULT_ORGANIZATION_TYPE) withValue(CommonDataKinds.Organization.TYPE, DEFAULT_ORGANIZATION_TYPE)
withValue(CommonDataKinds.Organization.TITLE, contact.organization.jobPosition) withValue(CommonDataKinds.Organization.TITLE, contact.organization.jobPosition)
@ -960,7 +1068,12 @@ class ContactsHelper(val activity: Activity) {
operations.clear() operations.clear()
} }
} }
activity.contentResolver.applyBatch(ContactsContract.AUTHORITY, operations)
try {
activity.contentResolver.applyBatch(ContactsContract.AUTHORITY, operations)
} catch (e: Exception) {
activity.showErrorToast(e)
}
} }
fun removeContactsFromGroup(contacts: ArrayList<Contact>, groupId: Long) { fun removeContactsFromGroup(contacts: ArrayList<Contact>, groupId: Long) {
@ -1006,6 +1119,14 @@ class ContactsHelper(val activity: Activity) {
operations.add(build()) operations.add(build())
} }
// nickname
ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
withValue(ContactsContract.Data.MIMETYPE, Nickname.CONTENT_ITEM_TYPE)
withValue(Nickname.NAME, contact.nickname)
operations.add(build())
}
// phone numbers // phone numbers
contact.phoneNumbers.forEach { contact.phoneNumbers.forEach {
ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply { ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI).apply {
@ -1262,7 +1383,92 @@ class ContactsHelper(val activity: Activity) {
} }
} }
activity.contentResolver.applyBatch(ContactsContract.AUTHORITY, operations) if (activity.hasPermission(PERMISSION_WRITE_CONTACTS)) {
activity.contentResolver.applyBatch(ContactsContract.AUTHORITY, operations)
}
} catch (e: Exception) {
activity.showErrorToast(e)
}
}.start()
}
@SuppressLint("MissingPermission")
fun getRecents(callback: (ArrayList<RecentCall>) -> Unit) {
Thread {
val calls = ArrayList<RecentCall>()
if (!activity.hasPermission(PERMISSION_WRITE_CALL_LOG) || !activity.hasPermission(PERMISSION_READ_CALL_LOG)) {
callback(calls)
return@Thread
}
val uri = CallLog.Calls.CONTENT_URI
val projection = arrayOf(
CallLog.Calls._ID,
CallLog.Calls.NUMBER,
CallLog.Calls.DATE,
CallLog.Calls.CACHED_NAME
)
val sorting = "${CallLog.Calls._ID} DESC LIMIT 100"
val currentDate = Date(System.currentTimeMillis())
val currentYear = SimpleDateFormat("yyyy", Locale.getDefault()).format(currentDate)
val todayDate = SimpleDateFormat("dd MMM yyyy", Locale.getDefault()).format(currentDate)
val yesterdayDate = SimpleDateFormat("dd MMM yyyy", Locale.getDefault()).format(Date(System.currentTimeMillis() - DAY_SECONDS * 1000))
val yesterday = activity.getString(R.string.yesterday)
var prevNumber = ""
var cursor: Cursor? = null
try {
cursor = activity.contentResolver.query(uri, projection, null, null, sorting)
if (cursor?.moveToFirst() == true) {
do {
val id = cursor.getIntValue(CallLog.Calls._ID)
val number = cursor.getStringValue(CallLog.Calls.NUMBER)
val date = cursor.getLongValue(CallLog.Calls.DATE)
val name = cursor.getStringValue(CallLog.Calls.CACHED_NAME)
if (number == prevNumber) {
continue
}
var formattedDate = SimpleDateFormat("dd MMM yyyy, HH:mm", Locale.getDefault()).format(Date(date))
val datePart = formattedDate.substring(0, 11)
when {
datePart == todayDate -> formattedDate = formattedDate.substring(12)
datePart == yesterdayDate -> formattedDate = yesterday + formattedDate.substring(11)
formattedDate.substring(7, 11) == currentYear -> formattedDate = formattedDate.substring(0, 6) + formattedDate.substring(11)
}
prevNumber = number
val recentCall = RecentCall(id, number, formattedDate, name)
calls.add(recentCall)
} while (cursor.moveToNext())
}
} finally {
cursor?.close()
}
callback(calls)
}.start()
}
fun removeRecentCalls(ids: ArrayList<Int>) {
Thread {
try {
val operations = ArrayList<ContentProviderOperation>()
val selection = "${CallLog.Calls._ID} = ?"
ids.forEach {
ContentProviderOperation.newDelete(CallLog.Calls.CONTENT_URI).apply {
val selectionArgs = arrayOf(it.toString())
withSelection(selection, selectionArgs)
operations.add(build())
}
if (operations.size % BATCH_SIZE == 0) {
activity.contentResolver.applyBatch(CallLog.AUTHORITY, operations)
operations.clear()
}
}
activity.contentResolver.applyBatch(CallLog.AUTHORITY, operations)
} catch (e: Exception) { } catch (e: Exception) {
activity.showErrorToast(e) activity.showErrorToast(e)
} }

View File

@ -28,6 +28,7 @@ class DBHelper private constructor(val context: Context) : SQLiteOpenHelper(cont
private val COL_MIDDLE_NAME = "middle_name" private val COL_MIDDLE_NAME = "middle_name"
private val COL_SURNAME = "surname" private val COL_SURNAME = "surname"
private val COL_SUFFIX = "suffix" private val COL_SUFFIX = "suffix"
private val COL_NICKNAME = "nickname"
private val COL_PHOTO = "photo" private val COL_PHOTO = "photo"
private val COL_PHONE_NUMBERS = "phone_numbers" private val COL_PHONE_NUMBERS = "phone_numbers"
private val COL_EMAILS = "emails" private val COL_EMAILS = "emails"
@ -48,9 +49,9 @@ class DBHelper private constructor(val context: Context) : SQLiteOpenHelper(cont
private val mDb = writableDatabase private val mDb = writableDatabase
companion object { companion object {
private const val DB_VERSION = 5
const val DB_NAME = "contacts.db" const val DB_NAME = "contacts.db"
var dbInstance: DBHelper? = null private const val DB_VERSION = 6
private var dbInstance: DBHelper? = null
var gson = Gson() var gson = Gson()
fun newInstance(context: Context): DBHelper { fun newInstance(context: Context): DBHelper {
@ -65,7 +66,7 @@ class DBHelper private constructor(val context: Context) : SQLiteOpenHelper(cont
db.execSQL("CREATE TABLE $CONTACTS_TABLE_NAME ($COL_ID INTEGER PRIMARY KEY AUTOINCREMENT, $COL_FIRST_NAME TEXT, $COL_MIDDLE_NAME TEXT, " + db.execSQL("CREATE TABLE $CONTACTS_TABLE_NAME ($COL_ID INTEGER PRIMARY KEY AUTOINCREMENT, $COL_FIRST_NAME TEXT, $COL_MIDDLE_NAME TEXT, " +
"$COL_SURNAME TEXT, $COL_PHOTO BLOB, $COL_PHONE_NUMBERS TEXT, $COL_EMAILS TEXT, $COL_EVENTS TEXT, $COL_STARRED INTEGER, " + "$COL_SURNAME TEXT, $COL_PHOTO BLOB, $COL_PHONE_NUMBERS TEXT, $COL_EMAILS TEXT, $COL_EVENTS TEXT, $COL_STARRED INTEGER, " +
"$COL_ADDRESSES TEXT, $COL_NOTES TEXT, $COL_GROUPS TEXT, $COL_PREFIX TEXT, $COL_SUFFIX TEXT, $COL_COMPANY TEXT, $COL_JOB_POSITION TEXT," + "$COL_ADDRESSES TEXT, $COL_NOTES TEXT, $COL_GROUPS TEXT, $COL_PREFIX TEXT, $COL_SUFFIX TEXT, $COL_COMPANY TEXT, $COL_JOB_POSITION TEXT," +
"$COL_WEBSITES TEXT)") "$COL_WEBSITES TEXT, $COL_NICKNAME TEXT)")
// start autoincrement ID from FIRST_CONTACT_ID to avoid conflicts // start autoincrement ID from FIRST_CONTACT_ID to avoid conflicts
db.execSQL("REPLACE INTO sqlite_sequence (name, seq) VALUES ('$CONTACTS_TABLE_NAME', $FIRST_CONTACT_ID)") db.execSQL("REPLACE INTO sqlite_sequence (name, seq) VALUES ('$CONTACTS_TABLE_NAME', $FIRST_CONTACT_ID)")
@ -94,6 +95,10 @@ class DBHelper private constructor(val context: Context) : SQLiteOpenHelper(cont
if (oldVersion < 5) { if (oldVersion < 5) {
db.execSQL("ALTER TABLE $CONTACTS_TABLE_NAME ADD COLUMN $COL_WEBSITES TEXT DEFAULT ''") db.execSQL("ALTER TABLE $CONTACTS_TABLE_NAME ADD COLUMN $COL_WEBSITES TEXT DEFAULT ''")
} }
if (oldVersion < 6) {
db.execSQL("ALTER TABLE $CONTACTS_TABLE_NAME ADD COLUMN $COL_NICKNAME TEXT DEFAULT ''")
}
} }
private fun createGroupsTable(db: SQLiteDatabase) { private fun createGroupsTable(db: SQLiteDatabase) {
@ -135,6 +140,7 @@ class DBHelper private constructor(val context: Context) : SQLiteOpenHelper(cont
put(COL_MIDDLE_NAME, contact.middleName) put(COL_MIDDLE_NAME, contact.middleName)
put(COL_SURNAME, contact.surname) put(COL_SURNAME, contact.surname)
put(COL_SUFFIX, contact.suffix) put(COL_SUFFIX, contact.suffix)
put(COL_NICKNAME, contact.nickname)
put(COL_PHONE_NUMBERS, gson.toJson(contact.phoneNumbers)) put(COL_PHONE_NUMBERS, gson.toJson(contact.phoneNumbers))
put(COL_EMAILS, gson.toJson(contact.emails)) put(COL_EMAILS, gson.toJson(contact.emails))
put(COL_ADDRESSES, gson.toJson(contact.addresses)) put(COL_ADDRESSES, gson.toJson(contact.addresses))
@ -252,8 +258,8 @@ class DBHelper private constructor(val context: Context) : SQLiteOpenHelper(cont
fun getContacts(activity: Activity, selection: String? = null, selectionArgs: Array<String>? = null): ArrayList<Contact> { fun getContacts(activity: Activity, selection: String? = null, selectionArgs: Array<String>? = null): ArrayList<Contact> {
val storedGroups = ContactsHelper(activity).getStoredGroups() val storedGroups = ContactsHelper(activity).getStoredGroups()
val contacts = ArrayList<Contact>() val contacts = ArrayList<Contact>()
val projection = arrayOf(COL_ID, COL_PREFIX, COL_FIRST_NAME, COL_MIDDLE_NAME, COL_SURNAME, COL_SUFFIX, COL_PHONE_NUMBERS, COL_EMAILS, val projection = arrayOf(COL_ID, COL_PREFIX, COL_FIRST_NAME, COL_MIDDLE_NAME, COL_SURNAME, COL_SUFFIX, COL_NICKNAME, COL_PHONE_NUMBERS,
COL_EVENTS, COL_STARRED, COL_PHOTO, COL_ADDRESSES, COL_NOTES, COL_GROUPS, COL_COMPANY, COL_JOB_POSITION, COL_WEBSITES) COL_EMAILS, COL_EVENTS, COL_STARRED, COL_PHOTO, COL_ADDRESSES, COL_NOTES, COL_GROUPS, COL_COMPANY, COL_JOB_POSITION, COL_WEBSITES)
val phoneNumbersToken = object : TypeToken<List<PhoneNumber>>() {}.type val phoneNumbersToken = object : TypeToken<List<PhoneNumber>>() {}.type
val emailsToken = object : TypeToken<List<Email>>() {}.type val emailsToken = object : TypeToken<List<Email>>() {}.type
@ -271,6 +277,7 @@ class DBHelper private constructor(val context: Context) : SQLiteOpenHelper(cont
val middleName = cursor.getStringValue(COL_MIDDLE_NAME) val middleName = cursor.getStringValue(COL_MIDDLE_NAME)
val surname = cursor.getStringValue(COL_SURNAME) val surname = cursor.getStringValue(COL_SURNAME)
val suffix = cursor.getStringValue(COL_SUFFIX) val suffix = cursor.getStringValue(COL_SUFFIX)
val nickname = cursor.getStringValue(COL_NICKNAME)
val phoneNumbersJson = cursor.getStringValue(COL_PHONE_NUMBERS) val phoneNumbersJson = cursor.getStringValue(COL_PHONE_NUMBERS)
val phoneNumbers = if (phoneNumbersJson == "[]") ArrayList() else gson.fromJson<ArrayList<PhoneNumber>>(phoneNumbersJson, phoneNumbersToken) val phoneNumbers = if (phoneNumbersJson == "[]") ArrayList() else gson.fromJson<ArrayList<PhoneNumber>>(phoneNumbersJson, phoneNumbersToken)
@ -290,7 +297,11 @@ class DBHelper private constructor(val context: Context) : SQLiteOpenHelper(cont
val photoByteArray = cursor.getBlobValue(COL_PHOTO) ?: null val photoByteArray = cursor.getBlobValue(COL_PHOTO) ?: null
val photo = if (photoByteArray?.isNotEmpty() == true) { val photo = if (photoByteArray?.isNotEmpty() == true) {
BitmapFactory.decodeByteArray(photoByteArray, 0, photoByteArray.size) try {
BitmapFactory.decodeByteArray(photoByteArray, 0, photoByteArray.size)
} catch (e: OutOfMemoryError) {
null
}
} else { } else {
null null
} }
@ -311,8 +322,8 @@ class DBHelper private constructor(val context: Context) : SQLiteOpenHelper(cont
val websites = if (websitesJson == "[]") ArrayList() else gson.fromJson<ArrayList<String>>(websitesJson, websitesToken) val websites = if (websitesJson == "[]") ArrayList() else gson.fromJson<ArrayList<String>>(websitesJson, websitesToken)
?: ArrayList(1) ?: ArrayList(1)
val contact = Contact(id, prefix, firstName, middleName, surname, suffix, "", phoneNumbers, emails, addresses, events, val contact = Contact(id, prefix, firstName, middleName, surname, suffix, nickname, "", phoneNumbers, emails, addresses,
SMT_PRIVATE, starred, id, "", photo, notes, groups, organization, websites) events, SMT_PRIVATE, starred, id, "", photo, notes, groups, organization, websites)
contacts.add(contact) contacts.add(contact)
} }
} }

View File

@ -1,22 +1,30 @@
package com.simplemobiletools.contacts.helpers package com.simplemobiletools.contacts.helpers
import android.graphics.Bitmap
import android.net.Uri import android.net.Uri
import android.provider.ContactsContract.CommonDataKinds import android.provider.ContactsContract.CommonDataKinds
import android.provider.MediaStore import android.provider.MediaStore
import android.util.Base64
import com.simplemobiletools.commons.activities.BaseSimpleActivity import com.simplemobiletools.commons.activities.BaseSimpleActivity
import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.extensions.getFileOutputStream
import com.simplemobiletools.commons.extensions.showErrorToast
import com.simplemobiletools.commons.extensions.toFileDirItem
import com.simplemobiletools.commons.extensions.toast
import com.simplemobiletools.contacts.R import com.simplemobiletools.contacts.R
import com.simplemobiletools.contacts.extensions.getByteArray
import com.simplemobiletools.contacts.extensions.getDateTimeFromDateString
import com.simplemobiletools.contacts.helpers.VcfExporter.ExportResult.EXPORT_FAIL import com.simplemobiletools.contacts.helpers.VcfExporter.ExportResult.EXPORT_FAIL
import com.simplemobiletools.contacts.models.Contact import com.simplemobiletools.contacts.models.Contact
import java.io.BufferedWriter import ezvcard.Ezvcard
import java.io.ByteArrayOutputStream import ezvcard.VCard
import ezvcard.parameter.AddressType
import ezvcard.parameter.EmailType
import ezvcard.parameter.ImageType
import ezvcard.parameter.TelephoneType
import ezvcard.property.*
import ezvcard.util.PartialDate
import java.io.File import java.io.File
import java.util.*
class VcfExporter { class VcfExporter {
private val ENCODED_PHOTO_LINE_LENGTH = 74
enum class ExportResult { enum class ExportResult {
EXPORT_FAIL, EXPORT_OK, EXPORT_PARTIAL EXPORT_FAIL, EXPORT_OK, EXPORT_PARTIAL
} }
@ -36,61 +44,93 @@ class VcfExporter {
activity.toast(R.string.exporting) activity.toast(R.string.exporting)
} }
it.bufferedWriter().use { out -> val cards = ArrayList<VCard>()
for (contact in contacts) { for (contact in contacts) {
out.writeLn(BEGIN_VCARD) val card = VCard()
out.writeLn(VERSION_2_1) StructuredName().apply {
out.writeLn("$N${getNames(contact)}") prefixes.add(contact.prefix)
given = contact.firstName
additionalNames.add(contact.middleName)
family = contact.surname
suffixes.add(contact.suffix)
card.structuredName = this
}
contact.phoneNumbers.forEach { if (contact.nickname.isNotEmpty()) {
out.writeLn("$TEL;${getPhoneNumberLabel(it.type)}:${it.value}") card.setNickname(contact.nickname)
} }
contact.emails.forEach { contact.phoneNumbers.forEach {
val type = getEmailTypeLabel(it.type) val phoneNumber = Telephone(it.value)
val delimiterType = if (type.isEmpty()) "" else ";$type" phoneNumber.types.add(TelephoneType.find(getPhoneNumberLabel(it.type)))
out.writeLn("$EMAIL$delimiterType:${it.value}") card.addTelephoneNumber(phoneNumber)
} }
contact.addresses.forEach { contact.emails.forEach {
val type = getAddressTypeLabel(it.type) val email = Email(it.value)
val delimiterType = if (type.isEmpty()) "" else ";$type" email.types.add(EmailType.find(getEmailTypeLabel(it.type)))
out.writeLn("$ADR$delimiterType:;;${it.value.replace("\n", "\\n")};;;;") card.addEmail(email)
} }
contact.events.forEach { contact.events.forEach {
if (it.type == CommonDataKinds.Event.TYPE_BIRTHDAY) { if (it.type == CommonDataKinds.Event.TYPE_BIRTHDAY || it.type == CommonDataKinds.Event.TYPE_ANNIVERSARY) {
out.writeLn("$BDAY${it.value}") val dateTime = it.value.getDateTimeFromDateString()
if (it.value.startsWith("--")) {
val partialDate = PartialDate.builder().year(null).month(dateTime.monthOfYear - 1).date(dateTime.dayOfMonth).build()
if (it.type == CommonDataKinds.Event.TYPE_BIRTHDAY) {
card.birthdays.add(Birthday(partialDate))
} else {
card.anniversaries.add(Anniversary(partialDate))
}
} else {
Calendar.getInstance().apply {
clear()
set(Calendar.YEAR, dateTime.year)
set(Calendar.MONTH, dateTime.monthOfYear - 1)
set(Calendar.DAY_OF_MONTH, dateTime.dayOfMonth)
if (it.type == CommonDataKinds.Event.TYPE_BIRTHDAY) {
card.birthdays.add(Birthday(time))
} else {
card.anniversaries.add(Anniversary(time))
}
}
} }
} }
if (contact.notes.isNotEmpty()) {
out.writeLn("$NOTE${contact.notes.replace("\n", "\\n")}")
}
if (!contact.organization.isEmpty()) {
out.writeLn("$ORG${contact.organization.company.replace("\n", "\\n")}")
out.writeLn("$TITLE${contact.organization.jobPosition.replace("\n", "\\n")}")
}
contact.websites.forEach {
out.writeLn("$URL$it")
}
if (contact.thumbnailUri.isNotEmpty()) {
val bitmap = MediaStore.Images.Media.getBitmap(activity.contentResolver, Uri.parse(contact.thumbnailUri))
addBitmap(bitmap, out)
}
if (contact.photo != null) {
addBitmap(contact.photo!!, out)
}
out.writeLn(END_VCARD)
contactsExported++
} }
contact.addresses.forEach {
val address = Address()
address.streetAddress = it.value
address.types.add(AddressType.find(getAddressTypeLabel(it.type)))
card.addAddress(address)
}
if (contact.notes.isNotEmpty()) {
card.addNote(contact.notes)
}
if (!contact.organization.isEmpty()) {
val organization = Organization()
organization.values.add(contact.organization.company)
card.organization = organization
card.titles.add(Title(contact.organization.jobPosition))
}
contact.websites.forEach {
card.addUrl(it)
}
if (contact.thumbnailUri.isNotEmpty()) {
val photoByteArray = MediaStore.Images.Media.getBitmap(activity.contentResolver, Uri.parse(contact.thumbnailUri)).getByteArray()
val photo = Photo(photoByteArray, ImageType.JPEG)
card.addPhoto(photo)
}
cards.add(card)
contactsExported++
} }
Ezvcard.write(cards).go(file)
} catch (e: Exception) { } catch (e: Exception) {
activity.showErrorToast(e) activity.showErrorToast(e)
} }
@ -103,71 +143,24 @@ class VcfExporter {
} }
} }
private fun addBitmap(bitmap: Bitmap, out: BufferedWriter) {
val firstLine = "$PHOTO;$ENCODING=$BASE64;$JPEG:"
val byteArrayOutputStream = ByteArrayOutputStream()
bitmap.compress(Bitmap.CompressFormat.JPEG, 85, byteArrayOutputStream)
bitmap.recycle()
val byteArray = byteArrayOutputStream.toByteArray()
val encoded = Base64.encodeToString(byteArray, Base64.NO_WRAP)
val encodedFirstLineSection = encoded.substring(0, ENCODED_PHOTO_LINE_LENGTH - firstLine.length)
out.writeLn(firstLine + encodedFirstLineSection)
var curStartIndex = encodedFirstLineSection.length
do {
val part = encoded.substring(curStartIndex, Math.min(curStartIndex + ENCODED_PHOTO_LINE_LENGTH - 1, encoded.length))
out.writeLn(" $part")
curStartIndex += ENCODED_PHOTO_LINE_LENGTH - 1
} while (curStartIndex < encoded.length)
out.writeLn("")
}
private fun getNames(contact: Contact): String {
var result = ""
var firstName = contact.firstName
var surName = contact.surname
var middleName = contact.middleName
var prefix = contact.prefix
var suffix = contact.suffix
if (QuotedPrintable.urlEncode(firstName) != firstName
|| QuotedPrintable.urlEncode(surName) != surName
|| QuotedPrintable.urlEncode(middleName) != middleName
|| QuotedPrintable.urlEncode(prefix) != prefix
|| QuotedPrintable.urlEncode(suffix) != suffix) {
firstName = QuotedPrintable.encode(firstName)
surName = QuotedPrintable.encode(surName)
middleName = QuotedPrintable.encode(middleName)
prefix = QuotedPrintable.encode(prefix)
suffix = QuotedPrintable.encode(suffix)
result += ";CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE"
}
return "$result:$surName;$firstName;$middleName;$prefix;$suffix"
}
private fun getPhoneNumberLabel(type: Int) = when (type) { private fun getPhoneNumberLabel(type: Int) = when (type) {
CommonDataKinds.Phone.TYPE_MOBILE -> CELL CommonDataKinds.Phone.TYPE_MOBILE -> CELL
CommonDataKinds.Phone.TYPE_HOME -> HOME
CommonDataKinds.Phone.TYPE_WORK -> WORK CommonDataKinds.Phone.TYPE_WORK -> WORK
CommonDataKinds.Phone.TYPE_MAIN -> PREF CommonDataKinds.Phone.TYPE_MAIN -> PREF
CommonDataKinds.Phone.TYPE_FAX_WORK -> WORK_FAX CommonDataKinds.Phone.TYPE_FAX_WORK -> WORK_FAX
CommonDataKinds.Phone.TYPE_FAX_HOME -> HOME_FAX CommonDataKinds.Phone.TYPE_FAX_HOME -> HOME_FAX
CommonDataKinds.Phone.TYPE_PAGER -> PAGER CommonDataKinds.Phone.TYPE_PAGER -> PAGER
else -> VOICE else -> HOME
} }
private fun getEmailTypeLabel(type: Int) = when (type) { private fun getEmailTypeLabel(type: Int) = when (type) {
CommonDataKinds.Email.TYPE_HOME -> HOME
CommonDataKinds.Email.TYPE_WORK -> WORK CommonDataKinds.Email.TYPE_WORK -> WORK
CommonDataKinds.Email.TYPE_MOBILE -> MOBILE CommonDataKinds.Email.TYPE_MOBILE -> MOBILE
else -> "" else -> HOME
} }
private fun getAddressTypeLabel(type: Int) = when (type) { private fun getAddressTypeLabel(type: Int) = when (type) {
CommonDataKinds.StructuredPostal.TYPE_HOME -> HOME
CommonDataKinds.StructuredPostal.TYPE_WORK -> WORK CommonDataKinds.StructuredPostal.TYPE_WORK -> WORK
else -> "" else -> HOME
} }
} }

View File

@ -3,7 +3,6 @@ package com.simplemobiletools.contacts.helpers
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.provider.ContactsContract.CommonDataKinds import android.provider.ContactsContract.CommonDataKinds
import android.util.Base64
import android.widget.Toast import android.widget.Toast
import com.simplemobiletools.commons.extensions.showErrorToast import com.simplemobiletools.commons.extensions.showErrorToast
import com.simplemobiletools.contacts.activities.SimpleActivity import com.simplemobiletools.contacts.activities.SimpleActivity
@ -11,40 +10,19 @@ import com.simplemobiletools.contacts.extensions.getCachePhoto
import com.simplemobiletools.contacts.extensions.getCachePhotoUri import com.simplemobiletools.contacts.extensions.getCachePhotoUri
import com.simplemobiletools.contacts.helpers.VcfImporter.ImportResult.* import com.simplemobiletools.contacts.helpers.VcfImporter.ImportResult.*
import com.simplemobiletools.contacts.models.* import com.simplemobiletools.contacts.models.*
import ezvcard.Ezvcard
import org.joda.time.DateTime
import org.joda.time.format.DateTimeFormat
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
import java.util.*
class VcfImporter(val activity: SimpleActivity) { class VcfImporter(val activity: SimpleActivity) {
enum class ImportResult { enum class ImportResult {
IMPORT_FAIL, IMPORT_OK, IMPORT_PARTIAL IMPORT_FAIL, IMPORT_OK, IMPORT_PARTIAL
} }
private var curPrefix = "" private val PATTERN = "EEE MMM dd HH:mm:ss 'GMT'ZZ YYYY"
private var curFirstName = ""
private var curMiddleName = ""
private var curSurname = ""
private var curSuffix = ""
private var curPhotoUri = ""
private var curNotes = ""
private var curCompany = ""
private var curJobPosition = ""
private var curPhoneNumbers = ArrayList<PhoneNumber>()
private var curEmails = ArrayList<Email>()
private var curEvents = ArrayList<Event>()
private var curAddresses = ArrayList<Address>()
private var curGroups = ArrayList<Group>()
private var curWebsites = ArrayList<String>()
private var isGettingPhoto = false
private var currentPhotoString = StringBuilder()
private var currentPhotoCompressionFormat = Bitmap.CompressFormat.JPEG
private var isGettingName = false
private var currentNameIsANSI = false
private var currentNameString = StringBuilder()
private var isGettingNotes = false
private var currentNotesSB = StringBuilder()
private var contactsImported = 0 private var contactsImported = 0
private var contactsFailed = 0 private var contactsFailed = 0
@ -57,45 +35,70 @@ class VcfImporter(val activity: SimpleActivity) {
activity.assets.open(path) activity.assets.open(path)
} }
inputStream.bufferedReader().use { val ezContacts = Ezvcard.parse(inputStream).all()
while (true) { for (ezContact in ezContacts) {
val line = it.readLine() ?: break val structuredName = ezContact.structuredName
if (line.trim().isEmpty()) { val prefix = structuredName?.prefixes?.firstOrNull() ?: ""
if (isGettingPhoto) { val firstName = structuredName?.given ?: ""
savePhoto() val middleName = structuredName?.additionalNames?.firstOrNull() ?: ""
isGettingPhoto = false val surname = structuredName?.family ?: ""
} val suffix = structuredName?.suffixes?.firstOrNull() ?: ""
continue val nickname = ezContact.nickname?.values?.firstOrNull() ?: ""
} else if (line.startsWith('\t') && isGettingName) { val photoUri = ""
currentNameString.append(line.trimStart('\t'))
isGettingName = false
parseNames()
} else if (isGettingNotes) {
if (line.startsWith(' ')) {
currentNotesSB.append(line.substring(1))
} else {
curNotes = currentNotesSB.toString().replace("\\n", "\n").replace("\\,", ",")
isGettingNotes = false
}
}
when { val phoneNumbers = ArrayList<PhoneNumber>()
line.toUpperCase() == BEGIN_VCARD -> resetValues() ezContact.telephoneNumbers.forEach {
line.toUpperCase().startsWith(NOTE) -> addNotes(line.substring(NOTE.length)) val type = getPhoneNumberTypeId(it.types.firstOrNull()?.value ?: MOBILE)
line.toUpperCase().startsWith(N) -> addNames(line.substring(N.length)) val number = it.text
line.toUpperCase().startsWith(TEL) -> addPhoneNumber(line.substring(TEL.length)) phoneNumbers.add(PhoneNumber(number, type))
line.toUpperCase().startsWith(EMAIL) -> addEmail(line.substring(EMAIL.length)) }
line.toUpperCase().startsWith(ADR) -> addAddress(line.substring(ADR.length))
line.toUpperCase().startsWith(BDAY) -> addBirthday(line.substring(BDAY.length)) val emails = ArrayList<Email>()
line.toUpperCase().startsWith(ANNIVERSARY) -> addAnniversary(line.substring(ANNIVERSARY.length)) ezContact.emails.forEach {
line.toUpperCase().startsWith(PHOTO) -> addPhoto(line.substring(PHOTO.length)) val type = getEmailTypeId(it.types.firstOrNull()?.value ?: HOME)
line.toUpperCase().startsWith(ORG) -> addCompany(line.substring(ORG.length)) val email = it.value
line.toUpperCase().startsWith(TITLE) -> addJobPosition(line.substring(TITLE.length)) emails.add(Email(email, type))
line.toUpperCase().startsWith(URL) -> addWebsite(line.substring(URL.length)) }
line.toUpperCase() == END_VCARD -> saveContact(targetContactSource)
isGettingPhoto -> currentPhotoString.append(line.trim()) val addresses = ArrayList<Address>()
ezContact.addresses.forEach {
val type = getAddressTypeId(it.types.firstOrNull()?.value ?: HOME)
val address = it.streetAddress
if (address?.isNotEmpty() == true) {
addresses.add(Address(address, type))
} }
} }
val events = ArrayList<Event>()
ezContact.birthdays.forEach {
val event = Event(formatDateToDayCode(it.date), CommonDataKinds.Event.TYPE_BIRTHDAY)
events.add(event)
}
ezContact.anniversaries.forEach {
val event = Event(formatDateToDayCode(it.date), CommonDataKinds.Event.TYPE_ANNIVERSARY)
events.add(event)
}
val starred = 0
val contactId = 0
val notes = ezContact.notes.firstOrNull()?.value ?: ""
val groups = ArrayList<Group>()
val company = ezContact.organization?.values?.firstOrNull() ?: ""
val jobPosition = ezContact.titles?.firstOrNull()?.value ?: ""
val organization = Organization(company, jobPosition)
val websites = ezContact.urls.map { it.value } as ArrayList<String>
val photoData = ezContact.photos.firstOrNull()?.data
val photo = null
val thumbnailUri = savePhoto(photoData)
val contact = Contact(0, prefix, firstName, middleName, surname, suffix, nickname, photoUri, phoneNumbers, emails, addresses, events,
targetContactSource, starred, contactId, thumbnailUri, photo, notes, groups, organization, websites)
if (ContactsHelper(activity).insertContact(contact)) {
contactsImported++
}
} }
} catch (e: Exception) { } catch (e: Exception) {
activity.showErrorToast(e, Toast.LENGTH_LONG) activity.showErrorToast(e, Toast.LENGTH_LONG)
@ -109,188 +112,51 @@ class VcfImporter(val activity: SimpleActivity) {
} }
} }
private fun addNames(names: String) { private fun formatDateToDayCode(date: Date): String {
val parts = names.split(":") val dateTime = DateTime.parse(date.toString(), DateTimeFormat.forPattern(PATTERN))
currentNameIsANSI = parts.first().toUpperCase().contains("QUOTED-PRINTABLE") return dateTime.toString("yyyy-MM-dd")
currentNameString.append(parts[1].trimEnd('='))
if (!isGettingName && currentNameIsANSI && names.endsWith('=')) {
isGettingName = true
} else {
parseNames()
}
} }
private fun parseNames() { private fun getPhoneNumberTypeId(type: String) = when (type.toUpperCase()) {
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 = if (currentNameIsANSI) QuotedPrintable.decode(nameParts[2]) else nameParts[2]
curPrefix = if (currentNameIsANSI) QuotedPrintable.decode(nameParts[3]) else nameParts[3]
curSuffix = if (currentNameIsANSI) QuotedPrintable.decode(nameParts[4]) else nameParts[4]
}
}
private fun addPhoneNumber(phoneNumber: String) {
val phoneParts = phoneNumber.trimStart(';').split(":")
var rawType = phoneParts[0]
var subType = ""
if (rawType.contains('=')) {
val types = rawType.split('=')
if (types.any { it.contains(';') }) {
subType = types[1].split(';')[0]
}
rawType = types.last()
}
val type = getPhoneNumberTypeId(rawType.toUpperCase(), subType)
val value = phoneParts[1]
curPhoneNumbers.add(PhoneNumber(value, type))
}
private fun getPhoneNumberTypeId(type: String, subType: String) = when (type) {
CELL -> CommonDataKinds.Phone.TYPE_MOBILE CELL -> CommonDataKinds.Phone.TYPE_MOBILE
HOME -> CommonDataKinds.Phone.TYPE_HOME HOME -> CommonDataKinds.Phone.TYPE_HOME
WORK -> CommonDataKinds.Phone.TYPE_WORK WORK -> CommonDataKinds.Phone.TYPE_WORK
PREF, MAIN -> CommonDataKinds.Phone.TYPE_MAIN PREF, MAIN -> CommonDataKinds.Phone.TYPE_MAIN
WORK_FAX -> CommonDataKinds.Phone.TYPE_FAX_WORK WORK_FAX -> CommonDataKinds.Phone.TYPE_FAX_WORK
HOME_FAX -> CommonDataKinds.Phone.TYPE_FAX_HOME HOME_FAX -> CommonDataKinds.Phone.TYPE_FAX_HOME
FAX -> if (subType == WORK) CommonDataKinds.Phone.TYPE_FAX_WORK else CommonDataKinds.Phone.TYPE_FAX_HOME FAX -> CommonDataKinds.Phone.TYPE_FAX_WORK
PAGER -> CommonDataKinds.Phone.TYPE_PAGER PAGER -> CommonDataKinds.Phone.TYPE_PAGER
else -> CommonDataKinds.Phone.TYPE_OTHER else -> CommonDataKinds.Phone.TYPE_OTHER
} }
private fun addEmail(email: String) { private fun getEmailTypeId(type: String) = when (type.toUpperCase()) {
val emailParts = email.trimStart(';').split(":")
var rawType = emailParts[0]
if (rawType.contains('=')) {
rawType = rawType.split('=').last()
}
val type = getEmailTypeId(rawType.toUpperCase())
val value = emailParts[1]
curEmails.add(Email(value, type))
}
private fun getEmailTypeId(type: String) = when (type) {
HOME -> CommonDataKinds.Email.TYPE_HOME HOME -> CommonDataKinds.Email.TYPE_HOME
WORK -> CommonDataKinds.Email.TYPE_WORK WORK -> CommonDataKinds.Email.TYPE_WORK
MOBILE -> CommonDataKinds.Email.TYPE_MOBILE MOBILE -> CommonDataKinds.Email.TYPE_MOBILE
else -> CommonDataKinds.Email.TYPE_OTHER else -> CommonDataKinds.Email.TYPE_OTHER
} }
private fun addAddress(address: String) { private fun getAddressTypeId(type: String) = when (type.toUpperCase()) {
val addressParts = address.trimStart(';').split(":")
var rawType = addressParts[0]
if (rawType.contains('=')) {
rawType = rawType.split('=').last()
}
val type = getAddressTypeId(rawType.toUpperCase())
val addresses = addressParts[1].split(";")
if (addresses.size == 7) {
curAddresses.add(Address(addresses[2].replace("\\n", "\n"), type))
}
}
private fun getAddressTypeId(type: String) = when (type) {
HOME -> CommonDataKinds.Email.TYPE_HOME HOME -> CommonDataKinds.Email.TYPE_HOME
WORK -> CommonDataKinds.Email.TYPE_WORK WORK -> CommonDataKinds.Email.TYPE_WORK
else -> CommonDataKinds.Email.TYPE_OTHER else -> CommonDataKinds.Email.TYPE_OTHER
} }
private fun addBirthday(birthday: String) { private fun savePhoto(byteArray: ByteArray?): String {
curEvents.add(Event(birthday, CommonDataKinds.Event.TYPE_BIRTHDAY)) if (byteArray == null) {
} return ""
private fun addAnniversary(anniversary: String) {
curEvents.add(Event(anniversary, CommonDataKinds.Event.TYPE_ANNIVERSARY))
}
private fun addPhoto(photo: String) {
val photoParts = photo.trimStart(';').split(';')
if (photoParts.size == 2) {
val typeParts = photoParts[1].split(':')
currentPhotoCompressionFormat = getPhotoCompressionFormat(typeParts[0])
val encoding = photoParts[0].split('=').last()
if (encoding == BASE64) {
isGettingPhoto = true
currentPhotoString.append(typeParts[1].trim())
}
} }
}
private fun getPhotoCompressionFormat(type: String) = when (type.toLowerCase()) {
"png" -> Bitmap.CompressFormat.PNG
"webp" -> Bitmap.CompressFormat.WEBP
else -> Bitmap.CompressFormat.JPEG
}
private fun savePhoto() {
val file = activity.getCachePhoto() val file = activity.getCachePhoto()
val imageAsBytes = Base64.decode(currentPhotoString.toString().toByteArray(), Base64.DEFAULT) val bitmap = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size)
val bitmap = BitmapFactory.decodeByteArray(imageAsBytes, 0, imageAsBytes.size)
var fileOutputStream: FileOutputStream? = null var fileOutputStream: FileOutputStream? = null
try { try {
fileOutputStream = FileOutputStream(file) fileOutputStream = FileOutputStream(file)
bitmap.compress(currentPhotoCompressionFormat, 100, fileOutputStream) bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fileOutputStream)
} finally { } finally {
fileOutputStream?.close() fileOutputStream?.close()
} }
curPhotoUri = activity.getCachePhotoUri(file).toString() return activity.getCachePhotoUri(file).toString()
}
private fun addNotes(notes: String) {
currentNotesSB.append(notes)
isGettingNotes = true
}
private fun addCompany(company: String) {
curCompany = company
}
private fun addJobPosition(jobPosition: String) {
curJobPosition = jobPosition
}
private fun addWebsite(website: String) {
curWebsites.add(website)
}
private fun saveContact(source: String) {
val organization = Organization(curCompany, curJobPosition)
val contact = Contact(0, curPrefix, curFirstName, curMiddleName, curSurname, curSuffix, curPhotoUri, curPhoneNumbers, curEmails, curAddresses, curEvents,
source, 0, 0, "", null, curNotes, curGroups, organization, curWebsites)
if (ContactsHelper(activity).insertContact(contact)) {
contactsImported++
}
}
private fun resetValues() {
curPrefix = ""
curFirstName = ""
curMiddleName = ""
curSurname = ""
curSuffix = ""
curPhotoUri = ""
curNotes = ""
curCompany = ""
curJobPosition = ""
curPhoneNumbers = ArrayList()
curEmails = ArrayList()
curEvents = ArrayList()
curAddresses = ArrayList()
curGroups = ArrayList()
curWebsites = ArrayList()
isGettingPhoto = false
currentPhotoString = StringBuilder()
currentPhotoCompressionFormat = Bitmap.CompressFormat.JPEG
isGettingName = false
currentNameIsANSI = false
currentNameString = StringBuilder()
isGettingNotes = false
currentNotesSB = StringBuilder()
} }
} }

View File

@ -1,39 +1,48 @@
package com.simplemobiletools.contacts.models package com.simplemobiletools.contacts.models
import android.graphics.Bitmap import android.graphics.Bitmap
import com.simplemobiletools.commons.extensions.normalizeString
import com.simplemobiletools.commons.helpers.SORT_BY_FIRST_NAME import com.simplemobiletools.commons.helpers.SORT_BY_FIRST_NAME
import com.simplemobiletools.commons.helpers.SORT_BY_MIDDLE_NAME import com.simplemobiletools.commons.helpers.SORT_BY_MIDDLE_NAME
import com.simplemobiletools.commons.helpers.SORT_DESCENDING import com.simplemobiletools.commons.helpers.SORT_DESCENDING
import com.simplemobiletools.contacts.helpers.PHONE_NUMBER_PATTERN
data class Contact(val id: Int, var prefix: String, var firstName: String, var middleName: String, var surname: String, var suffix: String, var photoUri: String, data class Contact(val id: Int, var prefix: String, var firstName: String, var middleName: String, var surname: String, var suffix: String, var nickname: String,
var phoneNumbers: ArrayList<PhoneNumber>, var emails: ArrayList<Email>, var addresses: ArrayList<Address>, var events: ArrayList<Event>, var photoUri: String, var phoneNumbers: ArrayList<PhoneNumber>, var emails: ArrayList<Email>, var addresses: ArrayList<Address>,
var source: String, var starred: Int, val contactId: Int, val thumbnailUri: String, var photo: Bitmap?, var notes: String, var events: ArrayList<Event>, var source: String, var starred: Int, val contactId: Int, val thumbnailUri: String, var photo: Bitmap?, var notes: String,
var groups: ArrayList<Group>, var organization: Organization, var websites: ArrayList<String>) : Comparable<Contact> { var groups: ArrayList<Group>, var organization: Organization, var websites: ArrayList<String>) : Comparable<Contact> {
companion object { companion object {
var sorting = 0 var sorting = 0
var startWithSurname = false var startWithSurname = false
val pattern = "\\D+".toRegex()
} }
override fun compareTo(other: Contact): Int { override fun compareTo(other: Contact): Int {
val firstString: String var firstString: String
val secondString: String var secondString: String
when { when {
sorting and SORT_BY_FIRST_NAME != 0 -> { sorting and SORT_BY_FIRST_NAME != 0 -> {
firstString = firstName firstString = firstName.normalizeString()
secondString = other.firstName secondString = other.firstName.normalizeString()
} }
sorting and SORT_BY_MIDDLE_NAME != 0 -> { sorting and SORT_BY_MIDDLE_NAME != 0 -> {
firstString = middleName firstString = middleName.normalizeString()
secondString = other.middleName secondString = other.middleName.normalizeString()
} }
else -> { else -> {
firstString = surname firstString = surname.normalizeString()
secondString = other.surname secondString = other.surname.normalizeString()
} }
} }
if (firstString.isEmpty() && firstName.isEmpty() && middleName.isEmpty() && surname.isEmpty() && organization.company.isNotEmpty()) {
firstString = organization.company.normalizeString()
}
if (secondString.isEmpty() && other.firstName.isEmpty() && other.middleName.isEmpty() && other.surname.isEmpty() && other.organization.company.isNotEmpty()) {
secondString = other.organization.company.normalizeString()
}
var result = if (firstString.firstOrNull()?.isLetter() == true && secondString.firstOrNull()?.isLetter() == false) { var result = if (firstString.firstOrNull()?.isLetter() == true && secondString.firstOrNull()?.isLetter() == false) {
-1 -1
} else if (firstString.firstOrNull()?.isLetter() == false && secondString.firstOrNull()?.isLetter() == true) { } else if (firstString.firstOrNull()?.isLetter() == false && secondString.firstOrNull()?.isLetter() == true) {
@ -45,9 +54,9 @@ data class Contact(val id: Int, var prefix: String, var firstName: String, var m
-1 -1
} else { } else {
if (firstString.toLowerCase() == secondString.toLowerCase()) { if (firstString.toLowerCase() == secondString.toLowerCase()) {
getFullName().compareTo(other.getFullName()) getFullName().compareTo(other.getFullName(), true)
} else { } else {
firstString.toLowerCase().compareTo(secondString.toLowerCase()) firstString.compareTo(secondString, true)
} }
} }
} }
@ -85,8 +94,13 @@ data class Contact(val id: Int, var prefix: String, var firstName: String, var m
fun getHashToCompare(): Int { fun getHashToCompare(): Int {
val newPhoneNumbers = ArrayList<PhoneNumber>() val newPhoneNumbers = ArrayList<PhoneNumber>()
phoneNumbers.mapTo(newPhoneNumbers, { PhoneNumber(it.value.replace(pattern, ""), 0) }) phoneNumbers.mapTo(newPhoneNumbers) { PhoneNumber(it.value.replace(PHONE_NUMBER_PATTERN.toRegex(), ""), 0) }
return copy(id = 0, prefix = "", firstName = getFullName().toLowerCase(), middleName = "", surname = "", suffix = "", photoUri = "",
phoneNumbers = newPhoneNumbers, source = "", starred = 0, contactId = 0, thumbnailUri = "", notes = "").hashCode() val newEmails = ArrayList<Email>()
emails.mapTo(newEmails) { Email(it.value, 0) }
return copy(id = 0, prefix = "", firstName = getFullName().toLowerCase(), middleName = "", surname = "", suffix = "", nickname = "", photoUri = "",
phoneNumbers = newPhoneNumbers, events = ArrayList(), addresses = ArrayList(), emails = newEmails, source = "", starred = 0,
contactId = 0, thumbnailUri = "", notes = "", groups = ArrayList(), websites = ArrayList(), organization = Organization("", "")).hashCode()
} }
} }

View File

@ -0,0 +1,3 @@
package com.simplemobiletools.contacts.models
data class RecentCall(var id: Int, var number: String, var dateTime: String, var name: String?)

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<scale
xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:fillAfter="false"
android:fromXScale="1.0"
android:fromYScale="1.0"
android:interpolator="@android:anim/accelerate_interpolator"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="infinite"
android:repeatMode="reverse"
android:toXScale="1.2"
android:toYScale="1.2"/>

Binary file not shown.

After

Width:  |  Height:  |  Size: 487 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 566 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 655 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 716 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 947 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 993 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -170,6 +170,22 @@
android:textCursorDrawable="@null" android:textCursorDrawable="@null"
android:textSize="@dimen/bigger_text_size"/> android:textSize="@dimen/bigger_text_size"/>
<com.simplemobiletools.commons.views.MyEditText
android:id="@+id/contact_nickname"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/contact_suffix"
android:layout_centerVertical="true"
android:layout_marginBottom="@dimen/medium_margin"
android:layout_toRightOf="@+id/contact_name_image"
android:hint="@string/nickname"
android:inputType="textCapWords"
android:lines="1"
android:maxLines="1"
android:singleLine="true"
android:textCursorDrawable="@null"
android:textSize="@dimen/bigger_text_size"/>
<ImageView <ImageView
android:id="@+id/contact_numbers_image" android:id="@+id/contact_numbers_image"
android:layout_width="@dimen/contact_icons_size" android:layout_width="@dimen/contact_icons_size"
@ -185,7 +201,7 @@
android:id="@+id/contact_numbers_holder" android:id="@+id/contact_numbers_holder"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@+id/contact_suffix" android:layout_below="@+id/contact_nickname"
android:layout_marginBottom="@dimen/small_margin" android:layout_marginBottom="@dimen/small_margin"
android:layout_toRightOf="@+id/contact_numbers_image" android:layout_toRightOf="@+id/contact_numbers_image"
android:orientation="vertical"> android:orientation="vertical">

View File

@ -14,27 +14,7 @@
app:tabIndicatorColor="@android:color/white" app:tabIndicatorColor="@android:color/white"
app:tabIndicatorHeight="2dp" app:tabIndicatorHeight="2dp"
app:tabMinWidth="150dp" app:tabMinWidth="150dp"
app:tabSelectedTextColor="@android:color/white"> app:tabSelectedTextColor="@android:color/white"/>
<android.support.design.widget.TabItem
android:id="@+id/tab_contacts"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:icon="@drawable/ic_person"/>
<android.support.design.widget.TabItem
android:id="@+id/tab_favorites"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:icon="@drawable/ic_star_on"/>
<android.support.design.widget.TabItem
android:id="@+id/tab_groups"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:icon="@drawable/ic_group"/>
</android.support.design.widget.TabLayout>
<com.booking.rtlviewpager.RtlViewPager <com.booking.rtlviewpager.RtlViewPager
android:id="@+id/viewpager" android:id="@+id/viewpager"

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<ScrollView <ScrollView
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/settings_scrollview" android:id="@+id/settings_scrollview"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
@ -11,6 +12,28 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="vertical">
<RelativeLayout
android:id="@+id/settings_purchase_thank_you_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.MyTextView
android:id="@+id/settings_purchase_thank_you"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:paddingLeft="@dimen/medium_margin"
android:paddingStart="@dimen/medium_margin"
android:text="@string/purchase_simple_thank_you"/>
</RelativeLayout>
<RelativeLayout <RelativeLayout
android:id="@+id/settings_customize_colors_holder" android:id="@+id/settings_customize_colors_holder"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -55,6 +78,28 @@
</RelativeLayout> </RelativeLayout>
<RelativeLayout
android:id="@+id/settings_manage_tabs_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.MyTextView
android:id="@+id/settings_manage_tabs"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:paddingLeft="@dimen/medium_margin"
android:paddingStart="@dimen/medium_margin"
android:text="@string/manage_shown_tabs"/>
</RelativeLayout>
<RelativeLayout <RelativeLayout
android:id="@+id/settings_use_english_holder" android:id="@+id/settings_use_english_holder"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -74,7 +119,8 @@
android:clickable="false" android:clickable="false"
android:paddingLeft="@dimen/medium_margin" android:paddingLeft="@dimen/medium_margin"
android:paddingStart="@dimen/medium_margin" android:paddingStart="@dimen/medium_margin"
android:text="@string/use_english_language"/> android:text="@string/use_english_language"
app:switchPadding="@dimen/medium_margin"/>
</RelativeLayout> </RelativeLayout>
@ -97,7 +143,8 @@
android:clickable="false" android:clickable="false"
android:paddingLeft="@dimen/medium_margin" android:paddingLeft="@dimen/medium_margin"
android:paddingStart="@dimen/medium_margin" android:paddingStart="@dimen/medium_margin"
android:text="@string/avoid_whats_new"/> android:text="@string/avoid_whats_new"
app:switchPadding="@dimen/medium_margin"/>
</RelativeLayout> </RelativeLayout>
@ -120,7 +167,8 @@
android:clickable="false" android:clickable="false"
android:paddingLeft="@dimen/medium_margin" android:paddingLeft="@dimen/medium_margin"
android:paddingStart="@dimen/medium_margin" android:paddingStart="@dimen/medium_margin"
android:text="@string/show_info_bubble"/> android:text="@string/show_info_bubble"
app:switchPadding="@dimen/medium_margin"/>
</RelativeLayout> </RelativeLayout>
@ -143,7 +191,8 @@
android:clickable="false" android:clickable="false"
android:paddingLeft="@dimen/medium_margin" android:paddingLeft="@dimen/medium_margin"
android:paddingStart="@dimen/medium_margin" android:paddingStart="@dimen/medium_margin"
android:text="@string/show_contact_thumbnails"/> android:text="@string/show_contact_thumbnails"
app:switchPadding="@dimen/medium_margin"/>
</RelativeLayout> </RelativeLayout>
@ -166,7 +215,8 @@
android:clickable="false" android:clickable="false"
android:paddingLeft="@dimen/medium_margin" android:paddingLeft="@dimen/medium_margin"
android:paddingStart="@dimen/medium_margin" android:paddingStart="@dimen/medium_margin"
android:text="@string/show_phone_numbers"/> android:text="@string/show_phone_numbers"
app:switchPadding="@dimen/medium_margin"/>
</RelativeLayout> </RelativeLayout>
@ -189,7 +239,8 @@
android:clickable="false" android:clickable="false"
android:paddingLeft="@dimen/medium_margin" android:paddingLeft="@dimen/medium_margin"
android:paddingStart="@dimen/medium_margin" android:paddingStart="@dimen/medium_margin"
android:text="@string/start_name_with_surname"/> android:text="@string/start_name_with_surname"
app:switchPadding="@dimen/medium_margin"/>
</RelativeLayout> </RelativeLayout>
@ -212,7 +263,32 @@
android:clickable="false" android:clickable="false"
android:paddingLeft="@dimen/medium_margin" android:paddingLeft="@dimen/medium_margin"
android:paddingStart="@dimen/medium_margin" android:paddingStart="@dimen/medium_margin"
android:text="@string/filter_duplicates"/> android:text="@string/filter_duplicates"
app:switchPadding="@dimen/medium_margin"/>
</RelativeLayout>
<RelativeLayout
android:id="@+id/settings_show_call_confirmation_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_show_call_confirmation"
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/show_call_confirmation_dialog"
app:switchPadding="@dimen/medium_margin"/>
</RelativeLayout> </RelativeLayout>

View File

@ -166,6 +166,21 @@
android:singleLine="true" android:singleLine="true"
android:textSize="@dimen/bigger_text_size"/> android:textSize="@dimen/bigger_text_size"/>
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/contact_nickname"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/contact_suffix"
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 <ImageView
android:id="@+id/contact_numbers_image" android:id="@+id/contact_numbers_image"
android:layout_width="@dimen/contact_icons_size" android:layout_width="@dimen/contact_icons_size"
@ -181,7 +196,7 @@
android:id="@+id/contact_numbers_holder" android:id="@+id/contact_numbers_holder"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@+id/contact_suffix" android:layout_below="@+id/contact_nickname"
android:layout_toRightOf="@+id/contact_numbers_image" android:layout_toRightOf="@+id/contact_numbers_image"
android:orientation="vertical" android:orientation="vertical"
android:paddingLeft="@dimen/small_margin"/> android:paddingLeft="@dimen/small_margin"/>

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/dialog_holder"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="@dimen/activity_margin">
<ImageView
android:id="@+id/call_confirm_phone"
style="@style/MyBorderlessBackgroundStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:padding="@dimen/activity_margin"
android:src="@drawable/ic_phone_big"/>
</RelativeLayout>

View File

@ -54,6 +54,14 @@
android:paddingTop="@dimen/activity_margin" android:paddingTop="@dimen/activity_margin"
android:text="@string/suffix"/> android:text="@string/suffix"/>
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/manage_visible_fields_nickname"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/activity_margin"
android:paddingTop="@dimen/activity_margin"
android:text="@string/nickname"/>
<com.simplemobiletools.commons.views.MyAppCompatCheckbox <com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/manage_visible_fields_phone_numbers" android:id="@+id/manage_visible_fields_phone_numbers"
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/manage_visible_tabs_scroll_view"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/manage_visible_tabs_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="@dimen/activity_margin"
android:paddingRight="@dimen/activity_margin"
android:paddingTop="@dimen/activity_margin">
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/manage_visible_tabs_contacts"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/activity_margin"
android:paddingTop="@dimen/activity_margin"
android:text="@string/contacts"/>
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/manage_visible_tabs_favorites"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/activity_margin"
android:paddingTop="@dimen/activity_margin"
android:text="@string/favorites"/>
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/manage_visible_tabs_recents"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/activity_margin"
android:paddingTop="@dimen/activity_margin"
android:text="@string/recent_calls"/>
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/manage_visible_tabs_groups"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/activity_margin"
android:paddingTop="@dimen/activity_margin"
android:text="@string/groups"/>
</LinearLayout>
</ScrollView>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<com.simplemobiletools.contacts.fragments.RecentsFragment
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/recents_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="@layout/fragment_layout"/>
</com.simplemobiletools.contacts.fragments.RecentsFragment>

View File

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/recent_call_frame"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:foreground="@drawable/selector">
<android.support.constraint.ConstraintLayout
android:id="@+id/recent_call_holder"
android:layout_width="match_parent"
android:layout_height="@dimen/contact_item_height"
android:gravity="center_vertical"
android:paddingLeft="@dimen/activity_margin"
android:paddingRight="@dimen/activity_margin">
<TextView
android:id="@+id/recent_call_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:paddingRight="@dimen/activity_margin"
android:textSize="@dimen/big_text_size"
app:layout_constraintBottom_toTopOf="@+id/recent_call_number"
app:layout_constraintEnd_toStartOf="@+id/recent_call_date_time"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="John Doe"/>
<TextView
android:id="@+id/recent_call_number"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_below="@+id/recent_call_name"
android:maxLines="1"
android:textSize="@dimen/bigger_text_size"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/recent_call_date_time"
app:layout_constraintStart_toStartOf="parent"
tools:text="0123 456 789"/>
<TextView
android:id="@+id/recent_call_date_time"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:maxLines="1"
android:textSize="@dimen/normal_text_size"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:layout_editor_absoluteY="0dp"
tools:text="Yesterday, 17:00"/>
</android.support.constraint.ConstraintLayout>
</FrameLayout>

View File

@ -3,7 +3,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<item <item
android:id="@+id/cab_edit" android:id="@+id/cab_edit"
android:icon="@drawable/ic_rename" android:icon="@drawable/ic_edit"
android:title="@string/edit_contact" android:title="@string/edit_contact"
app:showAsAction="ifRoom"/> app:showAsAction="ifRoom"/>
<item <item

View File

@ -2,9 +2,9 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android" <menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<item <item
android:id="@+id/cab_edit" android:id="@+id/cab_rename"
android:icon="@drawable/ic_rename" android:icon="@drawable/ic_rename_new"
android:title="@string/edit_contact" android:title="@string/rename"
app:showAsAction="ifRoom"/> app:showAsAction="ifRoom"/>
<item <item
android:id="@+id/cab_select_all" android:id="@+id/cab_select_all"

View File

@ -0,0 +1,14 @@
<?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/cab_delete"
android:icon="@drawable/ic_delete"
android:title="@string/delete"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/cab_select_all"
android:icon="@drawable/ic_select_all"
android:title="@string/select_all"
app:showAsAction="ifRoom"/>
</menu>

View File

@ -3,7 +3,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<item <item
android:id="@+id/edit" android:id="@+id/edit"
android:icon="@drawable/ic_rename" android:icon="@drawable/ic_edit"
android:title="@string/edit_contact" android:title="@string/edit_contact"
app:showAsAction="ifRoom"/> app:showAsAction="ifRoom"/>
<item <item

View File

@ -0,0 +1,133 @@
<resources>
<string name="app_name">Sadə Kontaktlar</string>
<string name="app_launcher_name">Kontaktlar</string>
<string name="address">Ünvan</string>
<string name="inserting">Daxil edilir…</string>
<string name="updating">Yenilənir…</string>
<string name="phone_storage">Telefon yaddaşı</string>
<string name="phone_storage_hidden">Telefon yaddaşı (digər tətbiqlərə görünmür)</string>
<string name="company">Şirkət</string>
<string name="job_position">İş vəziyyəti</string>
<string name="website">Vebsayt</string>
<string name="send_sms_to_contacts">Kontaktlara SMS göndər</string>
<string name="send_email_to_contacts">Kontaktlara e-poçt göndər</string>
<string name="send_sms_to_group">Grupa SMS göndər</string>
<string name="send_email_to_group">Grupa e-poçt göndər</string>
<string name="call_person">%s şəxsinə zng et</string>
<string name="request_the_required_permissions">Lazım olan icazələri istə</string>
<string name="new_contact">Yeni kontakt</string>
<string name="edit_contact">Redaktə et</string>
<string name="select_contact">Kontakt seç</string>
<string name="select_contacts">Kontaktları seç</string>
<string name="first_name">Ad</string>
<string name="middle_name">Orta Ad</string>
<string name="surname">Soyad</string>
<string name="nickname">Nickname</string>
<!-- Groups -->
<string name="no_groups">Qruplar yoxdur</string>
<string name="create_new_group">Yeni qrup yarat</string>
<string name="remove_from_group">Qrupdan sil</string>
<string name="no_group_participants">Bu qrup boşdur</string>
<string name="add_contacts">Kontaktlar əlavə et</string>
<string name="no_group_created">Cihazda heçbir kontakt qrupu yoxdur</string>
<string name="create_group">Qrup yarat</string>
<string name="add_to_group">Qrupa əlavə et</string>
<string name="create_group_under_account">Hesab altında qrup yarat</string>
<!-- Photo -->
<string name="take_photo">Şəkil çək</string>
<string name="choose_photo">Şəkil seç</string>
<string name="remove_photo">Şəkli sil</string>
<!-- Settings -->
<string name="start_name_with_surname">Ada soyaddan başla</string>
<string name="show_phone_numbers">Telefon nömrələrini əsas ekranda göstər</string>
<string name="show_contact_thumbnails">Kontakt görüntülərini göstər</string>
<string name="on_contact_click">Kontakta toxunduqda</string>
<string name="call_contact">Kontakta zəng et</string>
<string name="view_contact">Kontakt detallarına bax</string>
<string name="manage_shown_contact_fields">Göstərilən kontakt sahəsini idarə et</string>
<string name="filter_duplicates">Təkrarlanmış kontaktları filtrləməyə çalış</string>
<string name="manage_shown_tabs">Göstərilən nişanları idarə et</string>
<string name="contacts">Kontaktlar</string>
<string name="favorites">Sevimlilər</string>
<string name="recent_calls">Hazırki zənglər</string>
<string name="show_call_confirmation_dialog">Zəngə başlamazdan əvvəl zəng təsdiq pəncərəsi göstər</string>
<!-- Emails -->
<string name="email">E-poçt</string>
<string name="home">Ev</string>
<string name="work">İş</string>
<string name="other">Başqa</string>
<!-- Phone numbers -->
<string name="number">Nömrə</string>
<string name="mobile">Mobil</string>
<string name="main_number">Əsas</string>
<string name="work_fax">İş Faksı</string>
<string name="home_fax">Ev Faksı</string>
<string name="pager">Zəng cihazı</string>
<string name="no_phone_number_found">Heçbir telefon nömrəsi tapılmadı</string>
<!-- Events -->
<string name="birthday">Ad günü</string>
<string name="anniversary">İl dönümü</string>
<!-- Favorites -->
<string name="no_favorites">Görünür, hələlik heçbir sevimli kontakt əlavə etməmisiniz.</string>
<string name="add_favorites">Sevimlilər əlavə et</string>
<string name="add_to_favorites">Sevimlilərə əlavə et</string>
<string name="remove_from_favorites">Sevimlilərdən sil</string>
<string name="must_be_at_edit">Kontaktı dəyişmək üçün İdarə et ekranında olmalısınız</string>
<!-- Search -->
<string name="search_contacts">Kontaktları axtar</string>
<string name="search_favorites">Sevimliləri axtar</string>
<!-- Export / Import -->
<string name="import_contacts">Kontaktları daxil et</string>
<string name="export_contacts">Kontaktları xaric et</string>
<string name="import_contacts_from_vcf">.vcf faylından kontaktları daxil et</string>
<string name="export_contacts_to_vcf">.vcf faylından kontaktları xaric et</string>
<string name="target_contact_source">Kontakt kökünü nişanla</string>
<string name="include_contact_sources">Kontakt köklərini daxil et</string>
<string name="filename_without_vcf">Fayl adı (.vcf olmadan)</string>
<!-- Visible fields -->
<string name="select_fields_to_show">Göstərmək üçün sahəni seç</string>
<string name="prefix">Ön şəkilçi</string>
<string name="suffix">Orta şəkilçi</string>
<string name="phone_numbers">Telefon nömrələri</string>
<string name="emails">E-poçtlar</string>
<string name="addresses">Ünvanlar</string>
<string name="events">Hadisələr (ad günləri, il dönümləri)</string>
<string name="notes">Qeydlər</string>
<string name="organization">Təşkilat</string>
<string name="websites">Vebsaytlar</string>
<string name="groups">Qruplar </string>
<string name="contact_source">Kontakt kökü</string>
<!-- FAQ -->
<string name="faq_1_title">I want to change what fields are visible at contacts. Can I do it?</string>
<string name="faq_1_text">Yes, all you have to do is go in Settings -> Manage shown contact fields. There you can select what fields should be visible. Some of them are even disabled by default, so you might find some new ones there.</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">A contacts app for managing your contacts without ads.</string>
<string name="app_long_description">
A simple app for creating or managing your contacts from any source. The contacts can be stored on your device only, but also synchronized via Google, or other accounts. You can display your favorite contacts on a separate list.
You can use it for managing user emails and events too. It has the ability to sort/filter by multiple parameters, optionally display surname as the first name.
Contains no ads or unnecessary permissions. It is fully opensource, provides customizable colors.
This app is just one piece of a bigger series of apps. You can find the rest of them at https://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

@ -6,13 +6,15 @@
<string name="updating">Aktualisiere…</string> <string name="updating">Aktualisiere…</string>
<string name="phone_storage">Gerätespeicher</string> <string name="phone_storage">Gerätespeicher</string>
<string name="phone_storage_hidden">Gerätespeicher (nicht sichtbar für andere Apps)</string> <string name="phone_storage_hidden">Gerätespeicher (nicht sichtbar für andere Apps)</string>
<string name="company">Company</string> <string name="company">Unternehmen</string>
<string name="job_position">Job position</string> <string name="job_position">Arbeitsstelle</string>
<string name="website">Website</string> <string name="website">Webseite</string>
<string name="send_sms_to_contacts">Send SMS to contacts</string> <string name="send_sms_to_contacts">Sende SMS an Kontakte</string>
<string name="send_email_to_contacts">Send email to contacts</string> <string name="send_email_to_contacts">Sende E-Mail an Kontakte</string>
<string name="send_sms_to_group">Send SMS to group</string> <string name="send_sms_to_group">Sende SMS an Gruppe</string>
<string name="send_email_to_group">Send email to group</string> <string name="send_email_to_group">Sende E-Mail an Gruppe</string>
<string name="call_person">%s anrufen</string>
<string name="request_the_required_permissions">Benötigte Berechtigungen anfordern</string>
<string name="new_contact">Neuer Kontakt</string> <string name="new_contact">Neuer Kontakt</string>
<string name="edit_contact">Kontakt bearbeiten</string> <string name="edit_contact">Kontakt bearbeiten</string>
@ -20,12 +22,13 @@
<string name="select_contacts">Kontakte auswählen</string> <string name="select_contacts">Kontakte auswählen</string>
<string name="first_name">Vorname</string> <string name="first_name">Vorname</string>
<string name="middle_name">Zweiter Vorname</string> <string name="middle_name">Zweiter Vorname</string>
<string name="surname">Familienname</string> <string name="surname">Nachname</string>
<string name="nickname">Spitzname</string>
<!-- Groups --> <!-- Groups -->
<string name="no_groups">Keine Gruppen</string> <string name="no_groups">Keine Gruppen</string>
<string name="create_new_group">Eine neue Gruppe erstellen</string> <string name="create_new_group">Eine neue Gruppe erstellen</string>
<string name="remove_from_group">Von Gruppe entfernen</string> <string name="remove_from_group">Aus Gruppe entfernen</string>
<string name="no_group_participants">Diese Gruppe ist leer</string> <string name="no_group_participants">Diese Gruppe ist leer</string>
<string name="add_contacts">Kontakte hinzufügen</string> <string name="add_contacts">Kontakte hinzufügen</string>
<string name="no_group_created">Es sind keine Kontaktgruppen auf diesem Gerät vorhanden</string> <string name="no_group_created">Es sind keine Kontaktgruppen auf diesem Gerät vorhanden</string>
@ -34,24 +37,27 @@
<string name="create_group_under_account">Gruppe in diesem Konto erstellen</string> <string name="create_group_under_account">Gruppe in diesem Konto erstellen</string>
<!-- Photo --> <!-- Photo -->
<string name="take_photo">Foto machen</string> <string name="take_photo">Foto aufnehmen</string>
<string name="choose_photo">Foto auswählen</string> <string name="choose_photo">Foto auswählen</string>
<string name="remove_photo">Foto entfernen</string> <string name="remove_photo">Foto entfernen</string>
<!-- Settings --> <!-- Settings -->
<string name="start_name_with_surname">Namen mit Nachnamen beginnen</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_phone_numbers">Zeige Telefonnummern im Hauptmenü</string>
<string name="show_contact_thumbnails">Zeige Vorschaubilder r Kontakte</string> <string name="show_contact_thumbnails">Zeige Vorschaubilder der Kontakte</string>
<string name="on_contact_click">Beim Klicken auf den Kontakt</string> <string name="on_contact_click">Beim Klicken auf den Kontakt</string>
<string name="call_contact">Kontakt anrufen</string> <string name="call_contact">Kontakt anrufen</string>
<string name="view_contact">Kontaktdetails ansehen</string> <string name="view_contact">Kontaktdetails anzeigen</string>
<string name="show_favorites_tab">Show favorites tab</string> <string name="manage_shown_contact_fields">Bearbeite sichtbare Kontaktfelder</string>
<string name="show_groups_tab">Show groups tab</string> <string name="filter_duplicates">Versuche Kontaktduplikate herauszufiltern</string>
<string name="manage_shown_contact_fields">Manage shown contact fields</string> <string name="manage_shown_tabs">Anzuzeigende Tabs festlegen</string>
<string name="filter_duplicates">Try filtering out duplicate contacts</string> <string name="contacts">Kontakte</string>
<string name="favorites">Favoriten</string>
<string name="recent_calls">Anrufliste</string>
<string name="show_call_confirmation_dialog">Bestätigungsdialog zeigen, bevor ein Anruf durchgeführt wird</string>
<!-- Emails --> <!-- Emails -->
<string name="email">Email</string> <string name="email">E-Mail</string>
<string name="home">Privat</string> <string name="home">Privat</string>
<string name="work">Arbeit</string> <string name="work">Arbeit</string>
<string name="other">Sonstiges</string> <string name="other">Sonstiges</string>
@ -74,7 +80,7 @@
<string name="add_favorites">Favoriten hinzufügen</string> <string name="add_favorites">Favoriten hinzufügen</string>
<string name="add_to_favorites">Zu Favoriten hinzufügen</string> <string name="add_to_favorites">Zu Favoriten hinzufügen</string>
<string name="remove_from_favorites">Aus Favoriten entfernen</string> <string name="remove_from_favorites">Aus Favoriten entfernen</string>
<string name="must_be_at_edit">You must be at the Edit screen to modify a contact</string> <string name="must_be_at_edit">Sie müssen sich im Bearbeitungsmodus befinden, um einen Kontakt zu bearbeiten</string>
<!-- Search --> <!-- Search -->
<string name="search_contacts">Kontakte durchsuchen</string> <string name="search_contacts">Kontakte durchsuchen</string>
@ -90,34 +96,34 @@
<string name="filename_without_vcf">Dateiname (ohne .vcf)</string> <string name="filename_without_vcf">Dateiname (ohne .vcf)</string>
<!-- Visible fields --> <!-- Visible fields -->
<string name="select_fields_to_show">Select fields to show</string> <string name="select_fields_to_show">Sichtbare Felder auswählen</string>
<string name="prefix">Prefix</string> <string name="prefix">Titel</string>
<string name="suffix">Suffix</string> <string name="suffix">Suffix</string>
<string name="phone_numbers">Phone numbers</string> <string name="phone_numbers">Telefonnummern</string>
<string name="emails">Emails</string> <string name="emails">E-Mails</string>
<string name="addresses">Addresses</string> <string name="addresses">Addressen</string>
<string name="events">Events (birthdays, anniversaries)</string> <string name="events">Termine (Geburtstage, Jahrestage)</string>
<string name="notes">Notes</string> <string name="notes">Notizen</string>
<string name="organization">Organization</string> <string name="organization">Organisation</string>
<string name="websites">Websites</string> <string name="websites">Webseiten</string>
<string name="groups">Groups</string> <string name="groups">Gruppen</string>
<string name="contact_source">Contact source</string> <string name="contact_source">Kontaktquelle</string>
<!-- FAQ --> <!-- FAQ -->
<string name="faq_1_title">I want to change what fields are visible at contacts. Can I do it?</string> <string name="faq_1_title">Ich möchte die sichtbaren Kontaktfelder ändern. Kann ich das machen?</string>
<string name="faq_1_text">Yes, all you have to do is go in Settings -> Manage shown contact fields. There you can select what fields should be visible. Some of them are even disabled by default, so you might find some new ones there.</string> <string name="faq_1_text">Ja, alles, was Sie tun müssen ist folgendes: Gehen Sie zu Einstellungen -> Bearbeite sichtbare Kontaktfelder. Hier können die sichtbaren Felder ausgewählt werden. Einige sind standardmäßig deaktiviert, weshalb hier neue gefunden werden können.</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars --> <!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Eine App zum Verwalten von Kontakten, ganz ohne Werbung.</string> <string name="app_short_description">Eine Kontakte-App zur Verwaltung Ihrer Kontake. Ohne Werbung.</string>
<string name="app_long_description"> <string name="app_long_description">
Eine schlichte App um Kontakte aus allen Quellen zu verwalten und neue zu erstellen. Die Kontakte können nur auf deinem Gerät gespeichert werden, aber auch über Google oder andere Dienste synchronisiert werden. Deine wichtigsten Kontakte werden in einer separaten Liste angezeigt. Eine einfache App, mit der Sie Kontakte erstellen oder von jeder Quelle verwalten können. Die Kontakte können entweder nur auf Ihrem Gerät gespeichert, oder mittels Google oder anderer Konten synchronisiert werden. Sie können Ihre Lieblingskontake in einer separaten Liste anzeigen.
Du kannst diese App auch dazu nutzen, um die Email-Adressen und Termine von Kontakten zu verwalten. Sie hat die Möglichkeit mithilfe von mehreren Parametern zu sortieren/filtern, optional auch den Familiennamen als Vornamen anzuzeigen. Sie können es auch zur Verwaltung von Nutzer-E-Mails und Ereignisse nutzen. Es kann nach mehreren Parametern sortieren oder filtern, oder optional den Nachnamen zuerst anzeigen.
Beinhaltet keine Werbung oder unnötige Berechtigungen. Sie ist komplett Open Source, alle verwendeten Farben sind anpassbar. Enthält keine Werbung oder unnötige Berechtigungen. Vollständig quelloffen. Die Farben sind anpassbar.
Diese App ist nur eine aus einer größeren Serie von schlichten Apps. Der Rest davon findet sich auf https://www.simplemobiletools.com Diese App ist nur ein Teil einer größeren App-Familie. Die übrigen finden Sie unter https://www.simplemobiletools.com
</string> </string>
<!-- <!--

View File

@ -1,18 +1,20 @@
<resources> <resources>
<string name="app_name">Απλές Επαφές</string> <string name="app_name">Simple Contacts</string>
<string name="app_launcher_name">Επαφές</string> <string name="app_launcher_name">Επαφές</string>
<string name="address">Διεύθυνση</string> <string name="address">Διεύθυνση</string>
<string name="inserting">Εισαγωγή…</string> <string name="inserting">Εισαγωγή…</string>
<string name="updating">Ενημέρωση…</string> <string name="updating">Ενημέρωση…</string>
<string name="phone_storage">Μνήμη τηλεφώνου</string> <string name="phone_storage">Μνήμη τηλεφώνου</string>
<string name="phone_storage_hidden">Μνήμη τηλεφώνου (δεν είναι ορατή από άλλες εφαρμογές)</string> <string name="phone_storage_hidden">Μνήμη τηλεφώνου (κρυφές από άλλες εφαρμογές)</string>
<string name="company">Company</string> <string name="company">Εταιρεία</string>
<string name="job_position">Job position</string> <string name="job_position">Θέση Εργασίας</string>
<string name="website">Website</string> <string name="website">Ιστοσελίδα</string>
<string name="send_sms_to_contacts">Send SMS to contacts</string> <string name="send_sms_to_contacts">Αποστολή SMS σε επαφές</string>
<string name="send_email_to_contacts">Send email to contacts</string> <string name="send_email_to_contacts">Αποστολή email σε επαφές</string>
<string name="send_sms_to_group">Send SMS to group</string> <string name="send_sms_to_group">Αποστολή SMS σε ομάδες</string>
<string name="send_email_to_group">Send email to group</string> <string name="send_email_to_group">Αποστολή email σε ομάδες</string>
<string name="call_person">Κλήση %s</string>
<string name="request_the_required_permissions">Ζητούνται τα απαιτούμενα δικαιώματα</string>
<string name="new_contact">Νέα επαφή</string> <string name="new_contact">Νέα επαφή</string>
<string name="edit_contact">Επεξεργασία επαφής</string> <string name="edit_contact">Επεξεργασία επαφής</string>
@ -21,6 +23,7 @@
<string name="first_name">Όνομα</string> <string name="first_name">Όνομα</string>
<string name="middle_name">Μεσαίο όνομα</string> <string name="middle_name">Μεσαίο όνομα</string>
<string name="surname">Επώνυμο</string> <string name="surname">Επώνυμο</string>
<string name="nickname">Ψευδώνυμο</string>
<!-- Groups --> <!-- Groups -->
<string name="no_groups">Δεν υπάρχουν ομάδες</string> <string name="no_groups">Δεν υπάρχουν ομάδες</string>
@ -31,7 +34,7 @@
<string name="no_group_created">Δεν υπάρχουν ομάδες επαφών στη συσκευή</string> <string name="no_group_created">Δεν υπάρχουν ομάδες επαφών στη συσκευή</string>
<string name="create_group">Δημιουργία ομάδας</string> <string name="create_group">Δημιουργία ομάδας</string>
<string name="add_to_group">Προσθήκη σε ομάδα</string> <string name="add_to_group">Προσθήκη σε ομάδα</string>
<string name="create_group_under_account">Δημιουργία ομάδας κάτω από λογαριασμό</string> <string name="create_group_under_account">Δημιουργία ομάδας υπο ενός λογαριασμού</string>
<!-- Photo --> <!-- Photo -->
<string name="take_photo">Λήψη φωτογραφίας</string> <string name="take_photo">Λήψη φωτογραφίας</string>
@ -39,20 +42,23 @@
<string name="remove_photo">Αφαίρεση φωτογραφίας</string> <string name="remove_photo">Αφαίρεση φωτογραφίας</string>
<!-- Settings --> <!-- Settings -->
<string name="start_name_with_surname">Το όνομα ξεκινά με το επώνυμο</string> <string name="start_name_with_surname">Εμφάνιση πρώτα το επώνυμο</string>
<string name="show_phone_numbers">Εμφάνιση τηλεφωνικών αριθμών στην κύρια οθόνη</string> <string name="show_phone_numbers">Εμφάνιση τηλεφωνικών αριθμών στην κύρια οθόνη</string>
<string name="show_contact_thumbnails">Εμφάνιση μικρογραφιών επαφής</string> <string name="show_contact_thumbnails">Εμφάνιση μικρογραφιών επαφής</string>
<string name="on_contact_click">Στην επιλογή επαφής</string> <string name="on_contact_click">Στην επιλογή επαφής</string>
<string name="call_contact">Κλήση επαφής</string> <string name="call_contact">Κλήση επαφής</string>
<string name="view_contact">Εμφάνιση λεπτομερειών επαφής</string> <string name="view_contact">Εμφάνιση λεπτομερειών επαφής</string>
<string name="show_favorites_tab">Εμφάνιση καρτέλας αγαπημένων</string> <string name="manage_shown_contact_fields">Διαχείριση εμφανιζόμενων πεδίων επαφής</string>
<string name="show_groups_tab">Εμφάνιση καρτέλας ομάδων</string> <string name="filter_duplicates">Δοκιμάστε το φιλτράρισμα διπλών επαφών</string>
<string name="manage_shown_contact_fields">Manage shown contact fields</string> <string name="manage_shown_tabs">Διαχείριση εμφανιζόμενων καρτελών</string>
<string name="filter_duplicates">Try filtering out duplicate contacts</string> <string name="contacts">Επαφές</string>
<string name="favorites">Αγαπημένες</string>
<string name="recent_calls">Πρόσφατες Κλήσεις</string>
<string name="show_call_confirmation_dialog">Εμφάνιση διαλόγου επιβεβαίωσης πριν από την έναρξη μιας κλήσης</string>
<!-- Emails --> <!-- Emails -->
<string name="email">Email</string> <string name="email">Email</string>
<string name="home">Σπίτι</string> <string name="home">Οικία</string>
<string name="work">Εργασία</string> <string name="work">Εργασία</string>
<string name="other">Άλλο</string> <string name="other">Άλλο</string>
@ -60,8 +66,8 @@
<string name="number">Αριθμός</string> <string name="number">Αριθμός</string>
<string name="mobile">Κινητό</string> <string name="mobile">Κινητό</string>
<string name="main_number">Κύριο</string> <string name="main_number">Κύριο</string>
<string name="work_fax">Φαξ εργασίας</string> <string name="work_fax">Φαξ Εργασίας</string>
<string name="home_fax">Φαξ σπιτιού</string> <string name="home_fax">Φαξ Οικίας</string>
<string name="pager">Βομβητής</string> <string name="pager">Βομβητής</string>
<string name="no_phone_number_found">Δεν βρέθηκε τηλεφωνικός αριθμός</string> <string name="no_phone_number_found">Δεν βρέθηκε τηλεφωνικός αριθμός</string>
@ -70,11 +76,11 @@
<string name="anniversary">Επέτειος</string> <string name="anniversary">Επέτειος</string>
<!-- Favorites --> <!-- Favorites -->
<string name="no_favorites">Φαίνεται ότι δεν έχεις προσθέσει αγαπημένες επαφές ακόμα.</string> <string name="no_favorites">Φαίνεται ότι δεν έχετε προσθέσει αγαπημένες επαφές ακόμη.</string>
<string name="add_favorites">Προσθήκη αγαπημένων</string> <string name="add_favorites">Προσθήκη αγαπημένων</string>
<string name="add_to_favorites">Προσθήκη στα αγαπημένα</string> <string name="add_to_favorites">Προσθήκη στις αγαπημένες</string>
<string name="remove_from_favorites">Αφαίρεση από τα αγαπημένα</string> <string name="remove_from_favorites">Αφαίρεση από τα αγαπημένα</string>
<string name="must_be_at_edit">You must be at the Edit screen to modify a contact</string> <string name="must_be_at_edit">Πρέπει να είστε στην οθόνη "Επεξεργασία" για να τροποποιήσετε μια επαφή</string>
<!-- Search --> <!-- Search -->
<string name="search_contacts">Αναζήτηση επαφών</string> <string name="search_contacts">Αναζήτηση επαφών</string>
@ -90,34 +96,34 @@
<string name="filename_without_vcf">Όνομα αρχείου (χωρίς .vcf)</string> <string name="filename_without_vcf">Όνομα αρχείου (χωρίς .vcf)</string>
<!-- Visible fields --> <!-- Visible fields -->
<string name="select_fields_to_show">Select fields to show</string> <string name="select_fields_to_show">Επιλογή εμφάνισης πεδίων</string>
<string name="prefix">Prefix</string> <string name="prefix">Πρόθεμα</string>
<string name="suffix">Suffix</string> <string name="suffix">Κατάληξη</string>
<string name="phone_numbers">Phone numbers</string> <string name="phone_numbers">Αριθμοί Τηλεφώνων</string>
<string name="emails">Emails</string> <string name="emails">Emails</string>
<string name="addresses">Addresses</string> <string name="addresses">Διευθύνσεις</string>
<string name="events">Events (birthdays, anniversaries)</string> <string name="events">Εκδηλώσεις (γενέθλια, επετείους)</string>
<string name="notes">Notes</string> <string name="notes">Σημειώσεις</string>
<string name="organization">Organization</string> <string name="organization">Εταιρεία</string>
<string name="websites">Websites</string> <string name="websites">Ιστοσελίδα</string>
<string name="groups">Groups</string> <string name="groups">Ομάδες</string>
<string name="contact_source">Contact source</string> <string name="contact_source">Προέλευση επαφής</string>
<!-- FAQ --> <!-- FAQ -->
<string name="faq_1_title">I want to change what fields are visible at contacts. Can I do it?</string> <string name="faq_1_title">Θέλω να αλλάξω τα πεδία που θα είναι ορατά στις επαφές. Μπορώ να το κάνω?</string>
<string name="faq_1_text">Yes, all you have to do is go in Settings -> Manage shown contact fields. There you can select what fields should be visible. Some of them are even disabled by default, so you might find some new ones there.</string> <string name="faq_1_text">Ναι, το μόνο που έχετε να κάνετε είναι να μεταβείτε στις Ρυθμίσεις -> Διαχείριση εμφανιζόμενων πεδίων επαφής. Εκεί μπορείτε να επιλέξετε ποια πεδία θα πρέπει να είναι ορατά. Κάποια από αυτά είναι ακόμη και απενεργοποιημένα από προεπιλογή, επομένως ίσως βρείτε κάποια νέα εκεί.</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have --> <!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars --> <!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Μια εφαρμογή επαφών για να διαχειρίζεσαι τις επαφές σου χωρίς διαφημίσεις.</string> <string name="app_short_description">Μια εφαρμογή για την διαχείρηση των επαφών σου χωρίς διαφημίσεις.</string>
<string name="app_long_description"> <string name="app_long_description">
Μια απλή εφαρμογή για δημιουργία και διαχείριση των επαφών σου από κάθε πηγή. Οι επαφές μπορεί να είναι αποθηκευμένες μόνο στη συσκευή σου, αλλά μπορούν να συγχρονίζονται στο Google, ή σε κάποιο άλλο λογαριασμό. Μπορείς να εμφανίσεις τις αγαπημένες σου επαφές σε ξεχωριστή λίστα. Μια απλή εφαρμογή για δημιουργία και διαχείριση των επαφών σου από κάθε πηγή. Οι επαφές μπορεί να είναι αποθηκευμένες μόνο στη συσκευή σου, αλλά μπορούν να συγχρονίζονται στο Google, ή σε κάποιο άλλο λογαριασμό. Μπορείς να εμφανίσεις τις αγαπημένες σου επαφές σε ξεχωριστή λίστα.
Μπορείς να τη χρησιμοποιήσεις για τη διαχείριση των email των χρηστών και τα γεγονότα. Έχει τη δυνατότητα ταξινόμησης/φιλτραρίσματος με διάφορες παραμέτρους, προαιρετικά να εμφανίζεται το επώνυμο σαν όνομα. Μπορείτε να τη χρησιμοποιήσετε για τη διαχείριση των email και εκδηλώσεων επίσης. Έχει τη δυνατότητα ταξινόμησης/φιλτραρίσματος με διάφορες παραμέτρους, προαιρετικά να εμφανίζεται το επώνυμο πρώτα ή το όνομα.
Δεν περιέχει διαφημίσεις ή περιττές άδειες. Είναι πλήρως ανοικτού κώδικα, παρέχει δυνατότητα προσαρμογής των χρωμάτων. Δεν περιέχει διαφημίσεις ή περιττές άδειες. Έιναι όλη ανοιχτού κώδικα και παρέχει προσαρμόσιμα χρώματα για την εφαρμογή.
Αυτή η εφαρμογή είναι ένα μικρό κομμάτι μιας μεγαλύτερης συλλογής εφαρμογών. Μπορείς να βρεις τις υπόλοιπες στο https://www.simplemobiletools.com Αυτή η εφαρμογή είναι μέρος μιας σειράς εφαρμογών. Μπορείτε να βρείτε τις υπόλοιπες στο https://www.simplemobiletools.com
</string> </string>
<!-- <!--

View File

@ -13,6 +13,8 @@
<string name="send_email_to_contacts">Envoyer un e-mail aux contacts</string> <string name="send_email_to_contacts">Envoyer un e-mail aux contacts</string>
<string name="send_sms_to_group">Envoyer un SMS au groupe</string> <string name="send_sms_to_group">Envoyer un SMS au groupe</string>
<string name="send_email_to_group">Envoyer un e-mail au groupe</string> <string name="send_email_to_group">Envoyer un e-mail au groupe</string>
<string name="call_person">Call %s</string>
<string name="request_the_required_permissions">Request the required permissions</string>
<string name="new_contact">Nouveau contact</string> <string name="new_contact">Nouveau contact</string>
<string name="edit_contact">Modifier contact</string> <string name="edit_contact">Modifier contact</string>
@ -21,6 +23,7 @@
<string name="first_name">Prénom</string> <string name="first_name">Prénom</string>
<string name="middle_name">Deuxième prénom</string> <string name="middle_name">Deuxième prénom</string>
<string name="surname">Nom</string> <string name="surname">Nom</string>
<string name="nickname">Nickname</string>
<!-- Groups --> <!-- Groups -->
<string name="no_groups">Pas de groupe</string> <string name="no_groups">Pas de groupe</string>
@ -45,10 +48,13 @@
<string name="on_contact_click">Sur appui du contact</string> <string name="on_contact_click">Sur appui du contact</string>
<string name="call_contact">Appeler le contact</string> <string name="call_contact">Appeler le contact</string>
<string name="view_contact">Voir les détails du contact</string> <string name="view_contact">Voir les détails du contact</string>
<string name="show_favorites_tab">Afficher l\'onglet favoris</string>
<string name="show_groups_tab">Afficher l\'onglet groupes</string>
<string name="manage_shown_contact_fields">Configurer l\'affichage des champs des contacts</string> <string name="manage_shown_contact_fields">Configurer l\'affichage des champs des contacts</string>
<string name="filter_duplicates">Try filtering out duplicate contacts</string> <string name="filter_duplicates">Try filtering out duplicate contacts</string>
<string name="manage_shown_tabs">Manage shown tabs</string>
<string name="contacts">Contacts</string>
<string name="favorites">Favorites</string>
<string name="recent_calls">Recent calls</string>
<string name="show_call_confirmation_dialog">Show a call confirmation dialog before initiating a call</string>
<!-- Emails --> <!-- Emails -->
<string name="email">E-mail</string> <string name="email">E-mail</string>

View File

@ -13,6 +13,8 @@
<string name="send_email_to_contacts">Send email to contacts</string> <string name="send_email_to_contacts">Send email to contacts</string>
<string name="send_sms_to_group">Send SMS to group</string> <string name="send_sms_to_group">Send SMS to group</string>
<string name="send_email_to_group">Send email to group</string> <string name="send_email_to_group">Send email to group</string>
<string name="call_person">Call %s</string>
<string name="request_the_required_permissions">Request the required permissions</string>
<string name="new_contact">Novi kontakt</string> <string name="new_contact">Novi kontakt</string>
<string name="edit_contact">Uredi kontakt</string> <string name="edit_contact">Uredi kontakt</string>
@ -21,6 +23,7 @@
<string name="first_name">Ime</string> <string name="first_name">Ime</string>
<string name="middle_name">Srednje ime</string> <string name="middle_name">Srednje ime</string>
<string name="surname">Prezime</string> <string name="surname">Prezime</string>
<string name="nickname">Nickname</string>
<!-- Groups --> <!-- Groups -->
<string name="no_groups">Nema grupa</string> <string name="no_groups">Nema grupa</string>
@ -45,10 +48,13 @@
<string name="on_contact_click">Prilikom dodira kontakta</string> <string name="on_contact_click">Prilikom dodira kontakta</string>
<string name="call_contact">Nazovi kontakt</string> <string name="call_contact">Nazovi kontakt</string>
<string name="view_contact">Prikaži pojedinosti o kontaktu</string> <string name="view_contact">Prikaži pojedinosti o kontaktu</string>
<string name="show_favorites_tab">Prikaži karticu favorita</string>
<string name="show_groups_tab">Prikaži karticu grupa</string>
<string name="manage_shown_contact_fields">Manage shown contact fields</string> <string name="manage_shown_contact_fields">Manage shown contact fields</string>
<string name="filter_duplicates">Try filtering out duplicate contacts</string> <string name="filter_duplicates">Try filtering out duplicate contacts</string>
<string name="manage_shown_tabs">Manage shown tabs</string>
<string name="contacts">Contacts</string>
<string name="favorites">Favorites</string>
<string name="recent_calls">Recent calls</string>
<string name="show_call_confirmation_dialog">Show a call confirmation dialog before initiating a call</string>
<!-- Emails --> <!-- Emails -->
<string name="email">E-pošta</string> <string name="email">E-pošta</string>

View File

@ -0,0 +1,133 @@
<resources>
<string name="app_name">Simple Contacts</string>
<string name="app_launcher_name">連絡先</string>
<string name="address">住所</string>
<string name="inserting">挿入中…</string>
<string name="updating">更新中…</string>
<string name="phone_storage">内部ストレージ</string>
<string name="phone_storage_hidden">内部ストレージ (他のアプリからは表示されません)</string>
<string name="company">会社</string>
<string name="job_position">役職</string>
<string name="website">ウェブサイト</string>
<string name="send_sms_to_contacts">連絡先にSMSを送信</string>
<string name="send_email_to_contacts">連絡先にメールを送信</string>
<string name="send_sms_to_group">グループにSMSを送信</string>
<string name="send_email_to_group">グループにメールを送信</string>
<string name="call_person">Call %s</string>
<string name="request_the_required_permissions">Request the required permissions</string>
<string name="new_contact">新しい連絡先</string>
<string name="edit_contact">連絡先を編集</string>
<string name="select_contact">連絡先を選択</string>
<string name="select_contacts">連絡先を選択</string>
<string name="first_name"></string>
<string name="middle_name">ミドルネーム</string>
<string name="surname"></string>
<string name="nickname">Nickname</string>
<!-- Groups -->
<string name="no_groups">グループなし</string>
<string name="create_new_group">新しいグループを作成</string>
<string name="remove_from_group">グループから削除</string>
<string name="no_group_participants">このグループは空です</string>
<string name="add_contacts">連絡先を追加</string>
<string name="no_group_created">連絡先グループがありません</string>
<string name="create_group">グループを作成</string>
<string name="add_to_group">グループに追加</string>
<string name="create_group_under_account">アカウントの下にグループを作成</string>
<!-- Photo -->
<string name="take_photo">写真を撮影</string>
<string name="choose_photo">写真を選択</string>
<string name="remove_photo">写真を削除</string>
<!-- Settings -->
<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="view_contact">連絡先の詳細を表示</string>
<string name="manage_shown_contact_fields">連絡先に表示するフィールドを管理</string>
<string name="filter_duplicates">重複した連絡先を除外する</string>
<string name="manage_shown_tabs">表示するタブを管理</string>
<string name="contacts">連絡先</string>
<string name="favorites">お気に入り</string>
<string name="recent_calls">Recent calls</string>
<string name="show_call_confirmation_dialog">発信する前に確認ダイアログを表示する</string>
<!-- Emails -->
<string name="email">メール</string>
<string name="home">自宅</string>
<string name="work">職場</string>
<string name="other">その他</string>
<!-- Phone numbers -->
<string name="number">番号</string>
<string name="mobile">携帯</string>
<string name="main_number">Main</string>
<string name="work_fax">職場FAX</string>
<string name="home_fax">自宅FAX</string>
<string name="pager">ポケベル</string>
<string name="no_phone_number_found">電話番号が見つかりません</string>
<!-- Events -->
<string name="birthday">誕生日</string>
<string name="anniversary">記念日</string>
<!-- Favorites -->
<string name="no_favorites">お気に入りの連絡先はまだありません</string>
<string name="add_favorites">お気に入りを追加</string>
<string name="add_to_favorites">お気に入りに追加</string>
<string name="remove_from_favorites">お気に入りから削除</string>
<string name="must_be_at_edit">連絡先を編集するには編集画面に切り替えてください</string>
<!-- Search -->
<string name="search_contacts">連絡先を検索</string>
<string name="search_favorites">お気に入りを検索</string>
<!-- Export / Import -->
<string name="import_contacts">連絡先をインポート</string>
<string name="export_contacts">連絡先をエクスポート</string>
<string name="import_contacts_from_vcf">.vcfファイルから連絡先をインポート</string>
<string name="export_contacts_to_vcf">連絡先を.vcfファイルにエクスポート</string>
<string name="target_contact_source">Target contact source</string>
<string name="include_contact_sources">Include contact sources</string>
<string name="filename_without_vcf">ファイル名 (.vcfを含めない)</string>
<!-- Visible fields -->
<string name="select_fields_to_show">表示する項目を選択</string>
<string name="prefix">敬称(名前の前)</string>
<string name="suffix">敬称(名前の後)</string>
<string name="phone_numbers">電話番号</string>
<string name="emails">メール</string>
<string name="addresses">住所</string>
<string name="events">予定 (誕生日、記念日)</string>
<string name="notes">メモ</string>
<string name="organization">所属</string>
<string name="websites">ウェブサイト</string>
<string name="groups">グループ</string>
<string name="contact_source">インポート元</string>
<!-- FAQ -->
<string name="faq_1_title">連絡先に表示される項目(フィールド)を変更することはできますか?</string>
<string name="faq_1_text">可能です。[設定] -> [連絡先に表示するフィールドを管理] から、表示されるフィールドを選択することができます。これらの中にはデフォルトで無効になっているものもあるので、あなたは新しいものを見つけるかもしれません。</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">連絡先を管理するシンプルなアプリ (広告表示なし)。</string>
<string name="app_long_description">
連絡先を作成または管理するためのシンプルなアプリです。連絡先はお使いの端末上にのみ保存されますが、Googleや他のアカウントと同期することもできます。お気に入りの連絡先を別のリストとして表示することができます。
友人のメールアドレスや予定の管理にも使用できます。これらは複数の項目で並べ替えやフィルタリングする機能があり、名前を\"姓 名\"の順に表示することもできます。
広告や不要なアクセス許可は含まれていません。完全にオープンソースで、色のカスタマイズも可能です。
このアプリは大きな一連のアプリの一つです。 その他のアプリは 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

@ -13,6 +13,8 @@
<string name="send_email_to_contacts">Send email to contacts</string> <string name="send_email_to_contacts">Send email to contacts</string>
<string name="send_sms_to_group">Send SMS to group</string> <string name="send_sms_to_group">Send SMS to group</string>
<string name="send_email_to_group">Send email to group</string> <string name="send_email_to_group">Send email to group</string>
<string name="call_person">Call %s</string>
<string name="request_the_required_permissions">Request the required permissions</string>
<string name="new_contact">새로운 연락처</string> <string name="new_contact">새로운 연락처</string>
<string name="edit_contact">연락처 수정</string> <string name="edit_contact">연락처 수정</string>
@ -21,6 +23,7 @@
<string name="first_name">이름</string> <string name="first_name">이름</string>
<string name="middle_name">중간 이름</string> <string name="middle_name">중간 이름</string>
<string name="surname"></string> <string name="surname"></string>
<string name="nickname">Nickname</string>
<!-- Groups --> <!-- Groups -->
<string name="no_groups">No groups</string> <string name="no_groups">No groups</string>
@ -45,10 +48,13 @@
<string name="on_contact_click">On contact click</string> <string name="on_contact_click">On contact click</string>
<string name="call_contact">Call contact</string> <string name="call_contact">Call contact</string>
<string name="view_contact">View contact details</string> <string name="view_contact">View contact details</string>
<string name="show_favorites_tab">Show favorites tab</string>
<string name="show_groups_tab">Show groups tab</string>
<string name="manage_shown_contact_fields">Manage shown contact fields</string> <string name="manage_shown_contact_fields">Manage shown contact fields</string>
<string name="filter_duplicates">Try filtering out duplicate contacts</string> <string name="filter_duplicates">Try filtering out duplicate contacts</string>
<string name="manage_shown_tabs">Manage shown tabs</string>
<string name="contacts">Contacts</string>
<string name="favorites">Favorites</string>
<string name="recent_calls">Recent calls</string>
<string name="show_call_confirmation_dialog">Show a call confirmation dialog before initiating a call</string>
<!-- Emails --> <!-- Emails -->
<string name="email">이메일</string> <string name="email">이메일</string>

View File

@ -13,6 +13,8 @@
<string name="send_email_to_contacts">Send email to contacts</string> <string name="send_email_to_contacts">Send email to contacts</string>
<string name="send_sms_to_group">Send SMS to group</string> <string name="send_sms_to_group">Send SMS to group</string>
<string name="send_email_to_group">Send email to group</string> <string name="send_email_to_group">Send email to group</string>
<string name="call_person">Call %s</string>
<string name="request_the_required_permissions">Request the required permissions</string>
<string name="new_contact">Naujas kontaktas</string> <string name="new_contact">Naujas kontaktas</string>
<string name="edit_contact">Redaguoti kontaktą</string> <string name="edit_contact">Redaguoti kontaktą</string>
@ -21,6 +23,7 @@
<string name="first_name">Vardas</string> <string name="first_name">Vardas</string>
<string name="middle_name">Antras vardas</string> <string name="middle_name">Antras vardas</string>
<string name="surname">Pavardė</string> <string name="surname">Pavardė</string>
<string name="nickname">Nickname</string>
<!-- Groups --> <!-- Groups -->
<string name="no_groups">Nėra grupių</string> <string name="no_groups">Nėra grupių</string>
@ -45,10 +48,13 @@
<string name="on_contact_click">Ant kontakto paspaudimo</string> <string name="on_contact_click">Ant kontakto paspaudimo</string>
<string name="call_contact">Skambinti kontaktui</string> <string name="call_contact">Skambinti kontaktui</string>
<string name="view_contact">Žiūrėti kontakto detales</string> <string name="view_contact">Žiūrėti kontakto detales</string>
<string name="show_favorites_tab">Rodyti mėgiamiausiųjų skirtuką</string>
<string name="show_groups_tab">Rodyti grupių skirtuką</string>
<string name="manage_shown_contact_fields">Manage shown contact fields</string> <string name="manage_shown_contact_fields">Manage shown contact fields</string>
<string name="filter_duplicates">Try filtering out duplicate contacts</string> <string name="filter_duplicates">Try filtering out duplicate contacts</string>
<string name="manage_shown_tabs">Manage shown tabs</string>
<string name="contacts">Contacts</string>
<string name="favorites">Favorites</string>
<string name="recent_calls">Recent calls</string>
<string name="show_call_confirmation_dialog">Show a call confirmation dialog before initiating a call</string>
<!-- Emails --> <!-- Emails -->
<string name="email">Elektroninis paštas</string> <string name="email">Elektroninis paštas</string>

View File

@ -13,6 +13,8 @@
<string name="send_email_to_contacts">Enviar e-mail aos contactos</string> <string name="send_email_to_contacts">Enviar e-mail aos contactos</string>
<string name="send_sms_to_group">Enviar SMS para o grupo</string> <string name="send_sms_to_group">Enviar SMS para o grupo</string>
<string name="send_email_to_group">Enviar e-mail para o grupo</string> <string name="send_email_to_group">Enviar e-mail para o grupo</string>
<string name="call_person">Ligar a %s</string>
<string name="request_the_required_permissions">Request the required permissions</string>
<string name="new_contact">Novo contacto</string> <string name="new_contact">Novo contacto</string>
<string name="edit_contact">Editar contacto</string> <string name="edit_contact">Editar contacto</string>
@ -21,6 +23,7 @@
<string name="first_name">Primeiro nome</string> <string name="first_name">Primeiro nome</string>
<string name="middle_name">Segundo nome</string> <string name="middle_name">Segundo nome</string>
<string name="surname">Apelido</string> <string name="surname">Apelido</string>
<string name="nickname">Alcunha</string>
<!-- Groups --> <!-- Groups -->
<string name="no_groups">Não há grupos</string> <string name="no_groups">Não há grupos</string>
@ -45,10 +48,13 @@
<string name="on_contact_click">Ao tocar no contacto</string> <string name="on_contact_click">Ao tocar no contacto</string>
<string name="call_contact">Ligar</string> <string name="call_contact">Ligar</string>
<string name="view_contact">Ver detalhes</string> <string name="view_contact">Ver detalhes</string>
<string name="show_favorites_tab">Mostrar favoritos</string>
<string name="show_groups_tab">Mostrar grupos</string>
<string name="manage_shown_contact_fields">Gerir campos a exibir</string> <string name="manage_shown_contact_fields">Gerir campos a exibir</string>
<string name="filter_duplicates">Try filtering out duplicate contacts</string> <string name="filter_duplicates">Tentar filtrar contactos duplicados</string>
<string name="manage_shown_tabs">Manage shown tabs</string>
<string name="contacts">Contactos</string>
<string name="favorites">Favoritos</string>
<string name="recent_calls">Chamadas recentes</string>
<string name="show_call_confirmation_dialog">Mostrar diálogo para confirmar a chamada</string>
<!-- Emails --> <!-- Emails -->
<string name="email">E-mail</string> <string name="email">E-mail</string>

View File

@ -13,6 +13,8 @@
<string name="send_email_to_contacts">Отправить письмо контактам</string> <string name="send_email_to_contacts">Отправить письмо контактам</string>
<string name="send_sms_to_group">Отправить SMS группе</string> <string name="send_sms_to_group">Отправить SMS группе</string>
<string name="send_email_to_group">Отправить письмо группе</string> <string name="send_email_to_group">Отправить письмо группе</string>
<string name="call_person">Call %s</string>
<string name="request_the_required_permissions">Request the required permissions</string>
<string name="new_contact">Новый контакт</string> <string name="new_contact">Новый контакт</string>
<string name="edit_contact">Редактировать контакт</string> <string name="edit_contact">Редактировать контакт</string>
@ -21,6 +23,7 @@
<string name="first_name">Имя</string> <string name="first_name">Имя</string>
<string name="middle_name">Отчество</string> <string name="middle_name">Отчество</string>
<string name="surname">Фамилия</string> <string name="surname">Фамилия</string>
<string name="nickname">Nickname</string>
<!-- Groups --> <!-- Groups -->
<string name="no_groups">Нет групп</string> <string name="no_groups">Нет групп</string>
@ -45,10 +48,13 @@
<string name="on_contact_click">При нажатии на контакт</string> <string name="on_contact_click">При нажатии на контакт</string>
<string name="call_contact">Позвонить контакту</string> <string name="call_contact">Позвонить контакту</string>
<string name="view_contact">Просмотреть подробности о контакте</string> <string name="view_contact">Просмотреть подробности о контакте</string>
<string name="show_favorites_tab">Показывать вкладку избранного</string>
<string name="show_groups_tab">Показывать вкладку групп</string>
<string name="manage_shown_contact_fields">Управление отображаемыми полями контактов</string> <string name="manage_shown_contact_fields">Управление отображаемыми полями контактов</string>
<string name="filter_duplicates">Отфильтровывать повторяющиеся контакты</string> <string name="filter_duplicates">Отфильтровывать повторяющиеся контакты</string>
<string name="manage_shown_tabs">Manage shown tabs</string>
<string name="contacts">Contacts</string>
<string name="favorites">Favorites</string>
<string name="recent_calls">Recent calls</string>
<string name="show_call_confirmation_dialog">Show a call confirmation dialog before initiating a call</string>
<!-- Emails --> <!-- Emails -->
<string name="email">Эл. почта</string> <string name="email">Эл. почта</string>

View File

@ -13,6 +13,8 @@
<string name="send_email_to_contacts">Poslať kontaktom email</string> <string name="send_email_to_contacts">Poslať kontaktom email</string>
<string name="send_sms_to_group">Poslať skupine SMS</string> <string name="send_sms_to_group">Poslať skupine SMS</string>
<string name="send_email_to_group">Poslať skupine email</string> <string name="send_email_to_group">Poslať skupine email</string>
<string name="call_person">Zavolať %s</string>
<string name="request_the_required_permissions">Vyžiadať potrebné oprávnenia</string>
<string name="new_contact">Nový kontakt</string> <string name="new_contact">Nový kontakt</string>
<string name="edit_contact">Upraviť kontakt</string> <string name="edit_contact">Upraviť kontakt</string>
@ -21,6 +23,7 @@
<string name="first_name">Krstné meno</string> <string name="first_name">Krstné meno</string>
<string name="middle_name">Stredné meno</string> <string name="middle_name">Stredné meno</string>
<string name="surname">Priezvisko</string> <string name="surname">Priezvisko</string>
<string name="nickname">Prezývka</string>
<!-- Groups --> <!-- Groups -->
<string name="no_groups">Žiadne skupiny</string> <string name="no_groups">Žiadne skupiny</string>
@ -45,10 +48,13 @@
<string name="on_contact_click">Po kliknutí na kontakt</string> <string name="on_contact_click">Po kliknutí na kontakt</string>
<string name="call_contact">Zavolať kontakt</string> <string name="call_contact">Zavolať kontakt</string>
<string name="view_contact">Zobraziť údaje kontaktu</string> <string name="view_contact">Zobraziť údaje kontaktu</string>
<string name="show_favorites_tab">Zobraziť okno s obľúbenými</string>
<string name="show_groups_tab">Zobraziť okno so skupinami</string>
<string name="manage_shown_contact_fields">Spravovať zobrazené polia kontaktov</string> <string name="manage_shown_contact_fields">Spravovať zobrazené polia kontaktov</string>
<string name="filter_duplicates">Pokúsiť sa vyfiltrovať duplicitné kontakty</string> <string name="filter_duplicates">Pokúsiť sa vyfiltrovať duplicitné kontakty</string>
<string name="manage_shown_tabs">Spravovať zobrazené karty</string>
<string name="contacts">Kontakty</string>
<string name="favorites">Obľúbené</string>
<string name="recent_calls">Predošlé hovory</string>
<string name="show_call_confirmation_dialog">Zobraziť pred spustením hovoru okno na jeho potvrdenie</string>
<!-- Emails --> <!-- Emails -->
<string name="email">Email</string> <string name="email">Email</string>

View File

@ -13,6 +13,8 @@
<string name="send_email_to_contacts">Skicka e-post till kontakter</string> <string name="send_email_to_contacts">Skicka e-post till kontakter</string>
<string name="send_sms_to_group">Skicka sms till grupp</string> <string name="send_sms_to_group">Skicka sms till grupp</string>
<string name="send_email_to_group">Skicka e-post till grupp</string> <string name="send_email_to_group">Skicka e-post till grupp</string>
<string name="call_person">Ring %s</string>
<string name="request_the_required_permissions">Begär de behörigheter som krävs</string>
<string name="new_contact">Ny kontakt</string> <string name="new_contact">Ny kontakt</string>
<string name="edit_contact">Redigera kontakt</string> <string name="edit_contact">Redigera kontakt</string>
@ -21,6 +23,7 @@
<string name="first_name">Förnamn</string> <string name="first_name">Förnamn</string>
<string name="middle_name">Mellannamn</string> <string name="middle_name">Mellannamn</string>
<string name="surname">Efternamn</string> <string name="surname">Efternamn</string>
<string name="nickname">Smeknamn</string>
<!-- Groups --> <!-- Groups -->
<string name="no_groups">Inga grupper</string> <string name="no_groups">Inga grupper</string>
@ -45,10 +48,13 @@
<string name="on_contact_click">Vid kontakttryckning</string> <string name="on_contact_click">Vid kontakttryckning</string>
<string name="call_contact">Ring kontakt</string> <string name="call_contact">Ring kontakt</string>
<string name="view_contact">Visa kontaktuppgifter</string> <string name="view_contact">Visa kontaktuppgifter</string>
<string name="show_favorites_tab">Visa fliken Favoriter</string>
<string name="show_groups_tab">Visa fliken Grupper</string>
<string name="manage_shown_contact_fields">Hantera visade kontaktfält</string> <string name="manage_shown_contact_fields">Hantera visade kontaktfält</string>
<string name="filter_duplicates">Try filtering out duplicate contacts</string> <string name="filter_duplicates">Försök filtrera bort dubblettkontakter</string>
<string name="manage_shown_tabs">Hantera visade flikar</string>
<string name="contacts">Kontakter</string>
<string name="favorites">Favoriter</string>
<string name="recent_calls">Senaste samtal</string>
<string name="show_call_confirmation_dialog">Visa en bekräftelsedialogruta före uppringning</string>
<!-- Emails --> <!-- Emails -->
<string name="email">E-post</string> <string name="email">E-post</string>
@ -74,7 +80,7 @@
<string name="add_favorites">Lägg till favoriter</string> <string name="add_favorites">Lägg till favoriter</string>
<string name="add_to_favorites">Lägg till i favoriter</string> <string name="add_to_favorites">Lägg till i favoriter</string>
<string name="remove_from_favorites">Ta bort från favoriter</string> <string name="remove_from_favorites">Ta bort från favoriter</string>
<string name="must_be_at_edit">You must be at the Edit screen to modify a contact</string> <string name="must_be_at_edit">Kontakter kan bara redigeras i redigeringsvyn</string>
<!-- Search --> <!-- Search -->
<string name="search_contacts">Sök efter kontakter</string> <string name="search_contacts">Sök efter kontakter</string>

View File

@ -0,0 +1,133 @@
<resources>
<string name="app_name">Basit Kişiler</string>
<string name="app_launcher_name">Kişiler</string>
<string name="address">Adres</string>
<string name="inserting">Ekleniyor…</string>
<string name="updating">Güncelleniyor…</string>
<string name="phone_storage">Telefon belleği</string>
<string name="phone_storage_hidden">Telefon belleği (diğer uygulamalar tarafından görülmez)</string>
<string name="company">Şirket</string>
<string name="job_position">İş pozisyonu</string>
<string name="website">Web sitesi</string>
<string name="send_sms_to_contacts">Kişilere SMS gönder</string>
<string name="send_email_to_contacts">Kişilere e-posta gönder</string>
<string name="send_sms_to_group">Gruba SMS gönder</string>
<string name="send_email_to_group">Gruba e-posta gönder</string>
<string name="call_person">Call %s</string>
<string name="request_the_required_permissions">Request the required permissions</string>
<string name="new_contact">Yeni kişi</string>
<string name="edit_contact">Kişiyi düzenle</string>
<string name="select_contact">Kişi seç</string>
<string name="select_contacts">Kişileri seç</string>
<string name="first_name">Adı</string>
<string name="middle_name">Göbek adı</string>
<string name="surname">Soyadı</string>
<string name="nickname">Nickname</string>
<!-- Groups -->
<string name="no_groups">Grup yok</string>
<string name="create_new_group">Yeni grup oluştur</string>
<string name="remove_from_group">Gruptan kaldır</string>
<string name="no_group_participants">Bu grup boş</string>
<string name="add_contacts">Kişi ekle</string>
<string name="no_group_created">Cihazda kişi grubu yok</string>
<string name="create_group">Grup oluştur</string>
<string name="add_to_group">Gruba ekle</string>
<string name="create_group_under_account">Hesap altında grup oluştur</string>
<!-- Photo -->
<string name="take_photo">Fotoğraf çek</string>
<string name="choose_photo">Fotoğraf seç</string>
<string name="remove_photo">Fotoğrafı kaldır</string>
<!-- Settings -->
<string name="start_name_with_surname">Soyadı ile başla</string>
<string name="show_phone_numbers">Ana ekranda telefon numaralarını göster</string>
<string name="show_contact_thumbnails">Kişi küçük resimlerini göster</string>
<string name="on_contact_click">Kişi tıklandığında</string>
<string name="call_contact">Kişiyi ara</string>
<string name="view_contact">Kişi bilgilerini göster</string>
<string name="manage_shown_contact_fields">Görüntülenecek kişi alanlarını yönet</string>
<string name="filter_duplicates">Çift kişileri filtrelemeyi dene</string>
<string name="manage_shown_tabs">Manage shown tabs</string>
<string name="contacts">Contacts</string>
<string name="favorites">Favorites</string>
<string name="recent_calls">Recent calls</string>
<string name="show_call_confirmation_dialog">Show a call confirmation dialog before initiating a call</string>
<!-- Emails -->
<string name="email">E-posta</string>
<string name="home">Ev</string>
<string name="work">İş</string>
<string name="other">Diğer</string>
<!-- Phone numbers -->
<string name="number">Numara</string>
<string name="mobile">Cep</string>
<string name="main_number">Ana</string>
<string name="work_fax">İş Faksı</string>
<string name="home_fax">Ev Faksı</string>
<string name="pager">Çağrı Cihazı</string>
<string name="no_phone_number_found">Telefon numarası bulunamadı</string>
<!-- Events -->
<string name="birthday">Doğum günü</string>
<string name="anniversary">Yıldönümü</string>
<!-- Favorites -->
<string name="no_favorites">Henüz hiç favori kişi eklemediniz gibi görünüyor.</string>
<string name="add_favorites">Favorilerini ekle</string>
<string name="add_to_favorites">Favorilere ekle</string>
<string name="remove_from_favorites">Favorilerden kaldır</string>
<string name="must_be_at_edit">Bir kişiyi değiştirmek için Düzen ekranında olmalısınız</string>
<!-- Search -->
<string name="search_contacts">Kişileri ara</string>
<string name="search_favorites">Favorileri ara</string>
<!-- Export / Import -->
<string name="import_contacts">Kişileri içe aktar</string>
<string name="export_contacts">Kişileri dışa aktar</string>
<string name="import_contacts_from_vcf">Kişileri bir .vcf dosyasından içe aktar</string>
<string name="export_contacts_to_vcf">Kişileri bir .vcf dosyasına aktar</string>
<string name="target_contact_source">Hedef kişi kaynağı</string>
<string name="include_contact_sources">Kişi kaynaklarını dahil et</string>
<string name="filename_without_vcf">Dosya adı (.vcf olmadan)</string>
<!-- Visible fields -->
<string name="select_fields_to_show">Görüntülenecek alanları seç</string>
<string name="prefix">Önek</string>
<string name="suffix">Sonek</string>
<string name="phone_numbers">Telefon numaraları</string>
<string name="emails">E-postalar</string>
<string name="addresses">Adresler</string>
<string name="events">Etkinlikler (doğum günleri, yıldönümleri)</string>
<string name="notes">Notlar</string>
<string name="organization">Organizasyon</string>
<string name="websites">Web siteleri</string>
<string name="groups">Gruplar</string>
<string name="contact_source">Kişi kaynağı</string>
<!-- FAQ -->
<string name="faq_1_title">Rehberde görüntülenecek alanları değiştirmek istiyorum. Bunu yapabilir miyim?</string>
<string name="faq_1_text">Evet, tek yapmanız gereken Ayarlar -> Görüntülenecek kişi alanlarını yönet\'e gitmek. Orada hangi alanların görüntüleneceğini seçebilirsiniz. Bazıları varsayılan olarak devre dışı bile olsa, orada bazı yenilerini bulabilirsiniz.</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">Kişilerinizi reklamsız yönetmek için bir kişiler uygulaması.</string>
<string name="app_long_description">
Kişilerinizi herhangi bir kaynaktan oluşturmak veya yönetmek için basit bir uygulama. Kişiler yalnızca cihazınızda saklanabilir, aynı zamanda Google veya diğer hesaplarla senkronize edilebilir. Favori kişilerinizi ayrı bir listede görüntüleyebilirsiniz.
Kullanıcı e-postalarını ve etkinliklerini yönetmek için de kullanabilirsiniz. Birden çok parametreye göre sıralama/filtreleme, isteğe bağlı olarak soyadı ilk ad olarak görüntüleme yeteneğine sahiptir.
Reklam veya gereksiz izinler içermez. Tamamen açık kaynaktır, özelleştirilebilir renkler sağlar.
Bu uygulama, daha büyük bir uygulama serisinden sadece bir parça. Geri kalanı http://www.simplemobiletools.com adresinde bulabilirsiniz
</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

@ -8,11 +8,13 @@
<string name="phone_storage_hidden">手機空間 (其他程式不可見)</string> <string name="phone_storage_hidden">手機空間 (其他程式不可見)</string>
<string name="company">公司</string> <string name="company">公司</string>
<string name="job_position">職位</string> <string name="job_position">職位</string>
<string name="website">Website</string> <string name="website">網站</string>
<string name="send_sms_to_contacts">Send SMS to contacts</string> <string name="send_sms_to_contacts">發送簡訊給聯絡人</string>
<string name="send_email_to_contacts">Send email to contacts</string> <string name="send_email_to_contacts">發送電子郵件給聯絡人</string>
<string name="send_sms_to_group">Send SMS to group</string> <string name="send_sms_to_group">發送簡訊給群組</string>
<string name="send_email_to_group">Send email to group</string> <string name="send_email_to_group">發送電子郵件給群組</string>
<string name="call_person">打電話給 %s</string>
<string name="request_the_required_permissions">請求必要的權限</string>
<string name="new_contact">新聯絡人</string> <string name="new_contact">新聯絡人</string>
<string name="edit_contact">編輯聯絡人</string> <string name="edit_contact">編輯聯絡人</string>
@ -21,6 +23,7 @@
<string name="first_name">名字</string> <string name="first_name">名字</string>
<string name="middle_name">中間名</string> <string name="middle_name">中間名</string>
<string name="surname">姓氏</string> <string name="surname">姓氏</string>
<string name="nickname">Nickname</string>
<!-- Groups --> <!-- Groups -->
<string name="no_groups">沒有群組</string> <string name="no_groups">沒有群組</string>
@ -45,10 +48,13 @@
<string name="on_contact_click">點擊聯絡人</string> <string name="on_contact_click">點擊聯絡人</string>
<string name="call_contact">打電話給聯絡人</string> <string name="call_contact">打電話給聯絡人</string>
<string name="view_contact">顯示聯絡人資料</string> <string name="view_contact">顯示聯絡人資料</string>
<string name="show_favorites_tab">顯示我的最愛頁面</string>
<string name="show_groups_tab">顯示群組頁面</string>
<string name="manage_shown_contact_fields">管理顯示的聯絡人欄位</string> <string name="manage_shown_contact_fields">管理顯示的聯絡人欄位</string>
<string name="filter_duplicates">Try filtering out duplicate contacts</string> <string name="filter_duplicates">試著過濾重複的聯絡人</string>
<string name="manage_shown_tabs">管理顯示的頁面</string>
<string name="contacts">聯絡人</string>
<string name="favorites">我的最愛</string>
<string name="recent_calls">通話紀錄</string>
<string name="show_call_confirmation_dialog">開始通話前顯示通話確認框</string>
<!-- Emails --> <!-- Emails -->
<string name="email">電子信箱</string> <string name="email">電子信箱</string>
@ -74,7 +80,7 @@
<string name="add_favorites">添加我的最愛</string> <string name="add_favorites">添加我的最愛</string>
<string name="add_to_favorites">加入我的最愛</string> <string name="add_to_favorites">加入我的最愛</string>
<string name="remove_from_favorites">從我的最愛移除</string> <string name="remove_from_favorites">從我的最愛移除</string>
<string name="must_be_at_edit">You must be at the Edit screen to modify a contact</string> <string name="must_be_at_edit">你必須在編輯畫面去修改聯絡人</string>
<!-- Search --> <!-- Search -->
<string name="search_contacts">搜尋聯絡人</string> <string name="search_contacts">搜尋聯絡人</string>
@ -99,7 +105,7 @@
<string name="events">活動 (生日、紀念日)</string> <string name="events">活動 (生日、紀念日)</string>
<string name="notes">筆記</string> <string name="notes">筆記</string>
<string name="organization">組織</string> <string name="organization">組織</string>
<string name="websites">Websites</string> <string name="websites">網站</string>
<string name="groups">群組</string> <string name="groups">群組</string>
<string name="contact_source">聯絡人來源</string> <string name="contact_source">聯絡人來源</string>

View File

@ -2,6 +2,11 @@
<resources> <resources>
<!-- Release notes --> <!-- Release notes -->
<string name="release_29">Added an optional Nickname field</string>
<string name="release_27">
Allow customizing which tabs are visible\n
Added an optional call confirmation dialog
</string>
<string name="release_16"> <string name="release_16">
Added name prefix/suffix and contact organizations\n Added name prefix/suffix and contact organizations\n
Added a settings item \"Manage shown contact fields\" for customizing visible contact details, with some fields disabled by default Added a settings item \"Manage shown contact fields\" for customizing visible contact details, with some fields disabled by default

View File

@ -13,6 +13,8 @@
<string name="send_email_to_contacts">Send email to contacts</string> <string name="send_email_to_contacts">Send email to contacts</string>
<string name="send_sms_to_group">Send SMS to group</string> <string name="send_sms_to_group">Send SMS to group</string>
<string name="send_email_to_group">Send email to group</string> <string name="send_email_to_group">Send email to group</string>
<string name="call_person">Call %s</string>
<string name="request_the_required_permissions">Request the required permissions</string>
<string name="new_contact">New contact</string> <string name="new_contact">New contact</string>
<string name="edit_contact">Edit contact</string> <string name="edit_contact">Edit contact</string>
@ -21,6 +23,7 @@
<string name="first_name">First name</string> <string name="first_name">First name</string>
<string name="middle_name">Middle name</string> <string name="middle_name">Middle name</string>
<string name="surname">Surname</string> <string name="surname">Surname</string>
<string name="nickname">Nickname</string>
<!-- Groups --> <!-- Groups -->
<string name="no_groups">No groups</string> <string name="no_groups">No groups</string>
@ -45,10 +48,13 @@
<string name="on_contact_click">On contact click</string> <string name="on_contact_click">On contact click</string>
<string name="call_contact">Call contact</string> <string name="call_contact">Call contact</string>
<string name="view_contact">View contact details</string> <string name="view_contact">View contact details</string>
<string name="show_favorites_tab">Show favorites tab</string>
<string name="show_groups_tab">Show groups tab</string>
<string name="manage_shown_contact_fields">Manage shown contact fields</string> <string name="manage_shown_contact_fields">Manage shown contact fields</string>
<string name="filter_duplicates">Try filtering out duplicate contacts</string> <string name="filter_duplicates">Try filtering out duplicate contacts</string>
<string name="manage_shown_tabs">Manage shown tabs</string>
<string name="contacts">Contacts</string>
<string name="favorites">Favorites</string>
<string name="recent_calls">Recent calls</string>
<string name="show_call_confirmation_dialog">Show a call confirmation dialog before initiating a call</string>
<!-- Emails --> <!-- Emails -->
<string name="email">Email</string> <string name="email">Email</string>

View File

@ -1,7 +1,7 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript { buildscript {
ext.kotlin_version = '1.2.41' ext.kotlin_version = '1.2.61'
repositories { repositories {
google() google()
@ -9,7 +9,7 @@ buildscript {
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.1.2' classpath 'com.android.tools.build:gradle:3.1.4'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB